Conversion
String conversion and/or encoding is an important part of exploitation and firewall bypass
Convert String/Binary to Hex
If no prefix is needed, you just do the following
"Rubyfu".unpack("H*")
Otherwise, see the below ways
for a single character
'\x%02x' % "A".ord
Note: the symbols *""
are equal of .join
"ABCD".unpack('H*')[0].scan(/../).map {|h| '\x'+h }.join
or
"ABCD".unpack('C*').map { |c| '\x%02x' % c }.join
or
"ABCD".split("").map {|h| '\x'+h.unpack('H*')[0] }*""
or
"ABCD".split("").map {|c|'\x' + c.ord.to_s(16)}.join
or
"ABCD".split("").map {|c|'\x' + c.ord.to_s(16)}*""
or
"ABCD".chars.map {|c| '\x' + c.ord.to_s(16)}*""
or
"ABCD".each_byte.map {|b| b.to_s(16)}.join
or
"ABCD".each_char.map {|c| '\x'+(c.unpack('H*')[0])}.join
or
"ABCD".chars.map {|c| '\x%x' % c.ord}.join
Source: Ruby | Convert ASCII to HEX
Returns
\x41\x42\x43\x44
Convert Hex to String/Binary
["41424344"].pack('H*')
or
"41424344".scan(/../).map { |x| x.hex.chr }.join
or for raw socket
"41424344".scan(/../).map(&:hex).pack("C*")
Return
ABCD
in-case of binary that out of .chr
range. For example you may need to convert IP-address to hex raw then send it through socket. The case of just converting it to hex would not work for your
>> ip
=> "192.168.100.10"
>> ip.split(".").map {|c| '\x%02x' % c.to_i}.join
=> "\\xc0\\xa8\\x64\\x0a"
As you can see, Ruby reads returns "\\xc0\\xa8\\x64\\x0a"
which doesn't equal "\xc0\xa8\x64\x0a"
. Try to inter this value(with double-quotes) "\xc0\xa8\x64\x0a"
into your irb directly and you'll notice that the return is "\xC0\xA8d\n"
which what should be passed to the raw socket not the "\\xc0\\xa8\\x64\\x0a"
. The main cause is ruby escapes the backslash(\
).
To solve this issue, use pack to convert integers to 8-bit unsigned (unsigned char)
ip.split(".").map(&:to_i).pack("C*")
Note about hex: Sometimes you might face a none printable characters especially due dealing with binary raw. In this case, append (# -*- coding: binary -*-
) at top of your file to fix any interpretation issue.
Convert Hex(Return address) to Little-Endian format
Little-Endian format is simply reversing the string such as reversing/backwarding "Rubyfu" to "ufybuR" which can be done by calling reverse
method of String
class
"Rubyfu".reverse
In exploitation, this is not as simple as that since we're dealing with hex values that may not represent printable characters.
So assume we have 0x77d6b141
return address which we've to convert it to Little-Endian format to allow CPU to read it correctly.
Generally speaking, it's really a trivial task to convert 0x77d6b141
to \x41\xb1\xd6\x77
since it's one time process but this is not the case of you have ROP chain that has to be staged in your exploit. To do so simply pack
it as array
[0x77d6b141].pack('V')
It happens that sometime you get an error because of none Unicode string issue. To solve this issue, just force encoding to UTF-8 but most of the time you will not face this issue
[0x77d6b141].pack('V').force_encoding("UTF-8")
If you have ROP chain then it's not decent to apply this each time so you can use the first way and append (# -*- coding: binary -*-
) at top of your exploit file.
Convert to Unicode Escape
Hexadecimal unicode escape
"Rubyfu".each_char.map {|c| '\u' + c.ord.to_s(16).rjust(4, '0')}.join
Or using unpack
"Rubyfu".unpack('U*').map{ |i| '\u' + i.to_s(16).rjust(4, '0') }.join
shorter way
"Rubyfu".unpack('U*').map{ |i| "\\u00%x" % i }.join
Octal unicode escape
For octal escape is exact the same except we convert the string to octal instead of hexadecimal
"Rubyfu".each_char.map {|c| '\u' + c.ord.to_s(8).rjust(4, '0')}.join
En/Decode base-64 String
We'll present it by many ways
Encode string
["RubyFu"].pack('m0')
or
require 'base64'
Base64.encode64 "RubyFu"
Decode
"UnVieUZ1".unpack('m0')
or
Base64.decode64 "UnVieUZ1"
TIP: The string unpack method is incredibly useful for converting data we read as strings back to their original form. To read more, visit the String class reference at www.ruby-doc.org/core/classes/String.html.
En/Decode URL String
URL encoding/decoding is something known to most people. From hacker's point of view, we need it a lot in client-side vulnerability the most.
Encoding string
require 'uri'
puts URI.encode 'http://vulnerable.site/search.aspx?txt="><script>alert(/Rubyfu/.source)</script>'
Decoding string
require 'uri'
puts URI.decode "http://vulnerable.site/search.aspx?txt=%22%3E%3Cscript%3Ealert(/Rubyfu/.source)%3C/script%3E"
You can encode/decode and none URL string, of-course.
The above way will encode any non URL standard strings only(ex. <>"{}
) however if you want to encode the full string use URI.encode_www_form_component
puts URI.encode_www_form_component 'http://vulnerable.site/search.aspx?txt="><script>alert(/Rubyfu/.source)</script>'
HTML En/Decode
Encoding HTML
require 'cgi'
CGI.escapeHTML('"><script>alert("Rubyfu!")</script>')
Returns
"><script>alert("Rubyfu!")</script>
Decoding HTML
require 'cgi'
CGI.unescapeHTML(""><script>alert("Rubyfu!")</script>")
Returns
"><script>alert("Rubyfu!")</script>
En/Decode SAML String
Decoding SAML
# SAML Request
saml = "fZJNT%2BMwEIbvSPwHy%2Fd8tMvHympSdUGISuwS0cCBm%2BtMUwfbk%2FU4zfLvSVMq2Euv45n3fd7xzOb%2FrGE78KTRZXwSp5yBU1hpV2f8ubyLfvJ5fn42I2lNKxZd2Lon%2BNsBBTZMOhLjQ8Y77wRK0iSctEAiKLFa%2FH4Q0zgVrceACg1ny9uMy7rCdaM2%2Bs0BWrtppK2UAdeoVjW2ruq1bevGImcvR6zpHmtJ1MHSUZAuDKU0vY7Si2h6VU5%2BiMuJuLx65az4dPql3SHBKaz1oYnEfVkWUfG4KkeBna7A%2Fxm6M14j1gZihZazBRH4MODcoKPOgl%2BB32kFz08PGd%2BG0JJIkr7v46%2BhRCaEpod17DCRivYZCkmkd4N28B3wfNyrGKP5bws9DS6PKDz%2FMpsl36Tyz%2F%2Fax1jeFmi0emcLY7C%2F8SDD0Z7dobcynHbbV3QVbcZW0TlqQemNhoqzJD%2B4%2Fn8Yw7l8AA%3D%3D"
require 'cgi'
require 'base64'
require 'zlib'
inflated = Base64::decode64(CGI.unescape(saml))
# You don't need below code if it's not deflated/compressed
zlib = Zlib::Inflate.new(-Zlib::MAX_WBITS)
zlib.inflate(inflated)
Returns
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<samlp:AuthnRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\"agdobjcfikneommfjamdclenjcpcjmgdgbmpgjmo\" Version=\"2.0\" IssueInstant=\"2007-04-26T13:51:56Z\" ProtocolBinding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" ProviderName=\"google.com\" AssertionConsumerServiceURL=\"https://www.google.com/a/solweb.no/acs\" IsPassive=\"true\"><saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">google.com</saml:Issuer><samlp:NameIDPolicy AllowCreate=\"true\" Format=\"urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified\" /></samlp:AuthnRequest>\r\n"