Terrmaster 分析
date
Sep 27, 2020
URL
slug
tnas-analysis
status
Published
tags
固件研究
summary
对铁威马NAS的分析过程记录
type
Post
解码固件
我是直接用最粗暴的方法用360压缩无限解压就把固件源码拖出来了,对固件研究太浅显,不知道正规分析流程是怎样的。
解密php
目前我们能找到的固件版本是4.1.30,其php代码是基于screw-plus作了魔改升级。
具体修改是其增加了一个从文件流中获得的iv对其key进行异或的操作
screw_aes(0,datap,datalen,key,&datalen,v10);
for(i=0;i<blocks;i++) {
int v14=0LL;
if(crypt)
{
aes_crypt_cbc(&aes, AES_ENCRYPT, 16, key, buf+i*16, buf+i*16);
int v15 =0LL;
do
{
*(buf+i*16 + v15++) ^= iv;
}
while ( v15 != 16 );
}
else
{
do
*(buf+i*16 + v14++) ^= iv;
while ( v14 != 16 );
aes_crypt_cbc(&aes, AES_DECRYPT, 16, key, buf+i*16, buf+i*16);
}
这是原版
if(crypt)
aes_setkey_enc( &aes, key, 256 );
else
aes_setkey_dec( &aes, key, 256 );
for(i=0;i<blocks;i++) {
if(crypt)
aes_crypt_cbc(&aes, AES_ENCRYPT, 16, key, buf+i*16, buf+i*16);
else
aes_crypt_cbc(&aes, AES_DECRYPT, 16, key, buf+i*16, buf+i*16);
}
iv是一个根据文件输入不同他的值也不同的变量,但他只是一个char所以可以采用爆破获得其值
#!/bin/bash
function read_dir(){
B=".php"
for file in `ls $1`
do
#result=$(echo $i | grep ".php")
if [ -d $1"/"$file ]
then
read_dir $1"/"$file
else
if [[ $file == *$B* ]]
then
for j in {1..255}
do
result=$(./unscrew GH65Hws2jedf3fl3MeK $1"/"$file $j | grep php)
if [[ $result != "" ]]
then
echo $result
./unscrew GH65Hws2jedf3fl3MeK $1"/"$file $j >$1"/"$file.dec
fi
done
fi
fi
done
}
read_dir $1
然后通过
find ./ -name "*.php"|xargs rm -rf {}
find ./ -name "*.dec" | xargs rename 's/.php.dec/.php/'
批量修改名字
代码审计
设备还没到,就先大致看了一圈,感觉被临时补上的地方有好多,原本好多可以前台rce的地方也直接被加登陆验证变得鸡肋了。
花了两天时间能找到的东西并没有想的那么多,就先暂且当提炼一下思路,简单写一下我大概了解的能找到漏洞的地方。
代码审计辅助工具
之前我也没用过什么代码审计工具,最近了解并试用了一下,感觉还都可以
- Seay源代码审计工具(作者官网早挂了,搞金融挣钱去了。没空管这个都直接开源了)
- cobra (能对php和java进行源代码审计,使用的是自己写的匹配规则,还挺好用)
- rips (很强的产品,免费版都已经很厉害了,不知道收费版是啥样的。)
- kunlun-M (vidar 的大哥基于cobra魔改出了自己的产品 )
命令执行
首先是最想要找的rce,php是个很灵活的语言,它能够实现命令执行的方法很多,我们先去观察的也是那些危险函数。可以先用代码审计工具帮我们去扫描一下,去定位一下敏感函数,然后去回溯变量,看是否能够实现变量可控以及它的过滤是否严格。
命令注入的函数有:
- shell_exec
- popen
- proc_open
- system
- exec
- passthru
代码注入的函数有:
eval、preg_replace+/e、assert、call_user_func、
call_user_func_array、create_function
![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Ffe2d8d75-a888-49ca-b399-3984269ac4fe%2FUntitled.png?table=block&id=d69b8ecd-3278-4b8a-ae73-5fbd482c4986&cache=v2)
其次就是其自己构造实现命令执行的函数,这套系统里只是对原本的exec加上了,这个过滤跟没过滤没啥区别。如果遇到用它执行的前面的参数没有过滤的话就能很好绕过。
function _exec($cmd, $type=NULL, $part="/[\n\r]+/")
{
$cmd = str_replace("/bin/bash", "", $cmd);
$cmd = str_replace("/bin/sh", "", $cmd);
$ret = shell_exec($cmd);
switch($type)
{
case 'array':
$ret = preg_split($part, $ret, -1, 1);
break;
case 'json':
$ret = preg_split($part, $ret, -1, 1);
$ret = json_encode($ret);
break;
case 'original':
return $ret;
break;
default:
return $this->_replace($ret);
}
return $ret;
}
感觉应该是命令执行的地方
- /include/exportUser.php
![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F40bb81f0-1e5a-4637-bf61-63ee10c74607%2FUntitled.png?table=block&id=1bc4d847-34ca-4652-8c01-3b0b607995d2&cache=v2)
类名可控,函数名可控,参数可控,
但是检测了登陆。
![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fc63c2427-5ce4-4b21-9a3e-6c4066cf10fa%2FUntitled.png?table=block&id=0e2d0a96-95cf-4b54-aff8-ae92f1611703&cache=v2)
2.
跟上面一样
![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Ff681cdaa-3cbe-40af-841e-bfdb222e1ea5%2FUntitled.png?table=block&id=34de217c-19bb-4074-9288-0c391a0bec7a&cache=v2)
3. www/include/class/VPN.class.php
![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F2d9d9eb4-e5d4-4a36-a3b7-c3a5850236ee%2FUntitled.png?table=block&id=09f0fe34-5aa4-49ed-93b6-e6be95332612&cache=v2)
也是有可控参数可以进行命令执行,但他只能在上面那个地方调用。同样需要登陆。
![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F15527b65-cfc8-43a4-9324-5742ec92d456%2FUntitled.png?table=block&id=94135e96-bea8-49b5-a1d8-d52067e4388b&cache=v2)
4. plugs.class.php
本来以为这是一个完美的前台rce,但最后还是发现有登陆验证
plugs.class.php
function _logtotal($type)
{
$total = 0;
$type = $this->fun->_skip($type, "/[\s\t ]+/");
$list = $this->fetchAllFiles($type);
foreach ($list as $file) {
$total += $this->countLines($file);
}
return $total;
}
plugs.class.php
private function fetchAllFiles($type)
{
$zlog = "/var/log/zlog";
if (!file_exists($zlog)) return array();
$return = $this->fun->_exec("ls -t1 {$zlog}/{$type}_*", "array");
return $return;
}
if($data['tab'] == 'gettotal'){
$plugs = new plugs();
echo $plugs->_logtotal($data['Event']);
$data = $_POST;
handleParm($data);
$data['Event']
—>$type
fetchAllFiles($type)
_exec("ls -t1 {$zlog}/{$type}_*", "array")
$type=.;command;即可实现命令执行
但可惜
handleParm($data)
![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F57f826fa-4386-4e40-af50-794b14801829%2FUntitled.png?table=block&id=adfa9f64-9b04-4ffe-8f37-ad6f3a4912f5&cache=v2)
在wap版本里的
function event_log(){
$array_log = array();
$plugs = new plugs();
$tab = $this->in['tab'];
$event = $this->in['Event'];
$page = $this->in['page'];
$per = $this->in['per'];
if($tab == 'datatable'){//ajax 加载的数据
$lines = $plugs->_loglist($event,($page-1)*$per, $per);
if(count($lines) > 0){
$array_log["log"] = $lines;
}
}else if($tab == 'gettotal'){
$array_log["allLog"] = $plugs->_logtotal($event);
}else if($tab == 'cleanrs'){
$array_log["clear"] = $plugs->_cleanrs($event);
}
$this->output("success", true, $array_log);
}
也同样调用了 但没有经过过滤,还是要登陆,只能用于后台rce
Sql注入
sql注入主要就是要找能够与数据库产生交互的地方,查看之前的漏洞信息发现有前台sql注入,查看详情得知是日志文件写入数据库的交互有利用空间,但现版本日志直接写入到了文件,不再与数据库由交互,且登陆时所有的信息它都base64后再代入数据库查询,没有找到能够注入的地方。
![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F20615379-1cbe-4da4-b845-fc8f9770fdfe%2FUntitled.png?table=block&id=c233b68c-172a-4dab-b8de-e14eef922fee&cache=v2)
数据库采用的还是sqllite,虽然找不到注入,但也增加了我们的一个如果能找到任意文件读取即可下载数据库的思路
XSS
没有什么能够在未登录状态下实现交互的地方,所以存储型xss明面上很难找,反射型xss又都是鸡肋,而且他这里直接echo 变量的地方太多了所以用扫描器一扫大概都能找到100+ 反射型xss
![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F55254e0c-f004-4640-83b6-21459d5d76ea%2FUntitled.png?table=block&id=332c7fee-d807-47c6-b1b8-f99d2e5ecbcb&cache=v2)
会话固定
$in = parse_incoming();
//加载原始session
if(isset($in['PHPSESSID']) && !empty($in['PHPSESSID'])){
@session_id($in['PHPSESSID']);
}
@session_start();
@session_write_close();
function parse_incoming(){
global $_GET, $_POST,$_COOKIE;
$_COOKIE = stripslashes_deep($_COOKIE);
$_GET = stripslashes_deep($_GET);
$_POST = stripslashes_deep($_POST);
$return = array_merge($_GET,$_POST);
$remote = array_get($return,0);
$remote = explode('/',trim($remote[0],'/'));
$return['URLremote'] = $remote;
return $return;
}
没啥卵用 ,但能交cve的一个洞
其他的一些尝试
所有的尝试都是想要去获得登陆权限,从而实现rce
在没有用户的前提下去获得登陆权限的思路有:
Sql注入
存储型xss
读取数据库文件
任意文件删除 删除install.lock重装系统
![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fc26dbc40-3243-4083-a7eb-67fbebc6af29%2FUntitled.png?table=block&id=2f6a03b4-5c6d-4b54-9fdf-8db0b048a083&cache=v2)
其系统经历这么多次迭代,前台能够访问到的功能已经太少了。
最后还是没有找到能够获得登陆权限的地方。
END