· security-research  · 8 min read

Setting up a Bot Honeypot

Many bots are programmed to look for the same attact vectors. Quite frequently, they are used to scout for Wordpress exploitations.

See these access logs from a live webserver -

68.183.233.103 - "GET /wp-includes/Requests/about.php HTTP/1.0" 404 18 0.0010
68.183.233.103 - "GET /wp-includes/style-engine/about.php HTTP/1.0" 404 18 0.0012
68.183.233.103 - "GET /wp-includes/rest-api/about.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /wp-includes/SimplePie/about.php HTTP/1.0" 404 18 0.0009
68.183.233.103 - "GET /wp-content/banners/about.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /wp-content/about.php HTTP/1.0" 404 18 0.0008
68.183.233.103 - "GET /.well-known/about.php HTTP/1.0" 404 18 0.0010
68.183.233.103 - "GET /wp-includes/Text/about.php HTTP/1.0" 404 18 0.0008
68.183.233.103 - "GET /wp-includes/ID3/about.php HTTP/1.0" 404 18 0.0008
68.183.233.103 - "GET /img/about.php HTTP/1.0" 404 18 0.0009
68.183.233.103 - "GET /wp-content/languages/about.php HTTP/1.0" 404 18 0.0008
68.183.233.103 - "GET /wp-includes/customize/about.php HTTP/1.0" 404 18 0.0007
68.183.233.103 - "GET /wp-includes.bak/html-api/about.php HTTP/1.0" 404 18 0.0007
68.183.233.103 - "GET /wp-includes/widgets/about.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /wp-includes/IXR/about.php HTTP/1.0" 404 18 0.0008
68.183.233.103 - "GET /wp-admin/js/about.php HTTP/1.0" 404 18 0.0007
68.183.233.103 - "GET /.well-known/pki-validation/about.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /wp-includes/pomo/about.php HTTP/1.0" 404 18 0.0007
68.183.233.103 - "GET /wp-includes/block-patterns/about.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /wp-content/updraft/about.php HTTP/1.0" 404 18 0.0008
68.183.233.103 - "GET /wp-content/upgrade-temp-backup/about.php HTTP/1.0" 404 18 0.0008
68.183.233.103 - "GET /wp-content/themes/about.php HTTP/1.0" 404 18 0.0007
68.183.233.103 - "GET /wp-admin/includes/about.php HTTP/1.0" 404 18 0.0008
68.183.233.103 - "GET /images/about.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /wp-content/blogs.dir/about.php HTTP/1.0" 404 18 0.0009
68.183.233.103 - "GET /wp-includes/images/about.php HTTP/1.0" 404 18 0.0007
68.183.233.103 - "GET /wp-includes/about.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /cgi-bin/about.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /wp-content/gallery/about.php HTTP/1.0" 404 18 0.0007
68.183.233.103 - "GET /wp-includes/blocks/about.php HTTP/1.0" 404 18 0.0008
68.183.233.103 - "GET /wp-admin/css/about.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /wp-admin/images/about.php HTTP/1.0" 404 18 0.0008
68.183.233.103 - "GET /wp-admin/network/cloud.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /cloud.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /cgi-bin/cloud.php HTTP/1.0" 404 18 0.0008
68.183.233.103 - "GET /wp-content/updates.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /css/cloud.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /wp-admin/user/cloud.php HTTP/1.0" 404 18 0.0028
68.183.233.103 - "GET /img/cloud.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /wp-admin/css/colors/coffee/cloud.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /wp-admin/images/cloud.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /avaa.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /images/cloud.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /wp-admin/js/widgets/cloud.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /wp-includes/Requests/Text/admin.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /wp-admin/css/colors/cloud.php HTTP/1.0" 404 18 0.0009
68.183.233.103 - "GET /wp-admin/includes/cloud.php HTTP/1.0" 404 18 0.0011
68.183.233.103 - "GET /wp-admin/css/colors/blue/cloud.php HTTP/1.0" 404 18 0.0005
68.183.233.103 - "GET /wp-admin/cloud.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /updates.php HTTP/1.0" 404 18 0.0005
68.183.233.103 - "GET /libraries/legacy/updates.php HTTP/1.0" 404 18 0.0005
68.183.233.103 - "GET /libraries/phpmailer/updates.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /libraries/vendor/updates.php HTTP/1.0" 404 18 0.0005
68.183.233.103 - "GET /alfa-rex.php7 HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /alfanew.php HTTP/1.0" 404 18 0.0007
68.183.233.103 - "GET /wp-content/plugins/Cache/Cache.php HTTP/1.0" 404 18 0.0005
68.183.233.103 - "GET /wp-admin/js/widgets/about.php7 HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /wp-p.php7 HTTP/1.0" 404 18 0.0007
68.183.233.103 - "GET /wp-admin/repeater.php HTTP/1.0" 404 18 0.0008
68.183.233.103 - "GET /wp-includes/repeater.php HTTP/1.0" 404 18 0.0008
68.183.233.103 - "GET /wp-content/repeater.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /wsoyanz.php HTTP/1.0" 404 18 0.0008
68.183.233.103 - "GET /yanz.php HTTP/1.0" 404 18 0.0009
68.183.233.103 - "GET /wp-admin/js/about.php HTTP/1.0" 404 18 0.0011
68.183.233.103 - "GET /wp-content/plugins/seoo/wsoyanz.php HTTP/1.0" 404 18 0.0007
68.183.233.103 - "GET /wp-content/plugins/seoo/wsoyanz1.php HTTP/1.0" 404 18 0.0011
68.183.233.103 - "GET /cache-compat.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /ajax-actions.php HTTP/1.0" 404 18 0.0010
68.183.233.103 - "GET /wp-admin/ajax-actions.php HTTP/1.0" 404 18 0.0010
68.183.233.103 - "GET /wp-consar.php HTTP/1.0" 404 18 0.0008
68.183.233.103 - "GET /repeater.php HTTP/1.0" 404 18 0.0008
68.183.233.103 - "GET /admin-post.php HTTP/1.0" 404 18 0.0007
68.183.233.103 - "GET /wp-admin/maint/maint/ajax-actions.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /wp-admin/dropdown.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /wp-admin/css/index.php HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /dropdown.php HTTP/1.0" 404 18 0.0007
68.183.233.103 - "GET /about.php HTTP/1.0" 404 18 0.0013
68.183.233.103 - "GET /admin.php HTTP/1.0" 404 18 0.0008
68.183.233.103 - "GET /about.php7 HTTP/1.0" 404 18 0.0010
68.183.233.103 - "GET /alfanew.php7 HTTP/1.0" 404 18 0.0008
68.183.233.103 - "GET /adminfuns.php7 HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /ebs.php7 HTTP/1.0" 404 18 0.0006
68.183.233.103 - "GET /ws.php7 HTTP/1.0" 404 18 0.0007
68.183.233.103 - "GET /alfanew2.php7 HTTP/1.0" 404 18 0.0007
68.183.233.103 - "GET /alfa-rex2.php7 HTTP/1.0" 404 18 0.0011
68.183.233.103 - "GET /.well-known/acme-challenge/cloud.php HTTP/1.0" 404 18 0.0009
68.183.233.103 - "GET /wp-admin/images/index.php HTTP/1.0" 404 18 0.0008
194.38.23.16 - "GET /index.php?option=com_adsmanager&task=upload&tmpl=component HTTP/1.0" 404 18 0.0016
194.38.23.16 - "GET /sites/all/modules/plupload/plupload/examples/upload.php HTTP/1.0" 404 18 0.0006
194.38.23.16 - "GET /sites/all/libraries/plupload/examples/upload.php HTTP/1.0" 404 18 0.0007
152.42.184.117 - "GET //wp-includes/wlwmanifest.xml HTTP/1.0" 404 18 0.0012
152.42.184.117 - "POST //xmlrpc.php?rsd HTTP/1.0" 404 18 0.0010
152.42.184.117 - "GET //blog/wp-includes/wlwmanifest.xml HTTP/1.0" 404 18 0.0009
152.42.184.117 - "GET //web/wp-includes/wlwmanifest.xml HTTP/1.0" 404 18 0.0008
152.42.184.117 - "GET //wordpress/wp-includes/wlwmanifest.xml HTTP/1.0" 404 18 0.0009
152.42.184.117 - "GET //website/wp-includes/wlwmanifest.xml HTTP/1.0" 404 18 0.0008
152.42.184.117 - "GET //wp/wp-includes/wlwmanifest.xml HTTP/1.0" 404 18 0.0009
152.42.184.117 - "GET //news/wp-includes/wlwmanifest.xml HTTP/1.0" 404 18 0.0010
152.42.184.117 - "GET //2018/wp-includes/wlwmanifest.xml HTTP/1.0" 404 18 0.0010
152.42.184.117 - "GET //2019/wp-includes/wlwmanifest.xml HTTP/1.0" 404 18 0.0007
152.42.184.117 - "GET //shop/wp-includes/wlwmanifest.xml HTTP/1.0" 404 18 0.0008
152.42.184.117 - "GET //wp1/wp-includes/wlwmanifest.xml HTTP/1.0" 404 18 0.0009
152.42.184.117 - "GET //test/wp-includes/wlwmanifest.xml HTTP/1.0" 404 18 0.0011
152.42.184.117 - "GET //media/wp-includes/wlwmanifest.xml HTTP/1.0" 404 18 0.0014
152.42.184.117 - "GET //wp2/wp-includes/wlwmanifest.xml HTTP/1.0" 404 18 0.0012
152.42.184.117 - "GET //site/wp-includes/wlwmanifest.xml HTTP/1.0" 404 18 0.0011
152.42.184.117 - "GET //cms/wp-includes/wlwmanifest.xml HTTP/1.0" 404 18 0.0011
152.42.184.117 - "GET //sito/wp-includes/wlwmanifest.xml HTTP/1.0" 404 18 0.0009

You might notice a few things about these requests:

  1. They are looking for .php files
  2. They are probing for LFI exploits (.git/config, wp-includes, wp-content)
  3. They are not only using the HTTP GET verb, but also POST.
  4. All of these requests return HTTP 404.

I set up a very basic honeypot for my security research, to collect, observe, and analyze these bots.

How does it work?

Configure your webserver to “trap” the bots into thinking they got a successful response, by returning a 2xx HTTP code.

Example

Nginx example:

server {
  server_name example.com;
  listen 80;
  location / {
    # perusing normal site as usual.
  }

  location ~* .(php|git) {
    return 201 "<b>I pledge allegiance to the flag, of the United States of America. And to the republic, for which it stands, one nation, under God, indivisible, with liberty and justice for all.</b><br/><b>我向美利>坚合众国国旗宣誓效忠。对于它所代表的共和国来说,一个国家,在上帝统治下,不可分割,所有人享有自由和正义。</b><br /><b>Я клянусь в верности флагу Соединённых Штатов Америки. И республике, которую она представляет, единой нации под Богом, неделимой, со свободой и справедливостью для всех.</b>";
  }

  location ~* wp-.* {
    return 201 "<b>I pledge allegiance to the flag, of the United States of America. And to the republic, for which it stands, one nation, under God, indivisible, with liberty and justice for all.</b><br/><b>我向美利>坚合众国国旗宣誓效忠。对于它所代表的共和国来说,一个国家,在上帝统治下,不可分割,所有人享有自由和正义。</b><br /><b>Я клянусь в верности флагу Соединённых Штатов Америки. И республике, которую она представляет, единой нации под Богом, неделимой, со свободой и справедливостью для всех.</b>";
  }
}

Using nginx’s location directive, and matching any paths that I know are not going to resemble a real path, you return a faux HTTP 201 in an attempt to make the bot think that it struck gold, so that the requests now look like this:

68.183.233.103 - "GET /wp-includes/Requests/about.php HTTP/1.0" 201 18 0.0010
68.183.233.103 - "GET /wp-includes/style-engine/about.php HTTP/1.0" 201 18 0.0012
68.183.233.103 - "POST /xmlrpc.php HTTP/1.0" 201 18 0.0006
...

Annoying the bot’s owners

To understand why something like this would annoy the bot’s owners, you need to put yourself in the shoes of the people that programmed these bots.

Imagine you have a dictionary of common attack vectors for common software. You query various registrars and scrape the web to aggregate all kinds of websites.

Now, put yourself in the bot’s shoes. Imagine this script:

request = get("https://example.com/wp-admin/login.php?u=admin&p='%20OR%201=1")

if request.code != 404
  # our exploit worked!

  success!
else
  no_dice
end

What happens when the exploit “works”? Maybe an email is sent to the bot’s owner. Maybe the successful response is saved on a file system somewhere, ready for someone to take a look at it.

As the owner, you feel excited. You feel nefariously excited that a bot has found an exploit on some WordPress site. You start investigating, and instead of finding gold, you find the oath of loyalty to the United States’ flag, the Pledge of Allegiance in English, Chinese, and Russian.

This would be particularly annoying, especially to adversarial international actors that make it their job to subvert American ideology.

As an added benefit for a security researcher, you have the opportunity to observe and analyze subsequent requests from the bot’s owners that do not take proper security, and access precautions.

Back to Blog