上班的时候被安排了个代打比赛的活
内容主要是日志跟流量分析
其中一题题目文件如下
ha.pcapng
其中一个小问题是失陷主机上有多少个用户
常理来说应该是要看/etc/passwd
但是这个流量包并没有相关信息
流量包最后把ELF文件en下载, 也给了en的运行结果
感觉应该该是要对这个文件进行逆向了
下载, 拖IDA
看着像UPX
1 2 3 4 5 6 7 8 9 10 11 LOAD:0000000000005F3C dd 1E68h LOAD:0000000000005F40 db 0 LOAD:0000000000005F41 db 5Ah, 0E8h, 1Eh, 3 dup(0), 50h LOAD:0000000000005F48 aRotExecProtWri db 'ROT_EXEC|PROT_WRITE failed.',0Ah,0 LOAD:0000000000005F65 db 5Eh, 6Ah, 2 LOAD:0000000000005F68 dq 7F6A050F58016A5Fh, 0A050F583C6A5Fh LOAD:0000000000005F78 aInfoThisFileIs db '$Info: This file is packed with the UPX executable packer http://' LOAD:0000000000005F78 db 'upx.sf.net $',0Ah,0 LOAD:0000000000005FC7 aIdUpx424Copyri db '$Id: UPX 4.24 Copyright (C) 1996-2024 the UPX Team. All Rights Re' LOAD:0000000000005FC7 db 'served. $',0Ah,0 LOAD:0000000000006013 align 4
要在github上下一个新版本的upx
https://github.com/upx/upx/releases/tag/v4.2.4
脱壳之后看主函数
1 2 3 4 5 6 7 8 9 10 11 12 13 int __cdecl main(int argc, const char **argv, const char **envp) { int v4[6]; // [rsp+10h] [rbp-20h] BYREF unsigned __int64 v5; // [rsp+28h] [rbp-8h] v5 = __readfsqword(0x28u); v4[0] = 0x12345678; v4[1] = 0x9ABCDEF0; v4[2] = 0xFEDCBA98; v4[3] = 0x76543210; encrypt_file("/etc/passwd", "message.txt.enc", v4); return 0; }
跟进函数encrypt_file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 void __fastcall encrypt_file(const char *a1, const char *a2, __int64 a3) { FILE *stream; // [rsp+28h] [rbp-28h] __int64 n; // [rsp+30h] [rbp-20h] unsigned __int64 nmemb; // [rsp+38h] [rbp-18h] void *ptr; // [rsp+40h] [rbp-10h] FILE *s; // [rsp+48h] [rbp-8h] stream = fopen(a1, "rb"); if ( !stream ) { perror("Failed to open input file"); exit(1); } fseek(stream, 0LL, 2); n = ftell(stream); fseek(stream, 0LL, 0); nmemb = (unsigned __int64)(n + 3) >> 2; ptr = calloc(nmemb, 4uLL); fread(ptr, 1uLL, n, stream); fclose(stream); xxtea_encrypt(ptr, (unsigned int)nmemb, a3); s = fopen(a2, "wb"); if ( !s ) { perror("Failed to open output file"); free(ptr); exit(1); } fwrite(ptr, 4uLL, nmemb, s); fclose(s); free(ptr); }
稍微理一下流程
主函数里的v4是密钥
读取/etc/passwd
作为明文进行加密
加密轮数参数为输入长度除以4
加密结果为输出的message.txt.enc
在流量包里面有这个密文
所以是要进行XXTEA解密
Solution 0x00
这里给出一个取巧的方法
由于这个程序给出了函数xxtea_decrypt
而且函数参数与函数xxtea_encrypt
一致
都是输入
, 加密/解密轮数参数
, 密钥
虽然说程序中没有调用这个函数, 但是可以在动态调试中把call xxtea_encrypt
改为call xxtea_decrypt
输入需要读取/etc/passwd
, 那就把密文放在/etc/passwd
, 这样就解决了输入密文的问题
Call指令的字节码为E8 75 FC FF FF
Call参数 = 目标地址 - 当前地址 - 指令长度
0xFFFFFCF5 = (0x12F5 - 0x167B - 0x5)& 0xFFFFFFFF
这里的Call的函数是xxtea_encrypt
需要改为xxtea_decrypt
, 地址为0x1456
那么Call的参数则为
(0x1456 - 0x167B - 0x5)& 0xFFFFFFFF = 0xFFFFFDD6
则修改过后的Call指令应当为E8 D6 FD FF FF
运行即可, 得到的message.txt.enc
即为解密之后的/etc/passwd
文件
内容如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin systemd-timesync:x:100:103:systemd Time Synchronization,,,:/run/systemd:/bin/false systemd-network:x:101:104:systemd Network Management,,,:/run/systemd/netif:/bin/false systemd-resolve:x:102:105:systemd Resolver,,,:/run/systemd/resolve:/bin/false systemd-bus-proxy:x:103:106:systemd Bus Proxy,,,:/run/systemd:/bin/false hack:x:1000:1000::/home/hack:/bin/sh admin:x:1001:1001::/home/admin:/bin/sh admin1:x:1002:1002::/home/admin1:/bin/sh admin2:x:1003:1003::/home/admin2:/bin/sh admin3:x:1004:1004::/home/admin3:/bin/sh
应该是六个用户吧
Solution 0x01
当然,也有不取巧的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 import binascii def read_int(a, i): tmp_list = a[4*i:4*i+4] num = 0 for I in range(4): num = num + (tmp_list[I] << I * 8) return num def write_int(a, i, num): tmp_list = [] hex_num = hex(num)[2:] if len(hex_num) < 8: hex_num = (8 - len(hex_num)) * "0" + hex_num for I in range(4): a[4 * i + I] = int(hex_num[6 - 2*I:8 - 2*I], 16) cipher_bytes = binascii.unhexlify(b"868F9499451EC1600F994E17A3656B5E688ADBFA251AEA32331935FBD377C95492D8A870D9C29281BA32032400A7D04E7E7988AA11973B8AD996BB081F2F560B29F1CE80F35E86C74E14D1D1EEE1D2AEFD2513DF6E47162F1CC259FC23A73EE5A5F82C453F7933B997A220F1214EF8B241FC8586C95B87B37A17A12B35EFD45FB0BAE2B3850BA3AD25213BB2C75E82925C4DBA50E60E95118C8F61BA5E34043BA54688423E60857F159AF93C7E5E5D059577A5F758DC791533FB3714B8186DC9E27F78A082F3551BBDE57A72EDBA9051AB91C1C84F9BF95EF3AED2A17DD5DCE0D194A5F6D8F90D2B55C743D6889346AA38F08833943B8BA3F63853D45CA4D78C8F53F31EEFE9C2E24F633897EFCDD8CF0A9A7233FF8D824D5B0B52CD5D821BF420578EC651613AEC0B461A9079803D02910A43F898919C9288C8D941E9D0B9990789A4CE7A2A7FD4DC0F64B819616979BC56BEE51E92D0891792B155F4132265D475E02A372529211CB4DDADB39E22D4E5123458143405C32011A191915A5004E2142C9225E1DB8AAEEF040ECA3BB8E677DC058EE48B77A3D97E73F7494A8CEE9E41D98E690669AF396CD47AF26FFC0CEB704E9182AB4D62A8F474B233EFB317CC8B0EC1132E4394CEBF4EEE357493CEADE4F43CBEB0C2C664B62278E7B28CCEFA633BA5C0457830D3654BB3A9CA25BA33021E0611F9076807328DD3C8AC65F251D9B3AFCF4D47D43D1B0DCF1B5F93BC1DF69402E152EA2F12AC3F056F3EB85BF59D6647E0EDE6653550B3DD559974B1696641795BC0A40DA170EAE78054BC9833A9EA8A48CD27ABBC420F30A80CFBD181275D8434658F650B4D55AF817C0E94FAFE5329F5BF6CF66001689661EEA4318A4390F6D6BFFE52E347C4147D30C67B97FF6C5FE4755A8BC8AE4487DF86E9A282C8A90DBBB91923B4BB1112BBDC1DDE6D8A501B29390EFE5D9C01A130AD046891BBC21679B0EF7DFB83A89F4A944E8C4E87971FBF8902F6E0B4B6CDA714B472EE6C39E19F60A511EAC4AFA1670A936B20DF32A9F83754ADA308565FEC7C2E4F14CD361BE29B5E0C9C3EF81B32F347269AD7270BBFE1866E3DD5987BF5FF6193BF6F6693764AE8A2BA031715207F4F47384101BC8EE51CAAC05DF536C046D268905FF1367185091DCBD94CB8469DA91AF6354471289FE2148F8E50CFA37AE8AEA8517D2832D293DBF74332B187EC74087A8B77E167099065784B412A02ED05BD7C668AFE708DFB83CC48910E343D287D748721E59F7DFBE3F32FF69D624A2B5635544AF9C751B7C3024582F0BB7ADACEAEE07A44F7348F1C5989C75825FFF3F5EA220789D7EAC9F28526557EE333F412A9931E9FF4C073D2ED2C9EC302CF3FBF4A75456AB1C5DBDE2A0BBE61C05CB5F55BC940850151A9D530514B788CFA07D2CB6DBF6BF4FB913B380EA599DA9A18203AA8050AB7AC0149126A323D17D53716DF344DECB2CEDC3FEB9446459134D25B817727AAAC34498B448BDD44C2C8EADAC5B79200AA586C1186D51DCF8020A8D73D7D090857F2D97175D8262A39B4D78571972746858721B99AB833E60387EBEE3C9936ED8BC82C19F71D042F04A97CE4A7052936FC1287D98928DC1CB4BD6CAF1D605E96B3150D2AC14D5C073E2B2E56F08B27FDC4D257B137D8D220DDF10422DA261705828A2DE1FEB230F104030BB92DAE65C00CDD47176CC4A41FC47F0F383447EC3FA40680AE36698CF85D0D9D55059EEE4E769AD7FAA59D70DA828872E754F953C452911501EA69B70961DD22F54E49ED6445E04CB1EF8AAF10D085F242AB4507455866E4B56C20C7977662AAEE092A338BEB16D53FD9FA20658FE8588DAE80EB5F3FE7445536BF2D338264DBE2DC458D5C7565670147224CAC4E9CC81D681C301491615FC96729FE70CFB039F7B2EF28766A5859F05D97A5CF2CA49B041C251C68B3A96FA73D46498C2673737E13446FD8") a2 = len(cipher_bytes) // 4 cipher = [] for I in cipher_bytes: cipher.append(I) key_bytes = binascii.unhexlify(b"78563412F0DEBC9A98BADCFE10325476") key = [] for I in key_bytes: key.append(I) plain = "" y = read_int(cipher, 0) sum_num = (0x9E3779B9 * ((52 // a2) + 6)) & 0xffffffff v15 = False while not v15: e = (sum_num >> 2) & 3 if (a2 != 1): i = a2 - 1 while i != 0: z_i = read_int(cipher, i) z = read_int(cipher, i - 1) y = (z_i - (((sum_num ^ y) + (read_int(key, ((i & 3) ^ e)) ^ z)) ^ (((z << 4) ^ (y >> 3)) + ((y << 2) ^ (z >> 5))))) & 0xffffffff write_int(cipher, i, y) i = i - 1 z = read_int(cipher, a2 - 1) cipher_0 = read_int(cipher, 0) y = (cipher_0 - (((sum_num ^ y) + (read_int(key, (0 & 3) ^ e) ^ z)) ^ (((z << 4) ^ (y >> 3)) + ((y << 2) ^ (z >> 5))))) & 0xffffffff write_int(cipher, 0, y) v15 = (sum_num == 0x9E3779B9) sum_num = (sum_num + 0x61C88647) & 0xffffffff plain = "" for _ in cipher: plain = plain + chr(_) print(plain)
Update
看到XXTEA在github上有repo
https://github.com/xxtea/xxtea-python
下载下来后解密却解不出来
大概看了一下xxtea.c
函数static uint32_t * xxtea_to_uint_array(const uint8_t * data, size_t len, int inc_len, size_t * out_len)
会进行pad
pad为输入内容的长度, 四字节小端存储添加在明文末尾
而函数static uint8_t * xxtea_to_ubyte_array(const uint32_t * data, size_t len, int inc_len, size_t * out_len)
会unpad
unpad失败则会返回空字符串
程序en
加密/etc/passwd
的时候没有进行pad, 所以用这个repo解密自然在unpad时会失败
而这个repo里pad或unpad与否由参数inc_len
决定
所以解密时不进行unpad即可
删除python环境路径下的pyc文件缓存/Lib/site-packages/xxtea/__pycache__
(如果有的话)
python环境下的/Lib/site-packages/xxtea.c
函数xxtea_ubyte_decrypt
的定义中对函数xxtea_to_ubyte_array
的调用, 第三个参数改为0即可跳过unpad
EOF