Skip to content

Commit c78caf5

Browse files
committed
Improve writeup
1 parent 169a5df commit c78caf5

2 files changed

Lines changed: 34 additions & 23 deletions

File tree

  • content/posts/tfcctf25-cromozominus-rex
  • static/tfcctf25/cromo

content/posts/tfcctf25-cromozominus-rex/index.md

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
# example: UIUCTF 25 - ELF Capsule
3-
title: TFCCTF 25 - Cromozominus Rex
3+
title: TFC CTF 25 - Cromozominus Rex
44

55
# date of publication/creation
66
date: '2025-09-02T11:56:22+02:00'
@@ -102,18 +102,19 @@ undefined4 syscall(undefined4 param_1,undefined4 param_2)
102102
}
103103
```
104104
105-
System calls are triggered with `trap 0` in CSKY.
105+
This function will come in handy later.
106+
For now, know that system calls are triggered with `trap 0` in C-SKY.
106107
107-
Now that we found the overflow, we can start ropping.
108-
But first we need to study the architecture a bit.
108+
Well, we found the buffer overflow.
109+
But before we start ropping, a closer look at this architecture is in order.
109110
110111
### C-SKY 101
111112
112113
There are a lot of registers, but we care only about a handful:
113114
114115
|Register|Usage |
115116
|--------|-----------|
116-
|`r0` |First argument|
117+
|`r0` |First argument (& return value)|
117118
|`r1` |Second argument|
118119
|`r2` |Third argument|
119120
|`r3` |Fourth argument|
@@ -127,20 +128,20 @@ the address from the stack, but jumps on the link register.\
127128
So we need to find gadgets that pop to `r15` for the ROP chain.
128129
129130
Another important fact: the stack is not randomized.
130-
Every program execution got the same stack addresses (around `0x3ffff000`).
131+
Every program execution has the same stack addresses (around `0x3ffff000`).
131132
I did find a slight address discrepancy when running on Docker though.
132133
133134
> Note that in both challenges the stack is executable.\
134-
> I already started with the ROP route, but in mucusuki
135-
> you could put shellcode in the buffer.
135+
> I had already started with the ROP route, but you
136+
> could flag mucusuki by jumping to shellcode in the buffer.
136137
137138
### The syscall gadget
138139
139140
While the decompiled code is useful to get an overview of the program,
140141
to ROP we need to get to the assembly.
141142
Since my objdump did not support C-SKY, I just disassembled from gdb.
142143
143-
To help you understand the assembly I commented a bit the instructions:
144+
To help you understand the assembly I commented the instructions a bit:
144145
145146
```asm
146147
// get_input
@@ -207,7 +208,7 @@ Now that you've got the gist of it, let's see the `syscall` function:
207208
0x8262: rts
208209
```
209210

210-
As we can see this function does a bit more manipulation than what ghidra decompiled.\
211+
As we can see this function does a bit more than what ghidra decompiled.
211212
Let's rewrite it in pseudo code:
212213

213214
```c
@@ -229,6 +230,8 @@ undefined syscall(sysnr, arg1, arg2, arg3)
229230
To swap the registers appropriately for the syscall calling convention, they are stored and loaded from the stack.\
230231
Thus, we can store the register values that we want in the stack and jump in the middle of the function.
231232
233+
Also, note how `21` is subtracted from the syscall number (`r7`) after it was masked by `255`.
234+
232235
### The exploit
233236
234237
With gdb we can figure out the fixed stack position of the input buffer.
@@ -278,7 +281,9 @@ Let's follow this advice and move on to cromozominus.
278281

279282
## Cromozominus Rex
280283

281-
The program is the same except for the `get_input` function.\
284+
Similarly to before, we get the `crorex` and `qemu` binaries.
285+
286+
The program is the same, except for the `get_input` function.\
282287
A huge if statement was added to check if disallowed bytes were present in the buffer.
283288
If they are found, the function calls exit instead of returning,
284289
skipping our ROP chain.
@@ -430,11 +435,12 @@ Thus, I started looking for suitable gadgets in the the whole program.
430435
431436
### Debugging
432437
433-
This was one of the first times I tried a challenge in an exotic architecture (reading as, not x86).
434-
Debugging this kind of challenge is always hard, as pwntools' `gdb.attach` simply won't work.
438+
This was one of the first times I tried a challenge in an exotic architecture (reading as in, not x86).
439+
Debugging this kind of challenge is always hard, as pwntools' `gdb.attach` simply won't work
440+
when qemu and Docker enter the mix.
435441
436-
So I spent quite a bit of time preparing a comfortable gdb setup.
437-
Since this helped me immensely, I think it's worth sharing.
442+
So, I spent quite a bit of time preparing a comfortable gdb setup.
443+
Since this helped me lots, I think it's worth sharing.
438444
439445
First of all, I exposed a port for qemu's gdbserver:
440446
```Dockerfile
@@ -452,11 +458,17 @@ with open('args.gdb','w') as f:
452458
process(context.terminal + ["sh", "-c", f"sleep 1; sudo gdb -x args.gdb {e.path}"])
453459
```
454460

455-
Another point of frustration was that C-SKY is not supported by `pwndbg`.
456-
To mitigate the pain of using the tui layout and default commands,
457-
I made a small script to help me associate addresses with function names.
461+
At this point I got a gdb window connected to the remote executable.\
462+
However, we immediately stumble upon another point of frustration:
463+
`pwndbg` has no support whatsoever for C-SKY :wilted_flower:
464+
465+
![sad](/tfcctf25/cromo/sad.png)
466+
467+
I needed a way to mitigate the pain of only being able to use the TUI layouts and the default commands...\
468+
So I made this small script to help me associate addresses with function names.
458469

459470
```py
471+
# start - end address
460472
syms = {
461473
"get_input": (0x000823c, 0x8770),
462474
"entry": (0x8778, 0x87ae),
@@ -488,20 +500,19 @@ import os
488500
os.system("as -g sym.S -o sym.o")
489501
```
490502

491-
This script produces an object file that can be loaded with `add-symbol-file sym.o 0x0`.
503+
This script produces an object file that can be loaded with `add-symbol-file sym.o 0x0`.\
504+
Not the end of the world, but we can finally see where the exploit is jumping around :wink:
492505

493506
![gdb2](/tfcctf25/cromo/gdb2.png)
494507

495-
Now we can see where the exploit is jumping around :wink:
496-
497508
### Final script
498509

499510
This is the flow of the first payload:
500511
1) setup the buffer with fd (`0`), size (`0xff`) and buffer address
501512
2) set r3 to the buffer address + 16
502513
3) set r8 to the buffer address + 16
503514
4) call the read gadget
504-
5) do the second stage (basically the same as *mucusuki*)
515+
5) do the second stage (basically *mucusuki*'s payload)
505516

506517
```py
507518
from pwn import *
@@ -599,7 +610,7 @@ if __name__ == "__main__":
599610
main()
600611
```
601612

602-
I finished the script and got the flag at 4AM :pray:
613+
Fun fact: I finished this script and got the flag at 4AM :pray:
603614

604615
```
605616
TFCCTF{cromozominus_pulisaki_in_redacted_cro++_crorex_crovid}

static/tfcctf25/cromo/sad.png

149 KB
Loading

0 commit comments

Comments
 (0)