Daddy! how can I exploit unlink corruption?
This is a game that reads the "flag" with "unlink_pwn" group permission.
When it is run, I can type something.
When it is run, I can type something.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct tagOBJ{
struct tagOBJ* fd;
struct tagOBJ* bk;
char buf[8];
}OBJ;
void shell(){
system("/bin/sh");
}
void unlink(OBJ* P){
OBJ* BK;
OBJ* FD;
BK=P->bk;
FD=P->fd;
FD->bk=BK;
BK->fd=FD;
}
int main(int argc, char* argv[]){
malloc(1024);
OBJ* A = (OBJ*)malloc(sizeof(OBJ));
OBJ* B = (OBJ*)malloc(sizeof(OBJ));
OBJ* C = (OBJ*)malloc(sizeof(OBJ));
// double linked list: A <-> B <-> C
A->fd = B;
B->bk = A;
B->fd = C;
C->bk = B;
printf("here is stack address leak: %p\n", &A);
printf("here is heap address leak: %p\n", A);
printf("now that you have leaks, get shell!\n");
// heap overflow!
gets(A->buf);
// exploit this unlink!
unlink(B);
return 0;
}
#include <stdlib.h>
#include <string.h>
typedef struct tagOBJ{
struct tagOBJ* fd;
struct tagOBJ* bk;
char buf[8];
}OBJ;
void shell(){
system("/bin/sh");
}
void unlink(OBJ* P){
OBJ* BK;
OBJ* FD;
BK=P->bk;
FD=P->fd;
FD->bk=BK;
BK->fd=FD;
}
int main(int argc, char* argv[]){
malloc(1024);
OBJ* A = (OBJ*)malloc(sizeof(OBJ));
OBJ* B = (OBJ*)malloc(sizeof(OBJ));
OBJ* C = (OBJ*)malloc(sizeof(OBJ));
// double linked list: A <-> B <-> C
A->fd = B;
B->bk = A;
B->fd = C;
C->bk = B;
printf("here is stack address leak: %p\n", &A);
printf("here is heap address leak: %p\n", A);
printf("now that you have leaks, get shell!\n");
// heap overflow!
gets(A->buf);
// exploit this unlink!
unlink(B);
return 0;
}
This is the full source code.
There is struct variables A, B and C that is linked each other. and unlink() unlinks them.
The hint "exploit this unlink" is in the source code. "gets(A->buf)" seems to be able to cause a heap overflow exploiting "unlink(B)".
I identified the addresses of major functions before analysis.
"[ebp-0x4]" value affects "esp", which means that it affects the return value of the main() function.
Eventually, if [ebp-0x4] is manipulated, the main() function can go to the shell() function on return.
"OBJ" variable is consisted of two pointers(4 Bytes * 2) and eight characters(1 Bytes * 8). Total is 16 bytes.
And before the return of main(), there is the "unlink(B)" function.
"unlink(B)" means this.
[B->fd]->bk = B->bk
[B->bk]->fd = B->fd
It may seem complicated, but it is easy to understand when memory is referred. I inserted the value and identified the structure.
The distance between "A" and "B" is 16. I checked in <+78> ~ <+115> of main() disassembled code.
Think of "->fd" as "+0x0" and think of "->bk" as "+0x4".
So "unlink(B)" means this.
[B]+0x4 = B+0x4
[B+0x4] = B
Based on the functionality of unlink(B), I thinked this kind of overflow.
"[ebp-0x4]" will have "A+0x8+0x4".
And look at this image again.
The "ebp-0x4" pointing value is stored in "ecx" and the [ecx-0x4] address value is stored in "esp".
Therefore, "A+0x8+0x4" is stored in "ecx" and "A+0x8" is stored in "esp". and "ret" stores "shell()" address pointed to by "esp" to "eip".
For reference, the address of "B" and the address of "EBP-0x4" change continuously. Since "B" is in heap area, the address of B is checked by the distance from the allocated address of "A". And since EBP is in stack area, the address of "ebp-0x4" is checked by the distance from the variable address of "A".
In summary, the data to be transmitted is composed as follows.
"shell() address(4 Bytes)" + "dummy(12 Bytes)" + "A+0x8+0x4" + "ebp-0x4"
Eventually, if [ebp-0x4] is manipulated, the main() function can go to the shell() function on return.
"OBJ" variable is consisted of two pointers(4 Bytes * 2) and eight characters(1 Bytes * 8). Total is 16 bytes.
And before the return of main(), there is the "unlink(B)" function.
"unlink(B)" means this.
[B->fd]->bk = B->bk
[B->bk]->fd = B->fd
It may seem complicated, but it is easy to understand when memory is referred. I inserted the value and identified the structure.
The distance between "A" and "B" is 16. I checked in <+78> ~ <+115> of main() disassembled code.
Think of "->fd" as "+0x0" and think of "->bk" as "+0x4".
So "unlink(B)" means this.
[B]+0x4 = B+0x4
[B+0x4] = B
Based on the functionality of unlink(B), I thinked this kind of overflow.
"[ebp-0x4]" will have "A+0x8+0x4".
* [B+0x4] = B
And look at this image again.
The "ebp-0x4" pointing value is stored in "ecx" and the [ecx-0x4] address value is stored in "esp".
Therefore, "A+0x8+0x4" is stored in "ecx" and "A+0x8" is stored in "esp". and "ret" stores "shell()" address pointed to by "esp" to "eip".
For reference, the address of "B" and the address of "EBP-0x4" change continuously. Since "B" is in heap area, the address of B is checked by the distance from the allocated address of "A". And since EBP is in stack area, the address of "ebp-0x4" is checked by the distance from the variable address of "A".
In summary, the data to be transmitted is composed as follows.
"shell() address(4 Bytes)" + "dummy(12 Bytes)" + "A+0x8+0x4" + "ebp-0x4"