Linux内存取证
Linux内存取证一般有两种方法:
- 制作profile文件并配合vol2进行取证
- 制作符号表并配合vol3进行取证
下面以“第七届强网杯-强网先锋-找到PNG了吗”为例,通过制作profile文件进行取证
识别镜像文件镜像版本和内核版本
查看附件,镜像版本为:ubuntu 20.04,内核版本为:Linux version 5.4.0-100-generic
strings png.mem | grep "Linux version"
安装ubuntu20.04的虚拟机,并使用以下命令查看已经安装的内核镜像
dpkg --get-selections | grep linux-image
在虚拟机中使用以下命令安装对应内核版本:
sudo apt install linux-image-x.x.x-xx-lowlatency linux-headers-x.x.x-xx-lowlatency
# apt install linux-image-5.4.0-100-generic linux-headers-5.4.0-100-generic
安装完成后再查看发现对应内核已安装完成(ps:5.4.0-100-lowlatency那个安装错了,可以忽略)
切换内核版本
修改grub文件,使得每次开机可以自己选择要启动的内核版本
vim /etc/default/grub
GRUB_TIMEOUT_STYLE=hidden
GRUB_TIMEOUT=0
↓↓↓
#GRUB_TIMEOUT_STYLE=hidden
GRUB_TIMEOUT=10
修改完成后更新grub文件并重启,可以在Advanced options中选择下载好的内核版本
sudo update-grub
sudo reboot
进入后发现内核已经切换完成
simho@simho-virtual-machine:~/Desktop$ uname -a
Linux simho-virtual-machine 5.4.0-100-lowlatency #113-Ubuntu SMP PREEMPT Thu Feb 3 19:24:13 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
制作profiile文件
首先安装build-essential
和dwarfdump
,在/boot
目录下找到对应内核版本的System.map-xx
文件
然后传输 dwarf 内核调试文件的编译文件,并进行编译,编译后会在当前目录生成module.dwarf
文件
接着将以上两个文件打包成zip,放到volatility/plugins/overlays/linux
下
apt install build-essential dwarfdump
cd volatility/tools/linux
make
zip ./Ubuntu_5_4_0-100-generic_profile.zip ./module.dwarf /boot/System.map-`uname -r`
利用vol2进行取证
查看 profile 文件是否已经能够识别,识别到后就可以使用 profile 进行内存镜像的解析
python2 vol.py --info | grep Profile
查看banner信息
python2 vol.py -f png.mem --profile=LinuxUbuntu_5_4_0-100-generic_profilex64 linux_banner
PS:这里根据巨魔的博客,解析过程中可能会出现报错,需要对相应文件进行patch,不过我这里能直接正常解析
查看文件
python2 vol.py -f png.mem --profile=LinuxUbuntu_5_4_0-100-generic_profilex64 linux_enumerate_files | grep "Desktop"
提取文件
python2 vol.py -f png.mem --profile=LinuxUbuntu_5_4_0-100-generic_profilex64 linux_find_file -i 0xffff9ce28fe300e8 -O ./have_your_fun.jocker
发现文件为空,直接winhex全局搜索have_your_fun.jocker
文件,得到完整代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define SERVER_IP "192.168.6.1"
#define SERVER_PORT 110
unsigned char buff[20000];
void swap(char* a, char* b) {
char temp = *a;
*a = *b;
*b = temp;
}
void rc4_encrypt_decrypt(unsigned char* key, unsigned char* data, int data_length) {
int i, j = 0, t;
int s[256];
int key_length = strlen((const char*)key);
for (i = 0; i < 256; i++) {
s[i] = i;
}
for (i = 0; i < 256; i++) {
j = (j + s[i] + key[i % key_length]) % 256;
t = s[i];
s[i] = s[j];
s[j] = t;
}
i = j = 0;
for (int k = 0; k < data_length; k++) {
i = (i + 1) % 256;
j = (j + s[i]) % 256;
t = s[i];
s[i] = s[j];
s[j] = t;
data[k] ^= s[(s[i] + s[j]) % 256];
}
}
int main()
{
int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket == -1) {
printf("socket failed!\n");
return 1;
}
struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(SERVER_PORT);
serverAddr.sin_addr.s_addr = inet_addr(SERVER_IP);
connect(clientSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
int result = recv(clientSocket, buff, sizeof(buff), 0);
int a=0;
char q[10];
unsigned char key[]="do_not_care";
unsigned char key2[] = "where_is_the_key";
FILE* file = fopen("have_your_fun.jocker", "wb");
if (file == NULL) {
printf("open file failed!\n");
return 1;
}
unsigned char *str;
str = (char *) malloc(20000);
memcpy(str, buff, 20000);
rc4_encrypt_decrypt(key2, str, 20000);
printf("please give me the key of fun:");
scanf("%s",q);
rc4_encrypt_decrypt(key, str, 20000);
fwrite(buff, 1, 20000, file);
printf("maybe you go wrong");
fclose(file);
close(clientSocket);
return 0;
}
主要功能是将 png 进行两次RC4加密,得到的文件就是 have_your_fun.jocker,将 png 文件头进行RC4加密得到e5afbeba
接着全局搜索,将含该头的字符串复制,RC4解密得到flag
flag{It's_So_Hard_To_Find_A_Picture}
参考博客: