C'est la bonne surprise que j'ai eu. Ce n'est pas un challenge orienté applicatif mais plus web (enfin à mon sens) :).
Voilà le code du level :
#include <iostream> #include <fstream> #include <string> std::string strreplace(const char *msg, const char *replace, const char *with) { std::string ret; while(*msg) { if(strncmp(msg, replace, strlen(replace)) == 0) { ret += with; // Skip all in msg until we have another match msg++; for(unsigned int i = 1; i < strlen(replace) && *msg; i++) { if(strncmp(msg, replace, strlen(replace)) == 0) break; msg++; } continue; } else ret += *msg; msg++; } return ret; } int main(int argc, char **argv) { if(argc < 2) { std::cout << "This program allows you to read files from my shared files. See /usr/share/level5 for my shared files. Simply use the path relative to my shared files to read a file!" << std::endl; std::cout << "Example: " << argv[0] << " lyrics/foreverautumn" << std::endl; return 1; } std::string start_path = "/usr/share/level5/"; std::string relative_path = ""; char *ptr; ptr = argv[1]; while(*ptr == '/' || *ptr == '.') ptr++; relative_path = strreplace(ptr, "/../", ""); relative_path = strreplace(relative_path.c_str(), "/./", ""); std::string realpath = start_path + relative_path; std::cout << "Contents of " << realpath << ":" << std::endl; std::ifstream file(realpath.c_str(), std::ios::in); if(!file.is_open()) { std::cerr << "Unable to open file" << std::endl; return 1; } std::string cline; while(!file.eof()) { std::getline(file, cline); std::cout << cline << std::endl; } return 0; }Ici on a accès à un système de lecture de fichier "partagés". On peut voir qu'il y a un système de filtrage mis en place.
Les "/../" et "/./" sont filtrés et celà ne permet pas (à priori) de lire un fichier arbitraire via un path traversal.
En analysant le code, voilà ce que le programme fait :
- On récupère un chemin venant de l'utilisateur
- On enlève tous les '/' et '.' en début de la chaine soumise par l'utilisateur
- On filtre les "/../" puis "/./"
Ce qu'on va attaquer est donc la fonction de filtrage : strreplace().
Il y a 2 problêmes dans son utilisation :
- "/../" est filtré avant "/./", en effet si on filtrait "/./" en premier, ça serait plus difficile à bypasser
- la fonction strreplace() ne fait pas le remplacement des chaînes de manières recursive, il est donc possible de les reconstruire si crafté correctement
Et là ça me fait étrangement penser aux failles de filtrage qu'on peut voir sur certaines applications web :
- filtre sur <script> : <scri<script>pt>, après filtrage on re-obtient <script> si filtrage non récursif.
On va ici utiliser le même principe, on veut reconstruire "/../". Pour celà il suffit d'injecter des chaînes comme "/./.././", après filtrage "/../" sera reconstruit.
Essayons :
level4@blackbox:~$ ./shared lyrics/./../././../././../././.././home/level5/password Contents of /usr/share/level5/lyrics/../../../../home/level5/password: Traveller
Pawn ;).
m_101
Aucun commentaire :
Enregistrer un commentaire