CTF Writeup

2023 Bauhinia CTF - Very Simplified RPG

LittleDev0617 2023. 8. 21. 13:09

Build docker with given files and run.

Then localhost:8080 will be opened.

It is Unity WebGL!

Let's press the Play Button.

There are some monsters,

secret chest,

boss,

annnnnd boss chest.

 

 

First I want to open the chest arounded with the fence.

To open the chest we need to change Player's X value.

 

Open the devtools then some logs are listed.

They give the hint about player position.

go to js code.

Set the break point and move the player.

I'm going to chase a Call stack to find player's X memory address.

Click each call stack, set breakpoint, watch the memory with local variable values as memory address.

We can guess they make string with some format like "[%s] Player Rigidbody2D position x: %.f", ~~, ~~.

So check each call stack when string is formatted and x value is formatted.

 

I explored call stack and set a lot of breakpoints.

 

one of them I can find "%.40e" format string and formatted x value.

 

Finally I can find f32.load instruction.

0x2130CFC address is one of x values in the memory!

If I moved character then the memory changed.

 

I tried to change but real character x is in other memory.

var chestX = [0xf7, 0x74, 0x2c, 0x3f];
var offset = 0x02130CFC;
var buffer = new Uint8Array(memories.$Ug.buffer);
for (let index = 0; index < 4; index++) {
    buffer[offset+index] = chestX[index];
}

 

So I made some javascript code similar with cheat engine.

// need to set breakpoint at anywhere and run below line.
// var m = memories;

function compareMemory(m1, m2, len) {    
    for (let i = 0; i < len; i++) {
        if(m1[i] != m2[i])  
            return false;
    }
    return true;
}

function scanMemory(findValue) {
    var buffer = new Uint8Array(m.$Ug.buffer);
    var addressList = [];

    for (let index = 0; index < buffer.byteLength; index++) {
        if(index % 0x100000==0)
            console.log(index.toString(16));
        if(compareMemory(buffer.slice(index, index+findValue.length), findValue, findValue.length)) {
            addressList.push(index);
            // console.log(index.toString(16));
        }
    }
    return addressList;
}

function chaseMemory(memList, chaseValue) {
    var buffer = new Uint8Array(m.$Ug.buffer);
    var ml = [];
    for(var address of memList) {
        if(compareMemory(buffer.slice(address, address + chaseValue.length), chaseValue, chaseValue.length)) {
            ml.push(address);
        }
    }
    return ml;
}

function changeX(memList, x) {
    var buffer = new Uint8Array(m.$Ug.buffer);
    for(var address of memList) {
        for (let i = 0; i < x.length; i++) {
            buffer[address+i] =  x[i];
        }
    }
}

function getX() {
    var offset = 0x02130CFC;
    var buffer = new Uint8Array(m.$Ug.buffer);
    return buffer.slice(offset, offset+4);
}



function changeStrength(str) {
    var offset = 40116028;
    var buffer = new Uint8Array(m.$Ug.buffer);
    
    for (let i = 0; i < str.length; i++) {
        buffer[offset+i] =  str[i];
    }

}

function changeLevel(level) {
    var offset = 40116008;
    var buffer = new Uint8Array(m.$Ug.buffer);
    for (let i = 0; i < level.length; i++) {
        buffer[offset+i] =  level[i];
    }
}


var chestX = [0xf7, 0x74, 0x2c, 0x3f];

let myX = getX();
let xList = scanMemory(myX);

// move
myX = getX();
let mem2 = chaseMemory(xList, myX);
changeX(mem2, chestX)

let strList = scanMemory([13]);

// let mem2 = chaseMemory(xList, );
// changeX(mem2, chestX)

changeStrength([0x00,0x00,0x12,0x00])


// no need. just edit strength then can kill boss
let levelList = scanMemory([14]);
levelList = chaseMemory(levelList, [15])
levelList = chaseMemory(levelList, [16])
levelList = chaseMemory(levelList, [17])
// level = 40116008

 

First scan the memory with specific value and get whole list of memory address that have the value.

Second change the value(move character, level up), scan the memory with changed value in the list and only stay the memory address that have changed value.

- ex)

Scan the memory with 11 value(strength) and get memory address list. there are a lot of memory that have 0x1234 and list will be large.

With level up change strength 11 to 13. Find the memory address that have 13 value in the list.

If list length is still large, level up and find until list length is low.

So we can open the chest after change x value. If open the chest level become 14.

 

I tried to kill the boss but it's so hard like Dark Souls. :(

So I found the strength memory address with Scan | Chase | Chase | Chase like below, and I make changeStrength function.

let strList = scanMemory([14]); // 14 : start strength
// level up
strList = chaseMemory(strList, [15])
// level up
strList = chaseMemory(strList, [16])
// level up
strList = chaseMemory(strList, [17])

// strList : [40116028]

function changeStrength(str) {
    var offset = 40116028;
    var buffer = new Uint8Array(m.$Ug.buffer);
    
    for (let i = 0; i < str.length; i++) {
        buffer[offset+i] =  str[i];
    }
}

changeStrength([0x00,0x00,0x12,0x00])

 

Boss died by one attack :D

Flag : b6actf{345y_w3b6l_64m3_h4ck1n6_w17h_c37u5}

'CTF Writeup' 카테고리의 다른 글

2023 WACON Qual - Adult Artist  (2) 2023.09.03
2023 hspace CTF  (0) 2023.09.01
2023 SSTF - Dusty Code  (0) 2023.08.21
2023 SSTF - Libreria  (0) 2023.08.21
AmateursCTF 2023 - flagchecker Writeup  (0) 2023.07.19