Logstash Converting Base64 IP to Decimal IP

Hello,

I'm trying to convert a base64 IP into a decimal IP. I saw that Logstash Ruby filter plugin can assist with this by parsing the message field, but is there a way to do this for just an IP field?

Test Conf

input { generator { lines => ['{ "base64_ip": "OC44LjguOA==" }'] count => 1 codec => "json" } }
filter {
 ruby { 
  code => 'event.set("decoded_ip", Base64.decode64(event.get("base64_ip")))' 
 }
}    
output { stdout { codec => json_lines } }

Output

{
    "base64_ip": "OC44LjguOA==",
    "decoded_ip": "8.8.8.8"
}

Thank you @aaron-nimocks!

1 Like

Hi @aaron-nimocks,

I tried using the ruby script, but now I have a problem and two questions.

The problem is whenever I decode the IP address it shows as "45\xE1\xBC".

First question is can you run the ruby filter on an array of base64 encoded IPs. Secondly, can I use nested notation in the ruby filter (e.g., "[ip][ip][$binary]")? The raw nested fields looks identical to the one I provided.

Can you provide a sample of the data so I can see what it looks like?

@aaron-nimocks

"ip":  { "ip": [ {"$binary": "dnv5cg==", "$type": "00" }, {"$binary": "t4Ob4A==", "$type": "00" }, {"$binary": "24pmWw==", "$type": "00" }]}

@aaron-nimocks I forgot the nested ip object.

I am not sure what those values are but when I put them into a base64 decode tool they don't represent an IP and that is what I was basing my solution off of.

@aaron-nimocks when I use CyberChef

dnv5cg== converts to 118 123 249 114 without a delimiter. Unfortunately Cyber Chef doesn't have the dot delimiter which I know I could probably add that using a gsub filter.

Interesting. When I just do a normal base64 decode on there it looks to give the same results as I did. You are adding a To Decimal function which does Converts the input data to an ordinal integer array.

I am not familiar with this enough to speak on it but I think the first step would be to figure out if Ruby can do that function.

@aaron-nimocks you're right. CyberChef allows you to force the input to decimal whereas, the ruby filter may not recognize it and converts the value to Hex instead. Is there a cheatsheet of some kind to see Ruby filter functions?

I also noticed that I was getting unpacking errors.

The answer is yes it can be done but it would take me awhile to figure out. I may have time later or another day. Throw this in a Ruby playgound and you can see the results.

base64_ip = "dnv5cg=="
decoded_ip = Base64.decode64(base64_ip)

decoded_ip.each_char { |c| 
  puts c.ord
}

First you do a base64 decode.
Then you loop through that result 1 character at a time.
Then you apply .ord which gets the value of the character.

Now you would just need to tie that all back into a single string with decimals in between.

@aaron-nimocks ok thanks for your help so far. I will try to build off of what you've given me, but if I can't figure it out I will wait on your solution.

Try this. It will only work with IPV4 and there could be a better programic solution to this.

Test Conf

input { generator { codec => json count => 1 lines => [ '{ "ip": "dnv5cg==" }' ] } }
filter {
  ruby {
      code => '
        decoded_ip = Base64.decode64(event.get("ip")).chars
        event.set("parsed_ip", decoded_ip[0].ord.to_s + "." + decoded_ip[1].ord.to_s + "." + decoded_ip[2].ord.to_s + "." + decoded_ip[3].ord.to_s)
      '
  }
}
output {
  stdout { codec => json }
}

Output

{
    "parsed_ip": "118.123.249.114",
    "ip": "dnv5cg=="
}

Also I know it doesn't solve your entire problem because your input data is in an array. This was just to show it can be solved in Ruby when it comes to the conversion.

@aaron-nimocks thanks! Does this work with nested field notation? Ex. [ip][ip][$binary]

You select the field here. So it will. Never tested a $ in a fieldname in Ruby though so that could be an issue.

(event.get("[ip][ip][$binary]")

@aaron-nimocks no worries. I will just rename the field before the ruby filter.

This worked for me! Now if I can loop through the array that would be Gold!

Here's how to loop through the array.

Test Conf

input { generator { codec => json count => 1 lines => [ '{ "ip":  { "ip": [ {"$binary": "dnv5cg==", "$type": "00" }, {"$binary": "t4Ob4A==", "$type": "00" }, {"$binary": "24pmWw==", "$type": "00" }]}}' ] } }
filter {
  ruby {
      code => '
        event.get("[ip][ip]").each_with_index do |value, index|
          decoded_ip = Base64.decode64(value["$binary"]).chars
          event.set("[ip][ip][#{index}][parsed_ip]", decoded_ip[0].ord.to_s + "." + decoded_ip[1].ord.to_s + "." + decoded_ip[2].ord.to_s + "." + decoded_ip[3].ord.to_s)    
        end
      '
  }
}
output {
  stdout { codec => json }
}

Output

{
    "ip": {
        "ip": [
            {
                "$type": "00",
                "parsed_ip": "118.123.249.114",
                "$binary": "dnv5cg=="
            },
            {
                "$type": "00",
                "parsed_ip": "183.131.155.224",
                "$binary": "t4Ob4A=="
            },
            {
                "$type": "00",
                "parsed_ip": "219.138.102.91",
                "$binary": "24pmWw=="
            }
        ]
    }
}