Una mala calidad del código lleva a un comportamiento no predecible. Desde la perspectiva de un usuario, muchas veces también supone una usabilidad limitada. Pero para un atacante es una oportunidad para atacar al sistema de formas insospechadas.
...
var file:File = new File(directoryName + "\\" + fileName);
...
...
FileStream f = File.Create(directoryName + "\\" + fileName);
...
...
File file = new File(directoryName + "\\" + fileName);
...
...
os.open(directoryName + "\\" + fileName);
...
<script>
.
...
public String tagProcessor(String tag){
if (tag.toUpperCase().equals("SCRIPT")){
return null;
}
//does not contain SCRIPT tag, keep processing input
...
}
...
Example 1
es que java.lang.String.toUpperCase()
, cuando se usa sin configuración regional, emplea las reglas de la configuración regional predeterminada. El hecho de utilizar la configuración regional turca "title".toUpperCase()
devuelve "T\u0130TLE", donde "\u0130" es el carácter "I LATINA MAYÚSCULA CON PUNTO". Esto puede dar lugar a resultados inesperados, como en el Example 1
, en el que se impide que la validación capture la palabra "script", lo que puede dar lugar a una potencial vulnerabilidad de scripts entre sitios.
...
import java.sql.PreparedStatement;
import com.sap.sql.NativeSQLAccess;
String mssOnlyStmt = "...";
// variant 1
PreparedStatement ps =
NativeSQLAccess.prepareNativeStatement(
conn, mssOnlyStmt);
. . .
// variant 2
Statement stmt =
NativeSQLAccess.createNativeStatement(conn);
int result = stmt.execute(mssOnlyStmt);
. . .
// variant 3
CallableStatement cs =
NativeSQLAccess.prepareNativeCall(
conn, mssOnlyStmt);
. . .
...
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.
null
antes de comprobar si es null
o no. Los errores de desreferencia tras la comprobación se producen cuando un programa realiza una comprobación explícita de null
y procede a desreferenciar el puntero cuando se sabe que es null
. Los errores de este tipo son normalmente el resultado de errores de escritura o descuidos del programador. Los errores de desreferencia tras el almacenamiento se producen cuando un programa establece de forma explícita un puntero en null
y luego lo desreferencia. Con frecuencia, el error es el resultado de que un programador inicialice una variable en null
cuando se declara.foo
es null
y por eso lo desreferencia erróneamente. Si foo
es null
cuando se comprueba en la instrucción if
, se produce una desreferencia null
, lo que provoca una excepción de puntero nulo.Ejemplo 2: En el código siguiente, el programador presupone que la variable
if (foo is null) {
foo.SetBar(val);
...
}
foo
no es null
y confirma esta suposición eliminando la referencia del objeto. Sin embargo, este contradice posteriormente esta suposición al comprobar si foo
es null
. Si foo
puede ser null
al comprobarlo en relación con la instrucción if
, también puede ser null
cuando se elimina su referencia, lo que puede provocar una excepción de puntero nulo. O bien la desreferencia no es segura o la comprobación posterior es innecesaria.Ejemplo 3: En el código siguiente, el programador establece explícitamente la variable
foo.SetBar(val);
...
if (foo is not null) {
...
}
foo
en null
. A continuación, el programador desreferencia foo
antes de comprobar si en el objeto hay un valor null
.
Foo foo = null;
...
foo.SetBar(val);
...
}
null
antes de comprobar si es null
o no. Los errores de desreferencia tras la comprobación se producen cuando un programa realiza una comprobación explícita de null
y procede a desreferenciar el puntero cuando se sabe que es null
. Los errores de este tipo son normalmente el resultado de errores de escritura o descuidos del programador. Los errores de desreferencia tras el almacenamiento se producen cuando un programa establece de forma explícita un puntero en null
y luego lo desreferencia. Con frecuencia, el error es el resultado de que un programador inicialice una variable en null
cuando se declara.ptr
no es NULL
. Esta suposición se hace explícita cuando el programador desreferencia el puntero. Esta suposición luego queda contradicha cuando el programador contrasta ptr
y NULL
. Si ptr
puede ser NULL
al comprobarla en la instrucción if
, entonces también puede ser NULL
cuando se desreferencia y podría ocasionar un error de segmentación.Ejemplo 2: En el código siguiente, el programador confirma que la variable
ptr->field = val;
...
if (ptr != NULL) {
...
}
ptr
es NULL
y por eso lo desreferencia erróneamente. Si ptr
es NULL
cuando se comprueba en la instrucción if
, entonces se produce una desreferencia de null
que provocará un error de segmentación.Ejemplo 3: En el código siguiente, el programador olvida que la cadena
if (ptr == null) {
ptr->field = val;
...
}
'\0'
es en realidad 0 o NULL
; por lo tanto, puede desreferenciar un puntero nulo y provocar un fallo de segmentación.Ejemplo 4: En el código siguiente, el programador establece explícitamente la variable
if (ptr == '\0') {
*ptr = val;
...
}
ptr
en NULL
. A continuación, el programador desreferencia ptr
antes de comprobar si en el objeto hay un valor null
.
*ptr = NULL;
...
ptr->field = val;
...
}
null
, pero continúa eliminando la referencia del objeto cuando se detecta que es null
. Los errores de este tipo son normalmente el resultado de errores de escritura o descuidos del programador.foo
es null
y por eso lo desreferencia erróneamente. Si foo
es null
cuando se compruebe en la instrucción if
, se producirá una desreferencia null
, provocando una excepción de puntero nulo.
if (foo == null) {
foo.setBar(val);
...
}
unsigned char
a un int
, pero el valor devuelto se asigna a un tipo char
.EOF
.EOF
.
char c;
while ( (c = getchar()) != '\n' && c != EOF ) {
...
}
getchar()
se convierte en un char
y se compara con EOF
(un int
). Suponiendo que c
sea un valor de 8 bits con signo y que EOF
sea un valor de 32 bits con signo, entonces si getchar()
devuelve un carácter representado por 0xFF, el valor de c
producirá una extensión de signo para 0xFFFFFFFF en la comparación con EOF
. Como EOF
se define normalmente como -1 (0xFFFFFFFF), el bucle finalizará de forma errónea.amount
puede albergar un valor negativo cuando se devuelve. Como la función se declara para devolver un entero sin signo, amount
se convierte implícitamente a sin signo.
unsigned int readdata () {
int amount = 0;
...
if (result == ERROR)
amount = -1;
...
return amount;
}
Example 1
, el valor devuelto de readdata()
será 4,294,967,295 en un sistema que utiliza enteros de 32 bits.accecssmainframe()
, la variable amount
puede albergar un valor negativo cuando se devuelve. Como la función se declara para devolver un valor sin signo, amount
se convertirá implícitamente en un número sin signo.
unsigned int readdata () {
int amount = 0;
...
amount = accessmainframe();
...
return amount;
}
accessmainframe()
es -1, entonces el valor devuelto de readdata()
será 4,294,967,295 en un sistema que utilice enteros de 32 bits.1
debe transferirse al primer parámetro (el número de versión) de la siguiente función del sistema de archivos:
__xmknod
2
debe transferirse al tercer parámetro (el argumento de grupo) de las siguientes funciones de cadenas de caracteres amplias:
__wcstod_internal
__wcstof_internal
_wcstol_internal
__wcstold_internal
__wcstoul_internal
3
debe transferirse como primer parámetro (el número de versión) de las siguientes funciones del sistema de archivos:
__xstat
__lxstat
__fxstat
__xstat64
__lxstat64
__fxstat64
FILE *sysfile = fopen(test.file, "w+");
FILE insecureFile = *sysfile;
sysfile
se desreferencia en la asignación de insecureFile
, el uso deinsecureFile
puede resultar en una amplia variedad de problemas.
FILE *sysfile = fopen(test.file, "r+");
res = fclose(sysfile);
if(res == 0){
printf("%c", getc(sysfile));
}
getc()
se ejecuta después de que la secuencia del archivo para sysfile
esté cerrada, getc()
da como resultado un comportamiento indefinido y puede provocar un fallo del sistema o una posible modificación o lectura del mismo archivo o de uno diferente.
std::auto_ptr<foo> p(new foo);
foo* rawFoo = p.get();
delete rawFoo;
delete
, la clase de administración sabrá que no puede seguir utilizando el puntero.int a = (Int32)i + (Int32)j;
lanza una excepción no controlada y bloquea la aplicación en tiempo de ejecución.
class Program
{
static int? i = j;
static int? j;
static void Main(string[] args)
{
j = 100;
int a = (Int32)i + (Int32)j;
Console.WriteLine(i);
Console.WriteLine(j);
Console.WriteLine(a);
}
}
aN
y bN
, pero en el caso predeterminado, el programador accidentalmente estableció el valor de aN
dos veces.
switch (ctl) {
case -1:
aN = 0; bN = 0;
break;
case 0:
aN = i; bN = -i;
break;
case 1:
aN = i + NEXT_SZ; bN = i - NEXT_SZ;
break;
default:
aN = -1; aN = -1;
break;
}
Finalize()
de StreamReader
con el tiempo llama a Close()
, pero no hay ninguna garantía en cuanto el tiempo que pasará antes de que se llame al método Finalize()
. De hecho, no hay ninguna garantía de que se llame en algún momento al método Finalize()
. En un entorno muy activo, esto puede provocar que la VM utilice todos los identificadores de archivo disponibles.Ejemplo 2: en condiciones normales, el siguiente código ejecuta una consulta de base de datos, procesa los resultados devueltos por la base de datos y cierra el objeto
private void processFile(string fName) {
StreamWriter sw = new StreamWriter(fName);
string line;
while ((line = sr.ReadLine()) != null)
processLine(line);
}
SqlConnection
asignado. Sin embargo, si se produce una excepción al ejecutar el SQL o al procesar los resultados, el objeto SqlConnection
no se cerrará. Si esto ocurre con bastante frecuencia, la base de datos agotará los cursores disponibles y no podrá ejecutar ninguna otra consulta SQL.
...
SqlConnection conn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(queryString);
cmd.Connection = conn;
conn.Open();
SqlDataReader rdr = cmd.ExecuteReader();
HarvestResults(rdr);
conn.Connection.Close();
...
int decodeFile(char* fName)
{
char buf[BUF_SZ];
FILE* f = fopen(fName, "r");
if (!f) {
printf("cannot open %s\n", fName);
return DECODE_FAIL;
} else {
while (fgets(buf, BUF_SZ, f)) {
if (!checkChecksum(buf)) {
return DECODE_FAIL;
} else {
decodeBlock(buf);
}
}
}
fclose(f);
return DECODE_SUCCESS;
}
CALL "CBL_CREATE_FILE"
USING filename
access-mode
deny-mode
device
file-handle
END-CALL
IF return-code NOT = 0
DISPLAY "Error!"
GOBACK
ELSE
PERFORM write-data
IF ws-status-code NOT = 0
DISPLAY "Error!"
GOBACK
ELSE
DISPLAY "Success!"
END-IF
END-IF
CALL "CBL_CLOSE_FILE"
USING file-handle
END-CALL
GOBACK
.
New()
establece una nueva conexión con el daemon de registro del sistema. Forma parte del paquete log.syslog. Cada escritura al escritor devuelto envía un mensaje de registro con la prioridad dada (una combinación de la función syslog y la gravedad) y una etiqueta de prefijo. En un entorno ocupado, esto puede provocar que el sistema consuma todos sus sockets.Ejemplo 2: En este ejemplo, el método
func TestNew() {
s, err := New(syslog.LOG_INFO|syslog.LOG_USER, "the_tag")
if err != nil {
if err.Error() == "Unix syslog delivery error" {
fmt.Println("skipping: syslogd not running")
}
fmt.Println("New() failed: %s", err)
}
}
Dial()
del paquete net/smtp
devuelve un nuevo cliente conectado a un servidor SMTP en localhost. Los recursos de conexión se asignan, pero nunca se liberan llamando a la función Close()
.
func testDial() {
client, _ := smtp.Dial("127.0.0.1")
client.Hello("")
}
Arena.ofConfined()
no está cerrado.
...
Arena offHeap = Arena.ofConfined()
MemorySegment str = offHeap.allocateUtf8String("data");
...
//offHeap is never closed
BEGIN
...
F1 := UTL_FILE.FOPEN('user_dir','u12345.tmp','R',256);
UTL_FILE.GET_LINE(F1,V1,32767);
...
END;
Camera
en los controladores de eventos onPause()
, onStop()
o onDestroy()
.Camera
que no se ha liberado en las devoluciones de llamada onPause()
, onStop()
o onDestroy()
. El SO Android invoca estas devoluciones de llamadas cada vez que necesita enviar la actividad actual al segundo plano o cuando necesita destruir temporalmente la actividad debido al bajo nivel de recursos del sistema. Al no liberar correctamente el objeto Camera
, la actividad impide que otras aplicaciones (o incluso futuras instancias de la misma aplicación) accedan a la cámara. Además, la conservación de la posesión de la instancia Camera
mientras la actividad se encuentra pausada puede afectar negativamente a la experiencia del usuario debido a un consumo innecesario de la energía de la batería.onPause()
, que debe utilizarse para liberar el objeto Camera
, ni lo libera correctamente durante la secuencia de cierre.
public class UnreleasedCameraActivity extends Activity {
private Camera cam;
@Override
public void onCreate(Bundle state) {
...
}
@Override
public void onRestart() {
...
}
@Override
public void onStop() {
cam.stopPreview();
}
}
MediaRecorder
, MediaPlayer
o AudioRecord
en sus controladores de eventos onPause()
, onStop()
o onDestroy()
.onPause()
, onStop()
o onDestroy()
. El SO Android invoca estas devoluciones de llamadas cada vez que necesita enviar la actividad actual al segundo plano o cuando necesita destruir temporalmente la actividad debido al bajo nivel de recursos del sistema. Al no liberar correctamente el objeto multimedia, la actividad provoca que los siguientes accesos al hardware multimedia de Android (por parte de la misma aplicación u otras) se reviertan a las implementaciones de software o incluso presenten errores de forma conjunta. Al dejar abiertas demasiadas instancias multimedia sin liberar, Android generará excepciones, lo que provocará realmente una denegación de servicio. Además, la conservación de la posesión de la instancia multimedia mientras la actividad se encuentra pausada puede afectar negativamente a la experiencia del usuario debido a un consumo innecesario de la energía de la batería.onPause()
, que debe utilizarse para liberar el objeto multimedia, ni lo libera correctamente durante la secuencia de cierre.
public class UnreleasedMediaActivity extends Activity {
private MediaPlayer mp;
@Override
public void onCreate(Bundle state) {
...
}
@Override
public void onRestart() {
...
}
@Override
public void onStop() {
mp.stop();
}
}
onPause()
, onStop()
o onDestroy()
.onPause()
, onStop()
o onDestroy()
. El SO Android invoca estas devoluciones de llamadas cada vez que necesita enviar la actividad actual al segundo plano o cuando necesita destruir temporalmente la actividad debido al bajo nivel de recursos del sistema. Al no cerrar correctamente la base de datos, la actividad puede agotar los cursores disponibles del dispositivo si la actividad se reinicia constantemente. Además, en función de la implementación, el sistema operativo Android puede generar también DatabaseObjectNotClosedException
, lo que provoca que se bloquee la aplicación si no se detecta la excepción.onPause()
, que debería usarse para liberar el objeto de base de datos, ni lo libera correctamente durante la secuencia de cierre.
public class MyDBHelper extends SQLiteOpenHelper {
...
}
public class UnreleasedDBActivity extends Activity {
private myDBHelper dbHelper;
private SQLiteDatabase db;
@Override
public void onCreate(Bundle state) {
...
db = dbHelper.getWritableDatabase();
...
}
@Override
public void onRestart() {
...
}
@Override
public void onStop() {
db.insert(cached_data); // flush cached data
}
}
PWD_COMPARE
que no tenga acceso a sys.dba_users
para comprobar una contraseña de usuario.
CREATE or REPLACE procedure PWD_COMPARE(p_user VARCHAR, p_pwd VARCHAR)
AUTHID DEFINED
IS
cursor INTEGER;
...
BEGIN
IF p_user != 'SYS' THEN
cursor := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(cursor, 'SELECT password FROM SYS.DBA_USERS WHERE username = :u', DBMS_SQL.NATIVE);
DBMS_SQL.BIND_VARIABLE(cursor, ':u', p_user);
...
END IF;
END PWD_COMPARE;
sys
. Una manera de causar una excepción consiste en pasar un argumento demasiado largo a p_user
. Una vez que el usuario malintencionado sepa que el cursor se ha perdido, solo tiene que adivinarlo y asignar nuevas variables de enlace.
DECLARE
x VARCHAR(32000);
i INTEGER;
j INTEGER;
r INTEGER;
password VARCHAR2(30);
BEGIN
FOR i IN 1..10000 LOOP
x:='b' || x;
END LOOP;
SYS.PWD_COMPARE(x,'password');
EXCEPTION WHEN OTHERs THEN
FOR j IN 1..10000
DBMS_SQL.BIND_VARIABLE(j, ':u', 'SYS');
DBMS_SQL.DEFINE_COLUMN(j, 1, password, 30);
r := DBMS_SQL.EXECUTE(j);
IF DBMS_SQL.FETCH_ROWS(j) > 0 THEN
DBMS_SQL.COLUMN_VALUE(j, 1, password);
EXIT;
END IF;
END LOOP;
...
END;
DATA: result TYPE demo_update,
request TYPE REF TO IF_HTTP_REQUEST,
obj TYPE REF TO CL_SQL_CONNECTION.
TRY.
...
obj = cl_sql_connection=>get_connection( `R/3*my_conn`).
FINAL(sql) = NEW cl_sql_prepared_statement(
statement = `INSERT INTO demo_update VALUES( ?, ?, ?, ?, ?, ? )`).
CATCH cx_sql_exception INTO FINAL(exc).
...
ENDTRY.
SqlConnection
asignado. Sin embargo, si se produce una excepción al ejecutar el SQL o al procesar los resultados, el objeto SqlConnection
no se cerrará. Si esto ocurre con bastante frecuencia, la base de datos agotará los cursores disponibles y no podrá ejecutar ninguna otra consulta SQL.
...
SqlConnection conn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(queryString);
cmd.Connection = conn;
conn.Open();
SqlDataReader rdr = cmd.ExecuteReader();
HarvestResults(rdr);
conn.Connection.Close();
...
- void insertUser:(NSString *)name {
...
sqlite3_stmt *insertStatement = nil;
NSString *insertSQL = [NSString stringWithFormat:@INSERT INTO users (name, age) VALUES (?, ?)];
const char *insert_stmt = [insertSQL UTF8String];
...
if ((result = sqlite3_prepare_v2(database, insert_stmt,-1, &insertStatement, NULL)) != SQLITE_OK) {
MyLog(@"%s: sqlite3_prepare error: %s (%d)", __FUNCTION__, sqlite3_errmsg(database), result);
return;
}
if ((result = sqlite3_step(insertStatement)) != SQLITE_DONE) {
MyLog(@"%s: step error: %s (%d)", __FUNCTION__, sqlite3_errmsg(database), result);
return;
}
...
}
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(CXN_SQL);
harvestResults(rs);
stmt.close();
func insertUser(name:String, age:int) {
let dbPath = URL(fileURLWithPath: Bundle.main.resourcePath ?? "").appendingPathComponent("test.sqlite").absoluteString
var db: OpaquePointer?
var stmt: OpaquePointer?
if sqlite3_open(dbPath, &db) != SQLITE_OK {
print("Error opening articles database.")
return
}
let queryString = "INSERT INTO users (name, age) VALUES (?,?)"
if sqlite3_prepare(db, queryString, -1, &stmt, nil) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
log("error preparing insert: \(errmsg)")
return
}
if sqlite3_bind_text(stmt, 1, name, -1, nil) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
log("failure binding name: \(errmsg)")
return
}
if sqlite3_bind_int(stmt, 2, age) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
log("failure binding name: \(errmsg)")
return
}
if sqlite3_step(stmt) != SQLITE_DONE {
let errmsg = String(cString: sqlite3_errmsg(db)!)
log("failure inserting user: \(errmsg)")
return
}
}
finalize()
de ZipFile
con el tiempo llama a close()
, pero no hay ninguna garantía en cuanto al tiempo que pasará antes de que se llame al método finalize()
. En un entorno muy activo, esto puede provocar que la JVM utilice todos los identificadores de archivo.Ejemplo 2: en condiciones normales, la siguiente solución cierra correctamente el identificador de archivo tras imprimir todas las entradas de archivos zip. Sin embargo, si se produce una excepción al iterar por las entradas, no se cerrará el identificador de archivo zip. Si esto se produce con suficiente frecuencia, la JVM podría quedarse sin identificadores de archivo disponibles.
public void printZipContents(String fName) throws ZipException, IOException, SecurityException, IllegalStateException, NoSuchElementException {
ZipFile zf = new ZipFile(fName);
Enumeration<ZipEntry> e = zf.entries();
while (e.hasMoreElements()) {
printFileInfo(e.nextElement());
}
}
public void printZipContents(String fName) throws ZipException, IOException, SecurityException, IllegalStateException, NoSuchElementException {
ZipFile zf = new ZipFile(fName);
Enumeration<ZipEntry> e = zf.entries();
while (e.hasMoreElements()) {
printFileInfo(e.nextElement());
}
zf.close();
}