虎符ctf2021 writeup

1.签到
https://github.com/php/php-src/commit/c730aa26bd52829a49f2ad284b181b7e82a68d7d
php git仓库后门

2.unsetme
源码

<?php
// Kickstart the framework
$f3=require('lib/base.php');

$f3->set('DEBUG',0);
if ((float)PCRE_VERSION<8.0)
    trigger_error('PCRE version is out of date');

// Load configuration
highlight_file(__FILE__);
$a=$_GET['a'];
unset($f3->$a);

$f3->run();

看题目可以知道用了f3框架,而f3框架3.7.1版本有RCE漏洞 CVE-2020-5203
一开始以为复现原漏洞就行了,后来在/lib/CHANGELOG.md中发现他用的是3.7.2的版本
在原来RCE的地方加了正则过滤,但试了一下发现也可以绕过
关键代码在lib/base.php下

function compile($str, $evaluate=TRUE) {
		return (!$evaluate)
			? preg_replace_callback(
				'/^@(\w+)((?:\..+|\[(?:(?:[^\[\]]*|(?R))*)\])*)/',
				function($expr) {
					$str='$'.$expr[1];
					if (isset($expr[2]))
						$str.=preg_replace_callback(
							'/\.([^.\[\]]+)|\[((?:[^\[\]\'"]*|(?R))*)\]/',
							function($sub) {
								$val=isset($sub[2]) ? $sub[2] : $sub[1];
								if (ctype_digit($val))
									$val=(int)$val;
								$out='['.$this->export($val).']';
								return $out;
							},
							$expr[2]
						);
					return $str;
				},
				$str
			)
			: preg_replace_callback(
			'/(?<!\w)@(\w+(?:(?:\->|::)\w+)?)'.
			'((?:\.\w+|\[(?:(?:[^\[\]]*|(?R))*)\]|(?:\->|::)\w+|\()*)/',
			function($expr) {
				$str='$'.$expr[1];
				if (isset($expr[2]))
					$str.=preg_replace_callback(
						'/\.(\w+)(\()?|\[((?:[^\[\]]*|(?R))*)\]/',
						function($sub) {
							if (empty($sub[2])) {
								if (ctype_digit($sub[1]))
									$sub[1]=(int)$sub[1];
								$out='['.
									(isset($sub[3])?
										$this->compile($sub[3]):
										$this->export($sub[1])).
								']';
							}
							else
								$out=function_exists($sub[1])?
									$sub[0]:
									('['.$this->export($sub[1]).']'.$sub[2]);
							return $out;
						},
						$expr[2]
					);
				return $str;
			},
			$str
		);
	}

compile是修改后加了过滤函数的地方

	function __unset($key) {
		$this->offsetunset($key);
	}
	function clear($key) {
		// Normalize array literal
		$cache=Cache::instance();
		$parts=$this->cut($key);
		if ($key=='CACHE')
			// Clear cache contents
			$cache->reset();
		elseif (preg_match('/^(GET|POST|COOKIE)\b(.+)/',$key,$expr)) {
			$this->clear('REQUEST'.$expr[2]);
			if ($expr[1]=='COOKIE') {
				$parts=$this->cut($key);
				$jar=$this->hive['JAR'];
				unset($jar['lifetime']);
				$jar['expire']=0;
				if (version_compare(PHP_VERSION, '7.3.0') >= 0) {
					$jar['expires']=$jar['expire'];
					unset($jar['expire']);
					setcookie($parts[1],NULL,$jar);
				} else {
					unset($jar['samesite']);
					call_user_func_array('setcookie',
						array_merge([$parts[1],NULL],$jar));
				}
				unset($_COOKIE[$parts[1]]);
			}
		}
		elseif ($parts[0]=='SESSION') {
			if (!headers_sent() && session_status()!=PHP_SESSION_ACTIVE)
				session_start();
			if (empty($parts[1])) {
				// End session
				session_unset();
				session_destroy();
				$this->clear('COOKIE.'.session_name());
			}
			$this->sync('SESSION');
		}
		if (!isset($parts[1]) && array_key_exists($parts[0],$this->init))
			// Reset global to default value
			$this->hive[$parts[0]]=$this->init[$parts[0]];
		else {
			$val=preg_replace('/^(\$hive)/','$this->hive',
				$this->compile('@hive.'.$key, FALSE));
			eval('unset('.$val.');');
			if ($parts[0]=='SESSION') {
				session_commit();
				session_start();
			}
			if ($cache->exists($hash=$this->hash($key).'.var'))
				// Remove from cache
				$cache->clear($hash);
		}
	}

这里是执行RCE的地方,和3.7.1比没有修改
我们已知使用unset()销毁并不能销毁的变量时会调用__unset()方法,这里会把我们传入的参数赋值到$key,经过过滤后执行eval,可以发现eval处只是简单的字符串拼接,用分号闭合后就可以在后面构造代码进行RCE了
compile处最后返回的$str是@hive.xxxxx的形式
主要看一下第二个正则/\.([^.\[\]]+)|\[((?:[^\[\]\'"]*|(?R))*)\]/
这里匹配的是以.开始后面是字符串加[]的形式或[]包裹字符串的形式
这里我们get传入a=a[b]);phpinfo();
解析后就会变成unset($this->hive[a][b]);phpinfo();//

3.慢慢做管理系统
第一步md5万能密码登录admin,这里过滤了ffifdyop
可以用129581926211651571912466741651878684928登录
然后要用内网gopher打admin.php
这里给的输入框一开始我以为是构造好的gopher协议…就填目标url就行…
结果大佬试了一下发现不对,这里应该只是个curl,要自己加上gopher://的头
然后用gopher构造post请求,网上教程有很多,需要注意的是这里发上去的请求一定要包含Content-TypeContent-Length不然会报错
这里随便贴一篇大佬写的构造post请求的博客
https://blog.csdn.net/weixin_45887311/article/details/107327706
后面是堆叠注入

上一篇:Defcon 2018 Qualify: Easy Pisy writeup


下一篇:BUUCTF笔记之Crypto部分WriteUp(二)