Logo

Scripting

Learn basic scripting by solving some challenges!

Scripting

TryHackMe: Прохождение комнаты Scripting.

Доступ к комнате доступен только для аккаунтов с премиальной подпиской.

Комната предполагает написание ряда скриптов на python, но предпочтение отдано практике на Go.

[Easy] Base64

Дан файл с закодированными 50 раз данными в base64. Требуется восстановить исходный текст. Скрипт предлагается написать на bash.

#!/bin/bash

data=`cat Downloads/b64.txt`

echo -ne "Complete: 0%\r"
for i in {1..50}
do
    data=`echo $data | base64 --decode`
    echo -ne "Complete: $((i * 2))%\r"
done

echo -e "\nFlag: \033[1;33m$data\033[0m"
Первый скрипт

Поскольку процесс далеко не мгновенный, добавлено отображение прогресса в виде счётчика (9).

[Medium] Gotta Catch em All

Необходимо подключиться к определенному порту на сервере, выполнить полученную арифметическую операцию над числом и перейти к следующему порту.

Формат задания: операция, число, порт.
Пример задания: add 900 3212, что означает необходимо прибавить к числу значение 900 и далее перейти к порту 3212. Повторять до тех пор, пока не будет получена команда STOP или встретится порт 9765. Каждый порт доступен только 4 секунды, после чего происходит переход к следующему. Чтобы начать с самого начала, необходимо дождаться активации порта 1337. По адресу http://<machines_ip>:3010 указан текущий активный порт.

Работа скрипта №2
package main

import (
    "flag"
    "fmt"
    "github.com/charmbracelet/huh/spinner"
    "github.com/charmbracelet/lipgloss"
    "io"
    "net/http"
    "net/url"
    "os"
    "strconv"
    "strings"
    "time"
)

// settings
var targetUrl, proxy string
var initialPort int

// data
var port int
var result float64 = 0

const (
    startPort = 1337
    endPort   = 9765
)

var styleNone = lipgloss.Style{}

func init() {
    flag.StringVar(&targetUrl, "url", "", "address of target server")
    flag.IntVar(&initialPort, "port", 3010, "initial port")
    flag.StringVar(&proxy, "proxy", "", "address of proxy")
    flag.Parse()

    if targetUrl == "" {
        fmt.Println("No arguments defined")
        flag.PrintDefaults()
        os.Exit(0)
    }

    if proxy != "" {
        // os.Setenv("HTTP_PROXY", "socks5://...:1080")
        proxyUrl, _ := url.Parse(proxy)
        http.DefaultTransport = &http.Transport{Proxy: http.ProxyURL(proxyUrl)}
    }
}

func main() {
    _ = spinner.New().
        Type(spinner.Points).Style(styleNone).
        Title(" Awaiting appropriate port...").TitleStyle(styleNone).
        Action(awaitStart).Run()

    if port != startPort {
        // Пользователь нажал ctrl+c, выход
        os.Exit(0)
    }

    for {
        var res *http.Response
        portwait := func() {
            for {
                var err error
                res, err = http.Get(targetUrl + ":" + strconv.Itoa(port))
                if err == nil {
                    break
                }
                time.Sleep(2 * time.Second)
            }
        }
        _ = spinner.New().
            Type(spinner.Line).Style(styleNone).
            Title(fmt.Sprintf(" awaiting port %d...", port)).TitleStyle(styleNone).
            Action(portwait).Run()

        body, _ := io.ReadAll(res.Body)
        _ = res.Body.Close()
        data := string(body)
        var op string
        var val float64
        _, _ = fmt.Sscanf(data, "%s %f %d", &op, &val, &port)

        fmt.Println(">", op, val)
        switch op {
        case "multiply":
            result = val * result
        case "minus":
            result = result - val
        case "add":
            result = result + val
        case "divide":
            result = result / val
        case "STOP":
            break
        }

        if port == endPort {
            break
        }
    }

    fmt.Println("result:", result)
}

func awaitStart() {
    rhost := targetUrl + ":" + strconv.Itoa(initialPort)
    for {
        client := http.Client{
            Timeout: 5 * time.Second,
        }
        res, err := client.Get(rhost)
        if err != nil {
            fmt.Println("Can't connect to", rhost)
            panic(err)
        }
        body, _ := io.ReadAll(res.Body)
        _ = res.Body.Close()

        str := string(body)
        i := strings.Index(str, "onPort") + 8
        if len(str) < i {
            panic("Responce too short, no port data")
        }
        _, _ = fmt.Sscanf(str[i:], "%d", &port)

        if port == startPort {
            break
        } else {
            time.Sleep(time.Second * 4)
        }
    }
}

[Hard] Encrypted Server Chit Chat

Необходимо подключиться по UDP к порту 4000, после чего послать сообщение hello для получения последующих инструкций. Общий смысл в том, что необходимо расшифровать полученные данные. Данные зашифрованы с помощью AES-GCM, часть получаемые данных может быть подделана.

Результат скрипта №3

Стоит отметить, что передаваемый в данных вектор исполнения (инициализации, IV) так же нередко называется nonce, как в используемой в коде библиотеке. Смысл у данных терминов несколько разный, но принцип работы одинаков.

package main

import (
    "bufio"
    "crypto/aes"
    "crypto/cipher"
    sha256 "crypto/sha256"
    "encoding/hex"
    "errors"
    "flag"
    "fmt"
    "net"
    "os"
    "strconv"
    "strings"
)

// settings
var rhost string

const port = 4000

func init() {
    flag.StringVar(&rhost, "rhost", "", "address of target server")
    flag.Parse()

    if rhost == "" {
        fmt.Println("No arguments defined")
        flag.PrintDefaults()
        os.Exit(0)
    }
}

func main() {
    var conn net.Conn
    var err error
    if conn, err = net.Dial("udp", rhost+":"+strconv.Itoa(port)); err != nil {
        panic(err)
    }

    fmt.Println(" --- Conversation ---")
    fmt.Println("\033[34;2m> hello\u001B[0m")
    if _, err = fmt.Fprint(conn, "hello"); err != nil {
        panic(err)
    }

    const BufSize = 512
    reader := bufio.NewReader(conn)
    buf := make([]byte, BufSize)
    var read int

    if read, err = reader.Read(buf); err != nil || read > BufSize {
        panic(err)
    }
    var str string = string(buf[:read])
    if fmt.Println("<", str); err != nil {
        panic(err)
    }
    if !strings.Contains(str, "connected") {
        panic(errors.New("abnormal welcome message"))
    }

    fmt.Println("\u001B[34;2m> ready\u001B[0m")
    if _, err = fmt.Fprint(conn, "ready"); err != nil {
        panic(err)
    }
    if _, err = reader.Read(buf); err != nil {
        panic(err)
    }
    str = string(buf)
    fmt.Println("<", str)

    data := strings.Split(str, " ")
    if len(data) != 27 {
        panic(errors.New("abnormal response"))
    }
    if len(data[14]) != 32 {
        panic(errors.New("non SHA256 checksum"))
    }

    var key, iv string
    var checksum [32]byte
    key = strings.Split(data[0], ":")[1]
    iv = strings.Split(data[1], ":")[1]
    copy(checksum[:], []byte(data[14]))

    fmt.Println("\n --- Data ---")
    fmt.Printf("key:      %s [%s]\niv:       %s\nchecksum: %s\n\n",
        key, aesType(len(key)), iv, hex.EncodeToString(checksum[:]))

    fmt.Println(" --- Processing ---")
    for {
        if _, err = fmt.Fprint(conn, "final"); err != nil {
            panic(err)
        }
        if read, err = reader.Read(buf); err != nil {
            panic(err)
        }
        ciphertext := string(buf[:read])
        //fmt.Print("ciphertext:\n", hex.Dump([]byte(ciphertext)))

        if _, err = fmt.Fprint(conn, "final"); err != nil {
            panic(err)
        }
        if read, err = reader.Read(buf); err != nil {
            panic(err)
        }
        tag := string(buf[:read])
        //fmt.Print("tag:\n", hex.Dump([]byte(tag)))

        block, _ := aes.NewCipher([]byte(key))
        gcm, _ := cipher.NewGCM(block)
        if gcm.NonceSize() != len(iv) {
            fmt.Print("error: iv have wrong length\n")
            continue
        }

        ct := ciphertext + tag
        pt, err := gcm.Open(nil, []byte(iv), []byte(ct), nil)
        if err != nil {
            fmt.Println("fail:", err)
            continue
        }

        if checksum == sha256.Sum256(pt) {
            fmt.Printf("\033[32;1m%s\033[0m  <-- flag!\n", string(pt))
            break
        } else {
            fmt.Printf("\033[31;1m%s\033[0m\n", string(pt))
        }
    }
}

func aesType(len int) string {
    switch len {
    case 16:
        {
            return "AES-128"
        }
    case 24:
        {
            return "AES-192"
        }
    case 32:
        {
            return "AES-256"
        }
    }
    return "unknown"
}

На этом всё.