From One Git Server to Full Domain Admin
A medical-device manufacturer's internal pentest via an MSSP partner. One unauthenticated git server chained into full Active Directory forest compromise.
The Challenge
A global medical-device manufacturer engaged their managed security services provider for internal penetration testing. The MSSP brought us in as the technical delivery partner. The brief was deliberately spare: an authorized foothold on the internal network, no credentials, no list of targets, no map, and two days to work. That constraint is the point. An attacker who phishes one employee or plugs into one conference-room jack does not receive a target list either. They start with a network connection and curiosity, and they work outward.
This was not an under-defended company. They ran endpoint detection and response (EDR) across their systems and paid for managed detection and response (MDR) on top of it. Environments this size get tested every year, and this one was no exception. For a manufacturer whose production sits in a regulated, safety-critical environment, the real question was not “can the network be breached.” Most networks can. It was: starting from a single internal foothold, how far does a real adversary actually get, and does the corporate side reach the plant floor?
The MSSP needed a partner who would test the way an adversary would, chaining findings into a single attack path rather than handing back a list of unrelated issues. The MSSP’s name was the one the client saw. Our job was to make the testing under that name as rigorous as anything a larger firm would deliver, while the MSSP kept the relationship they had built.
The Approach
We followed the PTES methodology we use on every pentest: intelligence gathering, threat modeling, vulnerability analysis, exploitation, and post-exploitation. The first move was not an exploit. It was a git clone against a server nobody had put on a list.
We mapped the headquarters server subnet and looked at what answered. Most of it was ordinary: file servers, SQL, Exchange, the usual estate. One host stood out because it was speaking a developer’s language. It was a self-hosted GitLab instance, an internal system for storing source code, and two things about it mattered. Several of its repositories were set to public visibility. On a self-hosted git server, public does not mean published to the internet; it means readable by anyone who can reach the server, with no login. The server also allowed open self-registration, so any user on the network could mint an account and browse further. Between the two, the repositories were an open book.
The repositories that mattered were the IT automation ones: the code the infrastructure team uses to build servers, patch them, and manage the virtualization platform. Infrastructure-as-code has to authenticate to everything it manages, and the fastest way to make that work under deadline pressure is to write the password into the code. These repositories held plaintext secrets in the open: a virtualization-platform administrator password, privilege-escalation passwords for the automation tooling, a configuration granting one account passwordless root, and assorted database and application credentials. Some sat in the current files. Others had been removed in a later commit but were still recoverable from history, because deleting a secret from a repository does not delete it from the repository’s memory.
One repository did the right thing on paper and held an encrypted vault. The problem was that the password protecting the vault had been reused elsewhere in the same exposed material. We decrypted it and found a live administrator credential for the virtualization platform. A lock is only as good as the key you leave next to it.
That single account turned out to be the whole game, because of what it could touch. It held administrator rights in the virtualization platform that runs the company’s virtual machines, and it held administrative rights on the domain controllers (DCs) that run Active Directory (AD) itself, through the domain’s built-in Administrators group. Either right, on its own, is a path to owning the domain. This account held both. Administrative rights on a domain controller mean full control of the directory database: an attacker can grant themselves the replication rights that make a DCSync possible, or skip DCSync entirely and read the database off the host directly. DCSync asks a domain controller to replicate directory data, including every account’s password hash. It is a legitimate replication feature, which is exactly why it is dangerous in the wrong hands (MITRE tracks it as T1003.006). Administrator on the virtualization platform is a second door to the same room: an attacker can clone or snapshot any virtual machine, including a domain controller, and read its directory database offline, away from any monitoring on the live host.
One path, seven links
We ran the DCSync and recovered every password hash in the domain, including the krbtgt key that signs every Kerberos authentication ticket. At that point we held Domain Admin-equivalent control of the production forest. Then we measured how far it reached, because a finding’s severity is really a question of blast radius.
The Outcomes
It reached a long way. A single Active Directory forest is one security boundary, not several, so a second domain in the same forest fell without needing any credential native to it. Domain Admin rights gave hands-on control of employee workstations in another country. The remote-access tier, the jump servers meant to be a controlled gateway, answered to the same rights. And the manufacturing side was reachable: the plant floor ran in a segmented operational-technology (OT) zone with its own network and its own jump hosts, but those OT jump hosts were members of the same forest, so our Domain Admin rights already counted as OT administrator rights by design, and they answered network requests from the corporate side. We proved the OT bastions were reachable from the ordinary IT network and that our credentials were valid against them, then stopped short of touching anything on the plant floor. That reachability is the mirror image of a water utility whose segmentation held under a full IT-domain compromise: the boundary that should have separated corporate IT from the plant did not hold here. Why we stopped, and why stopping is the right call near production medical systems, is its own story.
How far one credential reached
One exposed credential
Found in a readable git repository, no login required
Virtualization platform
Domain controllers
Production forest
Second domain in the same forest
Workstations in another country
OT plant floor
we stopped here
None of the security stack stopped the chain. Whether any of it noticed the chain is a fair and separate question, and an honest one: the absence of anything being blocked is not the same as the absence of anything being logged. We flagged it for exactly that conversation with the client’s security operations team. What beat the tools here was not a clever exploit. It was a chain of things that each looked ordinary. A public repository looks ordinary. An automation account looks ordinary. A DCSync from an administrator that is supposed to have those rights looks ordinary. Line them up in the right order and they add up to the keys to the company. A vulnerability scanner grades findings one host at a time and does not see chains. Following them is the job, and it is the kind of work automated scanning cannot replicate.
We documented the full path as a single kill chain, not a pile of findings. Every link was written up with the misconfiguration that created it, the technique we used, the control that should have caught it, and the specific fix that breaks that link, all mapped to the CIS Top 18 so the MSSP and the client could track remediation against a framework they already used. The remediation priorities were unglamorous, and each one on its own would have broken the chain:
- Keep secrets out of source control, and treat any that got in as burned. Set repositories private by default, turn off open self-registration, and scan for secrets before every commit. When a secret has been exposed, rotate it rather than delete it, because the old value lives on in history. Deleting a secret from git does not remove it.
- Separate the privilege tiers. An account that administers domain controllers should never also administer the virtualization platform, and should never live in automation. One account, one job. The privileged access hiding where nobody counts it is the wider version of this problem.
- Assume the internal network is hostile. Public breach data, including the annual Verizon Data Breach Investigations Report, keeps putting stolen and misused credentials among the most common ways in. A foothold on the inside is not a remote possibility. Design as if someone already has one.
We did not disappear after the report. We worked the priority order with the MSSP and the client’s team, answered the technical questions on specific fixes, and stayed available for the conversations where IT leadership had to explain to non-technical decision-makers why each item mattered. The path to Domain Admin did not require a zero-day or a nation-state. It required a git server that was readable without a login and one account wearing two crowns. The attackers who look for exactly that are not rare, and they are patient.
This is the kind of work automated scanning cannot do. A scanner would have flagged each misconfiguration as modest. Only a human with access, time, and motivation chains modest findings into a path to the domain controller. If you run an MSP or MSSP and want to deliver pentests like this for your own clients, our partner program supports both referral and resell models; the partner program post for MSPs, MSSPs, and VARs walks through how it works.