Fork me on GitHub

pwnable.kr-crypto1

前言

这虽然不是一道二进制的题目,但是也有足够的pwn味道。

分析

题目是用了python写的b/s结构,代码逻辑简单。
client.py

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
#!/usr/bin/python
from Crypto.Cipher import AES
import base64
import os, sys
import xmlrpclib
rpc = xmlrpclib.ServerProxy("http://localhost:9100/")

BLOCK_SIZE = 16
PADDING = '\x00'
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
EncodeAES = lambda c, s: c.encrypt(pad(s)).encode('hex')
DecodeAES = lambda c, e: c.decrypt(e.decode('hex'))

# server's secrets
key = 'erased. but there is something on the real source code'
iv = 'erased. but there is something on the real source code'
cookie = 'erased. but there is something on the real source code'

# guest / 8b465d23cb778d3636bf6c4c5e30d031675fd95cec7afea497d36146783fd3a1
def sanitize(arg):
for c in arg:
if c not in '1234567890abcdefghijklmnopqrstuvwxyz-_':
return False
return True

def AES128_CBC(msg):
cipher = AES.new(key, AES.MODE_CBC, iv)
return EncodeAES(cipher, msg)

def request_auth(id, pw):
packet = '{0}-{1}-{2}'.format(id, pw, cookie)
e_packet = AES128_CBC(packet)
print 'sending encrypted data ({0})'.format(e_packet)
sys.stdout.flush()
return rpc.authenticate(e_packet)

if __name__ == '__main__':
print '---------------------------------------------------'
print '- PWNABLE.KR secure RPC login system -'
print '---------------------------------------------------'
print ''
print 'Input your ID'
sys.stdout.flush()
id = raw_input()
print 'Input your PW'
sys.stdout.flush()
pw = raw_input()

if sanitize(id) == False or sanitize(pw) == False:
print 'format error'
sys.stdout.flush()
os._exit(0)

cred = request_auth(id, pw)

if cred==0 :
print 'you are not authenticated user'
sys.stdout.flush()
os._exit(0)
if cred==1 :
print 'hi guest, login as admin'
sys.stdout.flush()
os._exit(0)

print 'hi admin, here is your flag'
print open('flag').read()
sys.stdout.flush()

server.py

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
#!/usr/bin/python
import xmlrpclib, hashlib
from SimpleXMLRPCServer import SimpleXMLRPCServer
from Crypto.Cipher import AES
import os, sys

BLOCK_SIZE = 16
PADDING = '\x00'
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
EncodeAES = lambda c, s: c.encrypt(pad(s)).encode('hex')
DecodeAES = lambda c, e: c.decrypt(e.decode('hex'))

# server's secrets
key = 'erased. but there is something on the real source code'
iv = 'erased. but there is something on the real source code'
cookie = 'erased. but there is something on the real source code'

def AES128_CBC(msg):
cipher = AES.new(key, AES.MODE_CBC, iv)
return DecodeAES(cipher, msg).rstrip(PADDING)

def authenticate(e_packet):
packet = AES128_CBC(e_packet)

id = packet.split('-')[0]
pw = packet.split('-')[1]

if packet.split('-')[2] != cookie:
return 0 # request is not originated from expected server

if hashlib.sha256(id+cookie).hexdigest() == pw and id == 'guest':
return 1
if hashlib.sha256(id+cookie).hexdigest() == pw and id == 'admin':
return 2
return 0

server = SimpleXMLRPCServer(("localhost", 9100))
print "Listening on port 9100..."
server.register_function(authenticate, "authenticate")
server.serve_forever()

分析代码可以知道,只有当id=admin,pw=sha256(id+cookie)时才能获得flag。然而我们不知道cookie的值,所以就无法计算出正确的pw
从代码中可以看到,程序使用aes128_cbc的加密模式。做web的都知道,这种加密方式容易受到字节反转攻击,但是这里却无法使用,因为字节反转攻击需要响应中有解密出来的明文。但是这里,由于这种加密模式明文和密文的字节是一一对应的,并且在这道题目中cookie前面的字符数量可控制,所以可以找到一种方法来爆破出cookie的值。
具体方法是,控制要加密的明文中cookie前面的字符数量,使要爆破的字节刚好位于分组中的最后一组的最后一个字节。接下来爆破方法是,每次改变最后一个字节,并把客户端返回的对应分组的加密密文与没有改变时的加密密文对比,当相等时说明该字节就是对应的cookie字节。如此爆破下去直到找到完整的cookie。随后就计算正确的ID和PW即可。
下面是poc,但是要传到题目平台上去,不然会很慢。

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
#!/usr/bin/python
from pwn import *

context(log_level='error')
cookie = ''

def getRealEPack(ID,PW):
r = remote('127.0.0.1',9006)
r.recvuntil('ID\n')
r.sendline(ID)
r.recvuntil('PW\n')
r.sendline(PW)
s = r.recvline()
e_pack = s[s.find('(')+1:-2]
r.close()
return e_pack

#get cookie
for i in xrange(2,100):
pack = '-'*(15-i%16)+'--'+cookie
for j in '1234567890abcdefghijklmnopqrstuvwxyz-_!':
e_pack0 = getRealEPack(pack+j,'')
e_pack1 = getRealEPack('-'*(15-i%16),'')
if e_pack0[:len(pack+j)*2] == e_pack1[:len(pack+j)*2]:
cookie += j
print cookie
break
if j == '!':
ID = 'admin'
PW = hashlib.sha256(ID+cookie).hexdigest()
print 'ID: {}\nPW: {}'.format(ID,PW)
exit(0)

运行后

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
fix@ubuntu:/tmp$ python 1.py
y
yo
you
you_
you_w
you_wi
you_wil
you_will
you_will_
you_will_n
you_will_ne
you_will_nev
you_will_neve
you_will_never
you_will_never_
you_will_never_g
you_will_never_gu
you_will_never_gue
you_will_never_gues
you_will_never_guess
you_will_never_guess_
you_will_never_guess_t
you_will_never_guess_th
you_will_never_guess_thi
you_will_never_guess_this
you_will_never_guess_this_
you_will_never_guess_this_s
you_will_never_guess_this_su
you_will_never_guess_this_sug
you_will_never_guess_this_suga
you_will_never_guess_this_sugar
you_will_never_guess_this_sugar_
you_will_never_guess_this_sugar_h
you_will_never_guess_this_sugar_ho
you_will_never_guess_this_sugar_hon
you_will_never_guess_this_sugar_hone
you_will_never_guess_this_sugar_honey
you_will_never_guess_this_sugar_honey_
you_will_never_guess_this_sugar_honey_s
you_will_never_guess_this_sugar_honey_sa
you_will_never_guess_this_sugar_honey_sal
you_will_never_guess_this_sugar_honey_salt
you_will_never_guess_this_sugar_honey_salt_
you_will_never_guess_this_sugar_honey_salt_c
you_will_never_guess_this_sugar_honey_salt_co
you_will_never_guess_this_sugar_honey_salt_coo
you_will_never_guess_this_sugar_honey_salt_cook
you_will_never_guess_this_sugar_honey_salt_cooki
you_will_never_guess_this_sugar_honey_salt_cookie
ID: admin
PW: fcf00f6fc7f66ffcfec02eaf69d30398b773fa9b2bc398f960784d60048cc503
fix@ubuntu:/tmp$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
root@kali:~/Desktop/test# nc pwnable.kr 9006
---------------------------------------------------
- PWNABLE.KR secure RPC login system -
---------------------------------------------------

Input your ID
admin
Input your PW
fcf00f6fc7f66ffcfec02eaf69d30398b773fa9b2bc398f960784d60048cc503
sending encrypted data (05c4ccfd4880c92339b995c7754ec2e6567f2ed91d955cb7144c1b6037855db1b3a8525e74d30fd4505bb38c975b86f23d0e5aa23eed44b9beaa7e2195da93ba53cb08758a261ada5612245f49d25b81aa5a297aa5d555886073b17e2ed719b3607da6fbfe40b260a45485910404d69c818a2faedac7bb3a727cfbb53eab8406)
hi admin, here is your flag
byte to byte leaking against block cipher plaintext is fun!!

root@kali:~/Desktop/test#

总结

很有意思的一次pwn。

-------------本文结束感谢您的阅读-------------

本文标题:pwnable.kr-crypto1

文章作者:r00tnb

发布时间:2018年04月24日 - 13:04

最后更新:2018年08月04日 - 22:08

原始链接:https://r00tnb.github.io/2018/04/24/pwnable.kr-crypto1/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

坚持原创,您的鼓励是我最大的动力!