附件链接
https://adworld.xctf.org.cn/media/file/task/554e0986d6db4c19b...
第一步:执行
使用file查看这个文件:
./re: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=c8d273ed1363a1878f348d6c506048f2354849d0, not stripped
显示是32位的程序,动态链接
使用readelf检查:
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x8048550
Start of program headers: 52 (bytes into file)
Start of section headers: 4452 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 9
Size of section headers: 40 (bytes)
Number of section headers: 30
Section header string table index: 27
显示是EXEC类,我突然想到一个问题:这和DYN类型有什么区别呢?在stackoverflow上找到了一些文章:
https://stackoverflow.com/questions/61553723/whats-the-difference-between-statically-linked-and-not-a-dynamic-executable
在执行时,报错:required file not found
或许是解释器的问题?它需要linux-ld.so.2,我的电脑上没有,我的第一反应是:建一个软连接文件(后来证明这是个非常愚蠢的想法),但是执行时提示我library corrupt,可能是我没有32位的库吗?
我甚至有一个想法:或许这个文件不是32位的,是64位的呢?但是用hexedit之后,readelf时一些数据变得非常离谱,排除了这种可能
我使用ldd查看文件依赖时,却报错:not a dynamic linked file
啊?不是写的好好的吗?你告诉我这不是动态链接?
在stackoverflow上,有人说是缺少32位库
https://stackoverflow.com/questions/16807560/ldd-doesnt-work-on-dynamically-linked-binary
先是在pacman.conf中允许multilib,然后又下载lib32-glibc,本来以为安装好了,但仔细一看发现竟然报错了:
error: failed to commit transaction (conflicting files)
lib32-glibc: /usr/lib/ld-linux.so.2 exists in filesystem
Errors occurred, no packages were upgraded.
好吧,原来是我自己刚才建立的软链接搞的鬼!删除之后就装好了
ldd正常运行:
linux-gate.so.1 (0xecd00000)
libc.so.6 => /usr/lib32/libc.so.6 (0xeca8d000)
/lib/ld-linux.so.2 => /usr/lib/ld-linux.so.2 (0xecd02000)
程序也能执行了,只是...
Welcome to cyber malware control software.
Currently tracking 1041969716 bots worldwide
Fatal glibc error: malloc.c:2599 (sysmalloc): assertion failed: (old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)
Aborted (core dumped)
从“能执行”到“正确执行”
使用valgrind检测程序:
==2585== Memcheck, a memory error detector
==2585== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==2585== Using Valgrind-3.23.0 and LibVEX; rerun with -h for copyright info
==2585== Command: ./re.bk
==2585==
Welcome to cyber malware control software.
Currently tracking 1389691921 bots worldwide
==2585== Invalid write of size 4
==2585== at 0x40510D3: wcscpy (vg_replace_strmem.c:2211)
==2585== by 0x80486A5: decrypt (in /test/re.bk)
==2585== by 0x8048724: authenticate (in /test/re.bk)
==2585== by 0x80487D4: main (in /test/re.bk)
==2585== Address 0x42c745c is 36 bytes inside a block of size 39 alloc'd
==2585== at 0x4042623: malloc (vg_replace_malloc.c:446)
==2585== by 0x8048693: decrypt (in /test/re.bk)
==2585== by 0x8048724: authenticate (in /test/re.bk)
==2585== by 0x80487D4: main (in /test/re.bk)
==2585==
提示我们出错在wcscpy函数,但真正有问题的可能是前面的malloc部分
再粘贴一下ida反编译:
- main
int __cdecl main(int argc, const char **argv, const char **envp)
{
setlocale(6, &unk_8048BE4);
banner();
prompt_authentication();
authenticate();
return 0;
}
前面三个函数都是没用的,只看authenticate
anthenticate
int authenticate() { int v1[8192]; // [esp+1Ch] [ebp-800Ch] BYREF int v2; // [esp+801Ch] [ebp-Ch] v2 = decrypt(&unk_8048AA8, &unk_8048A90); //这两个参数应该是data和key if ( fgetws(v1, 0x2000, stdin) ) //输入,比较。所以我们的flag应该就是解密后的内容 { v1[wcslen(v1) - 1] = 0; if ( !wcscmp(v1, v2) ) wprintf(&unk_8048B44); else wprintf(&unk_8048BA4); } return free(v2); }
- decrypt
int __cdecl decrypt(int a1, int a2)
{
int v2; // eax
int v4; // [esp+1Ch] [ebp-1Ch]
int i; // [esp+20h] [ebp-18h]
int v6; // [esp+24h] [ebp-14h]
int v7; // [esp+28h] [ebp-10h]
int v8; // [esp+2Ch] [ebp-Ch]
v6 = wcslen(a1);
v7 = wcslen(a2);
v2 = wcslen(a1); //从这里我们大致能确定a1是data, a2是key,
v8 = malloc(v2 + 1); //给解密数据分配空间,额外多一位,或许是需要结束符?
wcscpy(v8, a1);
while ( v4 < v6 )
{
for ( i = 0; i < v7 && v4 < v6; ++i )
*(_DWORD *)(v8 + 4 * v4++) -= *(_DWORD *)(a2 + 4 * i); // 每个wchar都被当作一个int来处理
}
return v8;
}
我们可以看到分配空间的部分为 malloc( len(a1) + 1 ), 有没有可能是分配的空间不够,导致堆溢出呢?
先来看看wchar在内存中具体是怎么存储的:
.rodata:08048AA8 unk_8048AA8 db 3Ah ; : ; DATA XREF: authenticate+11↑o
.rodata:08048AA9 db 14h
.rodata:08048AAA db 0
.rodata:08048AAB db 0
.rodata:08048AAC db 36h ; 6
.rodata:08048AAD db 14h
.rodata:08048AAE db 0
.rodata:08048AAF db 0
.rodata:08048AB0 db 37h ; 7
.rodata:08048AB1 db 14h
.rodata:08048AB2 db 0
.rodata:08048AB3 db 0
.rodata:08048AB4 db 3Bh ; ;
.rodata:08048AB5 db 14h
.rodata:08048AB6 db 0
.rodata:08048AB7 db 0
.rodata:08048AB8 db 80h
.rodata:08048AB9 db 14h
.rodata:08048ABA db 0
.rodata:08048ABB db 0
.rodata:08048ABC db 7Ah ; z
.rodata:08048ABD db 14h
.rodata:08048ABE db 0
.rodata:08048ABF db 0
我们发现前两个字节是有数据的,紧跟两个空字节。也就是说,这个程序中实际上每个wchar是占用4个字符的。
会不会是应该malloc(len(a1)+4)而不是malloc(len(a1)+1)呢?这里用到工具hexedit,通过Ctrl-s可以进行连续字节的搜索。我们objdump后定位到相关的指令码,修改后执行,遗憾的是,仍然报错。
再来仔细地看看malloc(v2+1), v2=wcslen(a1), 这个wcslen返回的具体是什么值呢?通过查阅资料可知,返回的是wchar字符个数而不是真正需要的内存,所以分配的空间是远远不够的,+4也不够!如果只修改1字节数据的话,我们就把它修改成最大的7f试试
即将:
8048684: e8 97 fe ff ff call 8048520 <wcslen@plt>
8048689: 83 c0 01 add $0x1,%eax
804868c: 89 04 24 mov %eax,(%esp)
804868f: e8 4c fe ff ff call 80484e0 <malloc@plt>
修改为
8048684: e8 97 fe ff ff call 8048520 <wcslen@plt>
8048689: 83 c0 7f add $0x7f,%eax
804868c: 89 04 24 mov %eax,(%esp)
804868f: e8 4c fe ff ff call 80484e0 <malloc@plt>
幸运的是,这次我们成功了!执行不再报错,提示我们输入:
Welcome to cyber malware control software.
Currently tracking 218702664 bots worldwide
Please enter authentication details:
找到明文
但我们当然是不会傻傻地给他输入的,直接上gdb,打断点,找到返回的明文:
0x804cfe0: 57 '9' 0 '\000' 0 '\000' 0 '\000' 52 '4' 0 '\000' 0 '\000' 0 '\000'
0x804cfe8: 52 '4' 0 '\000' 0 '\000' 0 '\000' 55 '7' 0 '\000' 0 '\000' 0 '\000'
0x804cff0: 123 '{' 0 '\000' 0 '\000' 0 '\000' 121 'y' 0 '\000' 0 '\000' 0 '\000'
0x804cff8: 111 'o' 0 '\000' 0 '\000' 0 '\000' 117 'u' 0 '\000' 0 '\000' 0 '\000'
0x804d000: 95 '_' 0 '\000' 0 '\000' 0 '\000' 97 'a' 0 '\000' 0 '\000' 0 '\000'
0x804d008: 114 'r' 0 '\000' 0 '\000' 0 '\000' 101 'e' 0 '\000' 0 '\000' 0 '\000'
0x804d010: 95 '_' 0 '\000' 0 '\000' 0 '\000' 97 'a' 0 '\000' 0 '\000' 0 '\000'
0x804d018: 110 'n' 0 '\000' 0 '\000' 0 '\000' 95 '_' 0 '\000' 0 '\000' 0 '\000'
0x804d020: 105 'i' 0 '\000' 0 '\000' 0 '\000' 110 'n' 0 '\000' 0 '\000' 0 '\000'
0x804d028: 116 't' 0 '\000' 0 '\000' 0 '\000' 101 'e' 0 '\000' 0 '\000' 0 '\000'
0x804d030: 114 'r' 0 '\000' 0 '\000' 0 '\000' 110 'n' 0 '\000' 0 '\000' 0 '\000'
0x804d038: 97 'a' 0 '\000' 0 '\000' 0 '\000' 116 't' 0 '\000' 0 '\000' 0 '\000'
0x804d040: 105 'i' 0 '\000' 0 '\000' 0 '\000' 111 'o' 0 '\000' 0 '\000' 0 '\000'
0x804d048: 110 'n' 0 '\000' 0 '\000' 0 '\000' 97 'a' 0 '\000' 0 '\000' 0 '\000'
0x804d050: 108 'l' 0 '\000' 0 '\000' 0 '\000' 95 '_' 0 '\000' 0 '\000' 0 '\000'
0x804d058: 109 'm' 0 '\000' 0 '\000' 0 '\000' 121 'y' 0 '\000' 0 '\000' 0 '\000'
0x804d060: 115 's' 0 '\000' 0 '\000' 0 '\000' 116 't' 0 '\000' 0 '\000' 0 '\000'
0x804d068: 101 'e' 0 '\000' 0 '\000' 0 '\000' 114 'r' 0 '\000' 0 '\000' 0 '\000'
0x804d070: 121 'y' 0 '\000' 0 '\000' 0 '\000' 125 '}' 0 '\000' 0 '\000' 0 '\000'
这就是我们的flag!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。