AntCTF x D^3CTF 2023|Misc Writeup
五一抽空玩了一下D3CTF,貌似又找回了当初的感觉🤤
d3readfile
读取Linux系统的敏感文件/proc/self/environ
根据hint的提示locate flag
检索发现Linux当中有/var/cache/locate/locatedb
数据库文件,里面存放了flag的文件目录,读取即可
d3gif
题目是一个GIF动图,名字是(x,y,bin).gif
GIF的考点无非两种:
- 时间
- 空间
提取图片每一帧的时间间隔发现都是0,那只能是空间的考点了,猜测是和之前国赛running_man类似的考点,需要提取每一帧图片不同位置的像素。
尝试之后发现不对劲,那么可以先把1089张图片所有像素点都提取出来,再逐个对比就好了。这里我把每一张图片(0,0)坐标的像素点提取出来做对比。
# -*- coding: UTF-8 -*-
from PIL import Image
def readImageValue():
"""
读取图片像素值
"""
for j in range(0, 1089):
print("./gif/flag-{}.png".format(j), end = ' ')
img = Image.open("./gif/flag-{}.png".format(j))
x = img.size[0]
y = img.size[1]
result = []
for i in range(x):
for j in range(y):
print('(' + str(i) + ',' + str(j) + ')', end = ' ')
data = (img.convert("RGB").getpixel((i, j))) # 每个像素点RGBA的值(r,g,b,alpha)
print(data)
break
break
readImageValue()
![](https://1cepeak.cn/post-images/1683281116393.png)
(r, g, b)
(1, 18, 0)
(0, 19, 0)
(29, 26, 0)
(3, 31, 0)
(9, 1, 0)
(1, 30, 0)
(18, 10, 0)
(10, 19, 0)
(12, 22, 1)
(29, 2, 1)
(20, 25, 1)
(9, 6, 0)
(18, 8, 1)
(18, 23, 0)
(15, 16, 0)
(32, 29, 1)
(24, 8, 0)
(10, 7, 0)
(6, 17, 0)
(17, 25, 0)
(18, 6, 1)
(20, 28, 1)
(19, 29, 0)
(7, 19, 1)
(31, 31, 0)
(24, 25, 1)
(4, 9, 1)
(25, 1, 0)
...
提取之后发现一个共性,每一帧(0,0)坐标的像素点RGB的Blue通道不是0就是1,一共1089帧,那么很快可以联想到01绘制二维码(33x33规格),根据附件名称(x,y,bin),很显然Red和Green通道对应的就是xy坐标。
所以思路就是将每一帧图片(0,0)坐标像素点提取出来,再根据Red、Green通道的值当作xy坐标来画图,Blue通道的值来决定颜色(黑或者白)。
# -*- coding: UTF-8 -*-
from PIL import Image
img = Image.open('(x,y,bin).gif')
rgb = []
for i in range(1089):
img.seek(i)
data = img.convert("RGB").getpixel((0, 0))
rgb.append(data)
flag = Image.new('RGB', (33, 33))
for j in rgb:
flag.putpixel((j[0], j[1]), (j[2] * 255, j[2] * 255, j[2] * 255))
flag.save('result.png')
最终结果生成的二维码如下:
扫描结果:
antd3ctf{G1F_0R_C0L0R_0R_QRC0D3_0R_WHAT???}
d3image
附件给的是一个Linux系统的mem内存文件,需要先使用Volatility对内存取证分析。Linux系统的取证命令可以使用-h获取,网上的大多数文章都是Windows内存文件的取证命令。
Linux系统的profile制作完成之后就可以对内存文件进行分析啦。
Options:
-h, --help list all available options and their default values.
Default values may be set in the configuration file
(/etc/volatilityrc)
--conf-file=/Users/yunoon/.volatilityrc
User based configuration file
-d, --debug Debug volatility
--plugins=PLUGINS Additional plugin directories to use (colon separated)
--info Print information about all registered objects
--cache-directory=/Users/yunoon/.cache/volatility
Directory where cache files are stored
--cache Use caching
--tz=TZ Sets the (Olson) timezone for displaying timestamps
using pytz (if installed) or tzset
-f FILENAME, --filename=FILENAME
Filename to use when opening an image
--profile=LinuxUbuntu16x64
Name of the profile to load (use --info to see a list
of supported profiles)
-l file:///Users/yunoon/Downloads/d3image-attachment/out.mem, --location=file:///Users/yunoon/Downloads/d3image-attachment/out.mem
A URN location from which to load an address space
-w, --write Enable write support
--dtb=DTB DTB Address
--shift=SHIFT Mac KASLR shift address
--output=text Output in this format (support is module specific, see
the Module Output Options below)
--output-file=OUTPUT_FILE
Write output in this file
-v, --verbose Verbose information
--physical_shift=PHYSICAL_SHIFT
Linux kernel physical shift address
--virtual_shift=VIRTUAL_SHIFT
Linux kernel virtual shift address
-g KDBG, --kdbg=KDBG Specify a KDBG virtual address (Note: for 64-bit
Windows 8 and above this is the address of
KdCopyDataBlock)
--force Force utilization of suspect profile
--cookie=COOKIE Specify the address of nt!ObHeaderCookie (valid for
Windows 10 only)
-k KPCR, --kpcr=KPCR Specify a specific KPCR address
Supported Plugin Commands:
imagecopy Copies a physical address space out as a raw DD image
limeinfo Dump Lime file format information
linux_apihooks Checks for userland apihooks
linux_arp Print the ARP table
linux_aslr_shift Automatically detect the Linux ASLR shift
linux_banner Prints the Linux banner information
linux_bash Recover bash history from bash process memory
linux_bash_env Recover a process' dynamic environment variables
linux_bash_hash Recover bash hash table from bash process memory
linux_check_afinfo Verifies the operation function pointers of network protocols
linux_check_creds Checks if any processes are sharing credential structures
linux_check_fop Check file operation structures for rootkit modifications
linux_check_idt Checks if the IDT has been altered
linux_check_inline_kernel Check for inline kernel hooks
linux_check_modules Compares module list to sysfs info, if available
linux_check_syscall Checks if the system call table has been altered
linux_check_tty Checks tty devices for hooks
linux_cpuinfo Prints info about each active processor
linux_dentry_cache Gather files from the dentry cache
linux_dmesg Gather dmesg buffer
linux_dump_map Writes selected memory mappings to disk
linux_dynamic_env Recover a process' dynamic environment variables
linux_elfs Find ELF binaries in process mappings
linux_enumerate_files Lists files referenced by the filesystem cache
linux_find_file Lists and recovers files from memory
linux_getcwd Lists current working directory of each process
linux_hidden_modules Carves memory to find hidden kernel modules
linux_ifconfig Gathers active interfaces
linux_info_regs It's like 'info registers' in GDB. It prints out all the
linux_iomem Provides output similar to /proc/iomem
linux_kernel_opened_files Lists files that are opened from within the kernel
linux_keyboard_notifiers Parses the keyboard notifier call chain
linux_ldrmodules Compares the output of proc maps with the list of libraries from libdl
linux_library_list Lists libraries loaded into a process
linux_librarydump Dumps shared libraries in process memory to disk
linux_list_raw List applications with promiscuous sockets
linux_lsmod Gather loaded kernel modules
linux_lsof Lists file descriptors and their path
linux_malfind Looks for suspicious process mappings
linux_memmap Dumps the memory map for linux tasks
linux_moddump Extract loaded kernel modules
linux_mount Gather mounted fs/devices
linux_mount_cache Gather mounted fs/devices from kmem_cache
linux_netfilter Lists Netfilter hooks
linux_netscan Carves for network connection structures
linux_netstat Lists open sockets
linux_pidhashtable Enumerates processes through the PID hash table
linux_pkt_queues Writes per-process packet queues out to disk
linux_plthook Scan ELF binaries' PLT for hooks to non-NEEDED images
linux_proc_maps Gathers process memory maps
linux_proc_maps_rb Gathers process maps for linux through the mappings red-black tree
linux_procdump Dumps a process's executable image to disk
linux_process_hollow Checks for signs of process hollowing
linux_psaux Gathers processes along with full command line and start time
linux_psenv Gathers processes along with their static environment variables
linux_pslist Gather active tasks by walking the task_struct->task list
linux_pslist_cache Gather tasks from the kmem_cache
linux_psscan Scan physical memory for processes
linux_pstree Shows the parent/child relationship between processes
linux_psxview Find hidden processes with various process listings
linux_recover_filesystem Recovers the entire cached file system from memory
linux_route_cache Recovers the routing cache from memory
linux_sk_buff_cache Recovers packets from the sk_buff kmem_cache
linux_slabinfo Mimics /proc/slabinfo on a running machine
linux_strings Match physical offsets to virtual addresses (may take a while, VERY verbose)
linux_threads Prints threads of processes
linux_tmpfs Recovers tmpfs filesystems from memory
linux_truecrypt_passphrase Recovers cached Truecrypt passphrases
linux_vma_cache Gather VMAs from the vm_area_struct cache
linux_volshell Shell in the memory image
linux_yarascan A shell in the Linux memory image
mbrparser Scans for and parses potential Master Boot Records (MBRs)
patcher Patches memory based on page scans
raw2dmp Converts a physical memory sample to a windbg crash dump
vmwareinfo Dump VMware VMSS/VMSN information
首先可以看看跑了哪些进程以及bash历史命令。
volatility -f out.mem --profile=LinuxUbuntu16x64 linux_bash
可以看到一条nohup proxychains firefox &
的命令。
ProxyChains是Linux和其他Unix下的代理工具。 它可以使任何程序通过代理上网, 允许TCP和DNS通过代理隧道, 支持HTTP、 SOCKS4和SOCKS5类型的代理服务器, 并且可配置多个代理。 ProxyChains通过一个用户定义的代理列表强制连接指定的应用程序, 直接断开接收方和发送方的连接。
我们可以先读取一下proxychains的配置文件,尝试获取账号密码等信息。
volatility -f out.mem --profile=LinuxUbuntu16x64 linux_enumerate_files | grep 'proxychains.conf'
Volatility Foundation Volatility Framework 2.6.1
0xffff9978bb96ebf0 147170 /etc/proxychains.conf
发现存在该配置文件,然后再使用linux_find_file
将它dump出来。
volatility -f out.mem --profile=LinuxUbuntu16x64 linux_find_file -i 0xffff9978bb96ebf0 -O proxychains.conf
打开之后发现存在配置好的账号密码:
[ProxyList]
# add proxy here ...
# meanwile
# defaults set to "tor"
socks5 192.168.31.136 51234 Gigantic_Splight Tearalaments_Kitkalos
之后我们在本地配置socks5代理连接题目靶机。
socks5 ip:port Gigantic_Splight Tearalaments_Kitkalos, ip/port即为提供的ip/port
连上后,proxifier配置路由规则,访问127.0.0.1:2333
发现需要token,在这里卡了很久,尝试多次后发现直接strings
该内存文件可以发现端倪。
strings out.mem | grep 127.0.0.1:2333
访问/magic.7z可以拿到该文件,打开后发现是一个流量包,看了一下流量包的报文都是查询报文,但是code只有0、3。
到目前为止,猜测可以看看dst ip和返回包的状态,然后有一个ip是可以访问拿到token的。折腾了一晚上发现没有什么用,倒是第一次见证了北京从黑夜到清晨的全貌。
第二天有了新的想法,把ip和返回包状态提取出来,ip从小到大排序,然后根据是否可达转为01。
tshark -r magic.pcap -Y "(icmp.code == 0 or icmp.code == 3) and icmp.type == 0" -T fields -e ip.dst -e icmp.code | awk '{if ($2 == "0") {print $1" 0"} else {print $1" 1"}}' | sort -k 1,1n > output.txt
最终由@ThTsOd
T神写了一版完整的提取脚本出来:
import struct
f = open("magic.pcap","rb")
f.read(24)
data = f.read()
D = bytearray(5038056)
data_len = len(data)
offset = 0
def procees1(num,status):
unreachable = status & 0x00030000
realstatus = (status & 0xFFFF0000) >> 16
if(D[num] == 0):
D[num] = 0x30
elif(D[num] == 0x30 and (not unreachable)):
D[num] = 0x31
elif(D[num] == 0x31):
D[num] = 0x41
print("??",num)
def procees4(num,status):
unreachable = status & 0x00030000
realstatus = (status & 0xFFFF0000) >> 16
if(realstatus == 0x0800 and D[num] == 0):
D[num] = 0x30
elif(realstatus == 0x0000 and D[num] == 0x30):
D[num] = 0x31
elif(realstatus == 0x0003 and D[num] == 0x30):
D[num] = 0x30
def procees2(num,status):
unreachable = status & 0x00030000
realstatus = (status & 0xFFFF0000) >> 16
if(realstatus == 0x0800):
D[num] = 0x30
if(realstatus == 0x0003):
D[num] = 0x31
if(realstatus == 0x0000):
D[num] = 0x30
def procees3(num,status):
unreachable = status & 0x00030000
realstatus = (status & 0xFFFF0000) >> 16
if(D[num] == 0):
D[num] = 0x31
if(D[num] == 0x31 and realstatus == 0x0800):
D[num] = 0x30
if(realstatus == 0x0003):
D[num] = 0x30
if(realstatus == 0x0000):
D[num] = 0x31
while(offset < data_len):
R = struct.unpack_from(">IIIIIIIIIII",data,offset)
offset += 44
dst = R[8]
status = R[9]
#0800 request
#0003 unreachable
#0000 reachable
num = dst & 0x00FFFFFF
procees4(num,status)
for i in range(len(D)):
if(D[i] == 0):
D[i] = 0x30
f = open("Q9","wb")
f.write(D)
f.close()
得到7z压缩包,解压后是3d模型文件:
然后透视可以看到里面有二维码:
用010editor加载,观察到文件最后的部分,y轴为40,去除这一部分:
修改总数:
去除后,发现脱掉了半个外壳,查看二维码:
3;A6eI`(J{z29|Gz":Dqt;~h*Bvc$7}c"dw'uBJth$Jg(+4+8x9eG7`>83$q5hF%I*)yrcb3+7$*~Dr"G|:K~C{_"Jv5=B9t9|>bwugCE~d&3fd{H;@hD?(DDz~$h#I%I`IB8zKyfHby3x'yfc56fH35|E8$+KGE@(u`7
使用Cyberchef
来解密:
再访问127.0.0.1:2333,根据js和之前的模型文件,猜测要外接手柄。将手柄插到电脑上,显示连接成功。
发现手柄能输入的只有:
L 🐻
LT 🍆
R 🍉
RT 🥔
Y 🥺
⬆️⬇️⬅️➡️🅰️🅱️❎
根据js的映射表输入token
输入之后手柄接收到了震动信号,这里选择抓流量来捕获震动信号,最终发现流量里发送了flag。
再次使用Cyberchef
来解密:
最终本题由@1cePeak、@ThTsOd、@Dr34m、@Yunoon
通力协作完成🎉
不得不说,我走过最长的路,就是出题人的套路。🤪