...
tid = request->get_form_field( 'tid' ).
CALL TRANSACTION tid USING bdcdata MODE 'N'
MESSAGES INTO messtab.
...
APPHOME
y, a continuación, carga una biblioteca nativa en función de una ruta de acceso relativa desde el directorio especificado.
...
string lib = ConfigurationManager.AppSettings["APPHOME"];
Environment.ExitCode = AppDomain.CurrentDomain.ExecuteAssembly(lib);
...
APPHOME
de la aplicación, con el objetivo de que apunte a una ruta diferente que contenga una versión malintencionada de LIBNAME
. Como el programa no valida el valor leído en el entorno, si el atacante puede controlar el valor de la propiedad del sistema APPHOME
, puede engañar a la aplicación para que ejecute código malintencionado y asumir el control del sistema.
...
RegQueryValueEx(hkey, "APPHOME",
0, 0, (BYTE*)home, &size);
char* lib=(char*)malloc(strlen(home)+strlen(INITLIB));
if (lib) {
strcpy(lib,home);
strcat(lib,INITCMD);
LoadLibrary(lib);
}
...
INITLIB
. Como el programa no valida el valor leído del entorno, si un usuario malintencionado puede controlar el valor de APPHOME
, este puede engañar a la aplicación para que ejecute código malicioso.liberty.dll
, que se supone que se encuentra en un directorio del sistema estándar.
LoadLibrary("liberty.dll");
liberty.dll
. Si un usuario malintencionado traslada en el orden de búsqueda una biblioteca maliciosa con el nombre liberty.dll
a una posición superior que el archivo previsto y consigue ejecutar el programa en su entorno en lugar de en el entorno del servidor web, la aplicación cargará la biblioteca maliciosa en lugar de la de confianza. Como este tipo de aplicación se ejecuta con privilegios elevados, el contenido del archivo liberty.dll
del usuario malintencionado se ejecutará ahora también con este nivel de privilegios, lo que podría darle el control completo del sistema.LoadLibrary()
cuando no se especifica una ruta absoluta. Si se realiza una búsqueda en el directorio actual antes que en los directorios del sistema, como era el caso hasta las versiones más recientes de Windows, este tipo de ataque pasa a ser trivial si el atacante puede ejecutar de forma local el programa. El orden de búsqueda depende de la versión del sistema operativo y, en los sistemas operativos más recientes, se controla mediante el valor de esta clave del Registro:
HKLM\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode
LoadLibrary()
presenta el siguiente comportamiento:SafeDllSearchMode
es 1, se utiliza el siguiente orden de búsqueda:PATH
.SafeDllSearchMode
es 0, se utiliza el siguiente orden de búsqueda:PATH
.
...
ACCEPT PROGNAME.
EXEC CICS
LINK PROGRAM(PROGNAME)
COMMAREA(COMA)
LENGTH(LENA)
DATALENGTH(LENI)
SYSID('CONX')
END-EXEC.
...
APPHOME
para determinar el directorio en el que se ha instalado y, a continuación, carga una biblioteca nativa en función de una ruta relativa desde el directorio especificado.
...
String home = System.getProperty("APPHOME");
String lib = home + LIBNAME;
java.lang.Runtime.getRuntime().load(lib);
...
APPHOME
para que señale a una ruta diferente que contiene una versión maliciosa de LIBNAME
. Como el programa no valida el valor leído desde el entorno, si el usuario malintencionado puede controlar el valor de la propiedad del sistema APPHOME
, pueden engañar a la aplicación para que ejecute código malintencionado y asuma el control del sistema. System.loadLibrary()
para cargar código de una biblioteca nativa denominada library.dll
, que normalmente se encuentra en un directorio del sistema estándar.
...
System.loadLibrary("library.dll");
...
System.loadLibrary()
acepta un nombre de biblioteca y no una ruta para que se cargue la biblioteca. Según la documentación de la API de Java 1.4.2 API, esta función se comporta de la siguiente forma [1]:library.dll
en una posición superior a la del archivo que pretende cargar la aplicación, esta cargará la copia malintencionada en lugar del archivo previsto. Dada la naturaleza de la aplicación, esta se ejecuta con privilegios elevados. Es decir, que el contenido de library.dll
del usuario malintencionado se ejecutará con privilegios elevados, dándole la posibilidad de tener un control completo del sistema.Express
para cargar un archivo de biblioteca de forma dinámica. Node.js
seguirá buscando en la ruta de carga de biblioteca habitual un archivo o un directorio que contenga dicha biblioteca[1].
var express = require('express');
var app = express();
app.get('/', function(req, res, next) {
res.render('tutorial/' + req.params.page);
});
Express
, la página que se transfiere a Response.render()
cargará una biblioteca de la extensión cuando no se conozca de antemano. Esto suele estar bien para las entradas como "foo.pug", ya que implica la carga de la biblioteca pug
, un motor de creación de plantillas muy conocido. Sin embargo, si un atacante controlase la página, y por lo tanto la extensión, podría cargar cualquier biblioteca de las rutas de carga de módulos Node.js
. Dado que el programa no valida la información recibida del parámetro de URL, un atacante podría engañar a la aplicación para que ejecutara código malintencionado y tomara el control del sistema.APPHOME
para determinar el directorio en el que se ha instalado y, a continuación, carga una biblioteca nativa en función de una ruta relativa desde el directorio especificado.
...
$home = getenv("APPHOME");
$lib = $home + $LIBNAME;
dl($lib);
...
APPHOME
para que señale a una ruta diferente que contiene una versión maliciosa de LIBNAME
. Como el programa no valida el valor leído desde el entorno, si el usuario malintencionado puede controlar el valor de la propiedad del sistema APPHOME
, pueden engañar a la aplicación para que ejecute código malintencionado y asuma el control del sistema.dl()
para cargar código de una biblioteca denominada sockets.dll
, que puede cargarse desde varios ubicaciones, según la instalación y configuración.
...
dl("sockets");
...
dl()
acepta un nombre de biblioteca y no una ruta para que se cargue la biblioteca.sockets.dll
en una posición superior a la del archivo que pretende cargar la aplicación, esta cargará la copia malintencionada en lugar del archivo previsto. Dada la naturaleza de la aplicación, esta se ejecuta con privilegios elevados. Es decir, que el contenido de sockets.dll
del usuario malintencionado se ejecutará con privilegios elevados, dándole la posibilidad de tener un control completo del sistema.Kernel.system()
para ejecutar un ejecutable llamado program.exe
, que normalmente se encuentra dentro de un directorio estándar del sistema.
...
system("program.exe")
...
Kernel.system()
ejecuta algo a través de un shell. Si un usuario malintencionado es capaz de manipular las variables de entorno RUBYSHELL
o COMSPEC
, es posible que apunten a un ejecutable malintencionado al que se llamará con el comando dado a Kernel.system()
. Debido a la naturaleza de la aplicación, se ejecuta con los privilegios necesarios para realizar las operaciones del sistema, lo que significa que el comando program.exe
del usuario malintencionado ahora se ejecutará con estos privilegios, posiblemente proporcionándole un control completo del sistema.Kernel.system()
. Si un usuario malintencionado puede modificar la variable $PATH
para que señale a un archivo binario malintencionado llamado program.exe
y, a continuación, ejecutar la aplicación en su entorno, el archivo malintencionado binario se cargará en lugar del que se pretende. Debido a la naturaleza de la aplicación, se ejecuta con los privilegios necesarios para realizar las operaciones del sistema, lo que significa que el comando program.exe
del usuario malintencionado ahora se ejecutará con estos privilegios, posiblemente proporcionándole un control completo del sistema.InvokerServlet
puede permitir a los usuarios malintencionados llamar a cualquier clase en el servidor.InvokerServlet
en desuso se puede utilizar para llamar a cualquier clase disponible en la máquina virtual del servidor. Al adivinar el nombre completo de una clase, un atacante no solo puede cargar las clases de servlet, sino también las clases de POJO o cualquier otra clase disponible en la JVM.
@GetMapping("/prompt_injection")
String generation(String userInput1, ...) {
return this.clientBuilder.build().prompt()
.system(userInput1)
.user(...)
.call()
.content();
}
client = new Anthropic();
# Simulated attacker's input attempting to inject a malicious system prompt
attacker_input = ...
response = client.messages.create(
model = "claude-3-5-sonnet-20240620",
max_tokens=2048,
system = attacker_input,
messages = [
{"role": "user", "content": "Analyze this dataset for anomalies: ..."}
]
);
...
client = OpenAI()
# Simulated attacker's input attempting to inject a malicious system prompt
attacker_input = ...
completion = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": attacker_input},
{"role": "user", "content": "Compose a poem that explains the concept of recursion in programming."}
]
)
@GetMapping("/prompt_injection_persistent")
String generation(String userInput1, ...) {
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users WHERE ...");
String userName = "";
if (rs != null) {
rs.next();
userName = rs.getString("userName");
}
return this.clientBuilder.build().prompt()
.system("Assist the user " + userName)
.user(userInput1)
.call()
.content();
}
client = new Anthropic();
# Simulated attacker's input attempting to inject a malicious system prompt
attacker_query = ...;
attacker_name = db.qyery('SELECT name FROM user_profiles WHERE ...');
response = client.messages.create(
model = "claude-3-5-sonnet-20240620",
max_tokens=2048,
system = "Provide assistance to the user " + attacker_name,
messages = [
{"role": "user", "content": attacker_query}
]
);
...
client = OpenAI()
# Simulated attacker's input attempting to inject a malicious system prompt
attacker_name = cursor.fetchone()['name']
attacker_query = ...
completion = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "Provide assistance to the user " + attacker_name},
{"role": "user", "content": attacker_query}
]
)
Object.prototype
, si un atacante puede sobrescribir el prototipo de un objeto, normalmente puede sobrescribir la definición de Object.prototype
, lo que afecta a todos los objetos dentro de la aplicación.undefined
en lugar de establecerse siempre explícitamente, si el prototipo ha sido contaminado, la aplicación podría leer inadvertidamente el prototipo en lugar del objeto previsto.lodash
para contaminar el prototipo del objeto:
import * as lodash from 'lodash'
...
let clonedObject = lodash.merge({}, JSON.parse(untrustedInput));
...
{"__proto__": { "isAdmin": true}}
, entonces Object.prototype
habrá definido isAdmin = true
.
...
let config = {}
if (isAuthorizedAsAdmin()){
config.isAdmin = true;
}
...
if (config.isAdmin) {
// do something as the admin
}
...
isAdmin
solo debe establecerse en verdadero si isAuthorizedAdmin()
devuelve verdadero, como la aplicación no establece config.isAdmin = false
en la condición else, se basa en el hecho de que config.isAdmin === undefined === false
.config
ahora se ha configurado en isAdmin === true
, que permite eludir la autorización del administrador.select()
de SimpleDB que busca facturas que coincidan con una categoría de productos especificada por el usuario. El usuario también puede especificar la columna por la que se ordenarán los resultados. Imagine que la aplicación ya ha autenticado y establecido correctamente el valor de customerID
antes de este segmento de código.
...
String customerID = getAuthenticatedCustomerID(customerName, customerCredentials);
...
AmazonSimpleDBClient sdbc = new AmazonSimpleDBClient(appAWSCredentials);
String query = "select * from invoices where productCategory = '"
+ productCategory + "' and customerID = '"
+ customerID + "' order by '"
+ sortColumn + "' asc";
SelectResult sdbResult = sdbc.select(new SelectRequest(query));
...
select * from invoices
where productCategory = 'Fax Machines'
and customerID = '12345678'
order by 'price' asc
productCategory
y price
no contienen caracteres de comillas simples. Sin embargo, si un usuario malintencionado proporciona la cadena "Fax Machines' or productCategory = \"
" para productCategory
y la cadena "\" order by 'price
" para sortColumn
, la consulta se convierte en lo siguiente:
select * from invoices
where productCategory = 'Fax Machines' or productCategory = "'
and customerID = '12345678'
order by '" order by 'price' asc
select * from invoices
where productCategory = 'Fax Machines'
or productCategory = "' and customerID = '12345678' order by '"
order by 'price' asc
customerID
y ver los registros de facturas que coinciden con 'Fax Machines'
de todos los clientes.customerID
que antecede a este segmento de código.
...
productCategory = this.getIntent().getExtras().getString("productCategory");
sortColumn = this.getIntent().getExtras().getString("sortColumn");
customerID = getAuthenticatedCustomerID(customerName, customerCredentials);
c = invoicesDB.query(Uri.parse(invoices), columns, "productCategory = '" + productCategory + "' and customerID = '" + customerID + "'", null, null, null, "'" + sortColumn + "'asc", null);
...
select * from invoices
where productCategory = 'Fax Machines'
and customerID = '12345678'
order by 'price' asc
productCategory
. Por lo tanto, la consulta solo presentará el comportamiento correcto si productCategory
y sortColumn
no contienen caracteres de comillas simples. Si un usuario malintencionado proporciona la cadena "Fax Machines' or productCategory = \"
" para productCategory
, y la cadena "\" order by 'price
" para sortColumn
, la consulta se convierte en:
select * from invoices
where productCategory = 'Fax Machines' or productCategory = "'
and customerID = '12345678'
order by '" order by 'price' asc
select * from invoices
where productCategory = 'Fax Machines'
or productCategory = "' and customerID = '12345678' order by '"
order by 'price' asc
customerID
y ver registros de facturas que coincidan con 'Fax Machines'
de todos los clientes.
...
var authenticated = true;
...
database_connect.query('SELECT * FROM users WHERE name == ? AND password = ? LIMIT 1', userNameFromUser, passwordFromUser, function(err, results){
if (!err && results.length > 0){
authenticated = true;
}else{
authenticated = false;
}
});
if (authenticated){
//do something privileged stuff
authenticatedActions();
}else{
sendUnathenticatedMessage();
}
true
; en caso contrario, como false
. Desafortunadamente, como hay E/S que bloquea la devolución de llamada, esta se ejecutará de forma asíncrona y puede que se ejecute después de la comprobación if (authenticated)
, y como el valor predeterminado era true, irá en la instrucción if tanto si el usuario se ha autenticado realmente como si no.
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(Environment.getExternalStorageDirectory() + "/download/" + "app.apk")), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
...
public class Box{
public int area;
public static final int width = 10;
public static final Box box = new Box();
public static final int height = (int) (Math.random() * 100);
public Box(){
area = width * height;
}
...
}
...
Example 1
, el desarrollador espera que box.area
sea un entero aleatorio que resulta ser múltiplo de 10, ya que width
es igual a 10. Sin embargo, en realidad, siempre tuvo un valor codificado de 0. Se inicializan primero los campos estáticos finales declarados con una constante de tiempo de compilación y, a continuación, se ejecutan en orden. Esto significa que, puesto que height
no es una constante de tiempo de compilación, se declara después de la declaración debox
y, por consiguiente, se llama al constructor antes de que se inicialice el campo height
.
...
class Foo{
public static final int f = Bar.b - 1;
...
}
...
class Bar{
public static final int b = Foo.f + 1;
...
}
This example is perhaps easier to identify, but would be dependent on which class is loaded first by the JVM. In this exampleFoo.f
could be either -1 or 0, andBar.b
could be either 0 or 1.
setuid root
. El programa realiza ciertas operaciones de archivo en nombre de usuarios no privilegiados, y usa comprobaciones de acceso para asegurar que este no usa sus privilegios origen para realizar operaciones que no deberían estar disponibles para el usuario actual. El programa utiliza la llamada al sistema access()
para comprobar si la persona que está ejecutando el programa tiene permiso para acceder al archivo especificado antes de que abra el archivo y realiza las operaciones necesarias.
if (!access(file,W_OK)) {
f = fopen(file,"w+");
operate(f);
...
}
else {
fprintf(stderr,"Unable to open file %s.\n",file);
}
access()
presenta el comportamiento previsto y devuelve 0
si el usuario que ejecuta el programa dispone de los permisos necesarios para escribir en el archivo y, de no ser así, devuelve -1. Sin embargo, debido a que tanto access()
como fopen()
realizan operaciones en los nombres de archivo en lugar de en los identificadores de archivo, no hay ninguna garantía de que la variable file
aún haga referencia al mismo archivo en el disco al transferirlo a fopen()
que cuando se transfirió a access()
. Si un usuario malintencionado sustituye file
tras la llamada a access()
por un vínculo simbólico a un archivo diferente, el programa utilizará sus privilegios raíz para realizar operaciones en el archivo, aunque se trate de un archivo que, de lo contrario, el usuario no podría modificar. Al engañar al programa para que realice una operación que, de lo contrario, no sería permisible, el usuario malintencionado ha obtenido privilegios elevados.root
. Si la aplicación es capaz de realizar cualquier operación que el usuario malintencionado no tendría permiso para realizar, se trata de un posible objetivo.
fd = creat(FILE, 0644); /* Create file */
if (fd == -1)
return;
if (chown(FILE, UID, -1) < 0) { /* Change file owner */
...
}
chown()
es el mismo que el archivo creado por la llamada a creat()
, pero esto no siempre es así. Como chown()
funciona en un nombre de archivo y no en un identificador de archivo, un atacante podría ser capaz de reemplazar el archivo con un vínculo a un archivo que no posee el atacante. La llamada a chown()
proporcionaría así al atacante la propiedad del archivo vinculado.CBL_CHECK_FILE_EXIST
para comprobar si el archivo existe antes de crear uno y lleva a cabo las operaciones necesarias.
CALL "CBL_CHECK_FILE_EXIST" USING
filename
file-details
RETURNING status-code
END-CALL
IF status-code NOT = 0
MOVE 3 to access-mode
MOVE 0 to deny-mode
MOVE 0 to device
CALL "CBL_CREATE_FILE" USING
filename
access-mode
deny-mode
device
file-handle
RETURNING status-code
END-CALL
END-IF
CBL_CHECK_FILE_EXIST
se comporta tal y como se esperaba y devuelve un valor distinto de cero, que indica que el archivo no existe. Sin embargo, dado que tanto CBL_CHECK_FILE_EXIST
como CBL_CREATE_FILE
operan sobre los nombres de los archivos y no sobre los identificadores, no hay garantía de que la variable filename
todavía se refiera al mismo archivo en el disco cuando se pase a CBL_CREATE_FILE
que era cuando se pasó a CBL_CHECK_FILE_EXIST
. Si un atacante crea un filename
después de la llamada a CBL_CHECK_FILE_EXIST
, la llamada a CBL_CREATE_FILE
fallará, lo que llevará al programa a creer que el archivo está vacío, cuando de hecho este contiene datos controlados por el atacante.root
que realiza ciertas operaciones de archivo en nombre de usuarios no privilegiados, y usa comprobaciones de acceso para asegurar que este no usa sus privilegios origen para realizar operaciones que no deberían estar disponibles para el usuario actual. Engañando al programa para que realice una operación que no sería permisible de otro modo, el atacante puede ganar ciertos privilegios superiores.parse()
y format()
de java.text.Format
contienen un error de diseño que puede ocasionar que un usuario vea los datos de otro usuario.parse()
y format()
de java.text.Format
contienen una condición de carrera que puede ocasionar que un usuario vea los datos de otro usuario.
public class Common {
private static SimpleDateFormat dateFormat;
...
public String format(Date date) {
return dateFormat.format(date);
}
...
final OtherClass dateFormatAccess=new OtherClass();
...
public void function_running_in_thread1(){
System.out.println("Time in thread 1 should be 12/31/69 4:00 PM, found: "+ dateFormatAccess.format(new Date(0)));
}
public void function_running_in_thread2(){
System.out.println("Time in thread 2 should be around 12/29/09 6:26 AM, found: "+ dateFormatAccess.format(new Date(System.currentTimeMillis())));
}
}
format()
.open_basedir
de PHP contiene un error de diseño que lo hace vulnerable a condiciones de carrera de acceso a archivos, lo cual puede permitir a un atacante eludir las comprobaciones de control de acceso en el sistema de archivos.open_basedir
está presente, intenta impedir a los programas de PHP que trabajen con archivos ubicados fuera del los árboles de directorios especificados en php.ini. Aunque la opción open_basedir
es un beneficio general para la seguridad, la implementación sufre una condición de carrera que puede permitir a los atacantes burlar las restricciones en algunas circunstancias [2]. Existe una condición de carrera de hora de comprobación/hora de uso (TOCTOU) entre la hora a la que PHP realiza la comprobación de permiso de acceso y el momento en que se abre el archivo. Como ocurre con las condiciones de carrera de los sistemas de archivos en otros lenguajes, esta vulnerabilidad puede permitir a los atacantes que sustituyan un symlink a un archivo que pasa la comprobación de control de acceso por otro que no superaría la prueba, obteniendo así acceso al archivo protegido.RoamingFolder
o RoamingSettings
de la clase Windows.Storage.ApplicationData
.RoamingFolder
y RoamingSettings
obtienen un contenedor en el almacén de datos de aplicaciones de movilidad, que luego se puede usar para compartir datos entre dos o varios dispositivos. Al escribir y leer los objetos almacenados en el almacén de datos de aplicaciones de movilidad, el desarrollador aumenta los riesgos. Estos abarcan la confidencialidad, la integridad y la disponibilidad de los datos, las aplicaciones y los sistemas que comparten esos objetos a través de dicho almacén.free()
dos veces con el mismo valor, se puede producir un buffer overflow. Si un programa llama a free()
dos veces con el mismo argumento, las estructuras de datos de administración de memoria del programa se dañan. Esto puede provocar que el programa se bloquee o, en algunas circunstancias, que se realicen dos llamadas posteriores a malloc()
para devolver la misma referencia. Si malloc()
devuelve el mismo valor dos veces y el programa concede posteriormente al usuario malintencionado control de los datos escritos en la memoria que se ha asignado dos veces, el programa será vulnerable a un ataque de buffer overflow.
void sh(int dummy) {
...
free(global2);
free(global1);
...
}
int main(int argc,char* argv[]) {
...
signal(SIGHUP,sh);
signal(SIGTERM,sh);
...
}
public class GuestBook extends HttpServlet {
String name;
protected void doPost (HttpServletRequest req, HttpServletResponse res) {
name = req.getParameter("name");
...
out.println(name + ", thanks for visiting!");
}
}
Dick
" a name
Jane
" a name
Jane, thanks for visiting!
"Jane, thanks for visiting!
"
public class ConnectionManager {
private static Connection conn = initDbConn();
...
}
dangerouslySetInnerHTML
El atributo se establece como HTML desde el código innecesariamente.dangerouslySetInnerHTML
en React reemplaza el uso de innerHTML en el DOM del navegador, pero se ha cambiado el nombre de la API para transmitir los peligros potenciales de su uso. En general, configurar HTML desde el código es arriesgado porque es fácil exponer inadvertidamente a los usuarios a un ataque de Cross-Site Scripting (XSS).dangerouslySetInnerHTML
:
function MyComponent(data) {
return (
<div
dangerouslySetInnerHTML={{__html: data.innerHTML}}
/>
);
}