Los problemas de validación y representación de entradas están causados por metacaracteres, codificaciones alternativas y representaciones numéricas. Los problemas de seguridad surgen de entradas en las que se confía. Estos problemas incluyen: «desbordamientos de búfer», ataques de «scripts de sitios», "SQL injection" y muchas otras acciones.
Value Stack
de Struts 2. Al permitir que se evalúen expresiones no validadas contrastándolas con la Value Stack
, puede permitir que un atacante acceda y modifique variables del sistema o ejecute un código arbitrario.
OgnlContext ctx = new OgnlContext();
String expression = request.getParameter("input");
Object expr = Ognl.parseExpression(expression);
Object value = Ognl.getValue(expr, ctx, root);
System.out.println("Value: " + value);
(#rt = @java.lang.Runtime@getRuntime(),#rt.exec("calc.exe"))
%{expr}
) en una etiqueta Struts que evalúa la expresión OGNL dos veces. Un usuario malintencionado que controlase el resultado de la primera evaluación podría controlar la expresión evaluada en la segunda evaluación OGNL e inyectar expresiones OGNL arbitrarias.redirectAction
evalúa sus parámetros dos veces. En este caso, un usuario malintencionado podría controlar el resultado de la expresión OGNL forzada en el parámetro actionName
mediante un parámetro de solicitud redirect
.
...
<action name="index" class="com.acme.MyAction">
<result type="redirectAction">
<param name="actionName">${#parameters['redirect']}</param>
<param name="namespace">/foo</param>
</result>
</action>
...
%{#parameters['redirect']}
que devuelve una cadena que controla el usuario como una expresión OGNL, lo que permitiría al usuario malintencionado evaluar expresiones OGNL arbitrarias.execute()
. El carácter !
(signo de admiración) o el prefijo method:
se pueden usar en la dirección URL de la acción para invocar cualquier método público en la acción si "Dynamic Method Invocation" está habilitada. En la versión 2.3.20
de Struts 2 el mecanismo para invocar el método alternativo que anteriormente se basaba en la reflexión, se sustituyó por OGNL, lo que permitía a los atacantes proporcionar expresiones OGNL malintencionadas en lugar de un nombre de método alternativo.debug
:console
mostrará una consola de evaluación OGNL que permite a los desarrolladores evaluar cualquier expresión OGNL arbitraria en el servidor.command
permitirá a los desarrolladores enviar expresiones OGNL arbitrarias para su evaluación usando el parámetro de solicitudexpression
.xml
volcará los parámetros, el contexto, la sesión y la pila de valores en un documento XML.browser
volcará los parámetros, el contexto, la sesión y la pila de valores en un documento HTML navegable.dest
cuando un usuario hace clic en el vínculo.
...
DATA: str_dest TYPE c.
str_dest = request->get_form_field( 'dest' ).
response->redirect( str_dest ).
...
Example 1
redireccionará el explorador a "http://www.wilyhacker.com".dest
cuando un usuario hace clic en el vínculo.
...
var params:Object = LoaderInfo(this.root.loaderInfo).parameters;
var strDest:String = String(params["dest"]);
host.updateLocation(strDest);
...
Example 1
redireccionará el explorador a "http://www.wilyhacker.com".PageReference
que consiste en una URL del parámetro de solicitud dest
.
public PageReference pageAction() {
...
PageReference ref = ApexPages.currentPage();
Map<String,String> params = ref.getParameters();
return new PageReference(params.get('dest'));
}
Example 1
redireccionará el explorador a "http://www.wilyhacker.com".dest
cuando un usuario hace clic en el vínculo.
String redirect = Request["dest"];
Response.Redirect(redirect);
Example 1
redireccionará el explorador a "http://www.wilyhacker.com".dest
cuando un usuario hace clic en el vínculo.
...
final server = await HttpServer.bind(host, port);
await for (HttpRequest request in server) {
final response = request.response;
final headers = request.headers;
final strDest = headers.value('strDest');
response.headers.contentType = ContentType.text;
response.redirect(Uri.parse(strDest!));
await response.close();
}
...
Example 1
redireccionará el explorador a "http://www.wilyhacker.com".dest
cuando un usuario hace clic en el vínculo.
...
strDest := r.Form.Get("dest")
http.Redirect(w, r, strDest, http.StatusSeeOther)
...
Example 1
redirecciona el explorador a "http://www.wilyhacker.com".dest
cuando un usuario hace clic en el vínculo.
<end-state id="redirectView" view="externalRedirect:#{requestParameters.dest}" />
Example 1
redireccionará el explorador a "http://www.wilyhacker.com".dest
cuando un usuario hace clic en el enlace.
...
strDest = form.dest.value;
window.open(strDest,"myresults");
...
Example 1
redireccionará el explorador a "http://www.wilyhacker.com".dest
cuando un usuario hace clic en el vínculo.
<%
...
$strDest = $_GET["dest"];
header("Location: " . $strDest);
...
%>
Example 1
redireccionará el explorador a "http://www.wilyhacker.com".dest
cuando un usuario hace clic en el vínculo.
...
-- Assume QUERY_STRING looks like dest=http://www.wilyhacker.com
dest := SUBSTR(OWA_UTIL.get_cgi_env('QUERY_STRING'), 6);
OWA_UTIL.redirect_url('dest');
...
Example 1
redireccionará el explorador a "http://www.wilyhacker.com".dest
cuando un usuario haga clic en el vínculo.
...
strDest = request.field("dest")
redirect(strDest)
...
Example 1
redireccionará el explorador a "http://www.wilyhacker.com".dest
.
...
str_dest = req.params['dest']
...
res = Rack::Response.new
...
res.redirect("http://#{dest}")
...
Example 1
redireccionará el explorador a "http://www.wilyhacker.com".dest
.
def myAction = Action { implicit request =>
...
request.getQueryString("dest") match {
case Some(location) => Redirect(location)
case None => Ok("No url found!")
}
...
}
Example 1
redireccionará el explorador a "http://www.wilyhacker.com".requestToLoad
para que señale al parámetro "dest" de la URL original, si existe, y a la URL original que usa el esquema http://
, y finalmente carga esta solicitud en una WKWebView:
...
let requestToLoad : String
...
func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool {
...
if let urlComponents = NSURLComponents(URL: url, resolvingAgainstBaseURL: false) {
if let queryItems = urlComponents.queryItems as? [NSURLQueryItem]{
for queryItem in queryItems {
if queryItem.name == "dest" {
if let value = queryItem.value {
request = NSURLRequest(URL:NSURL(string:value))
requestToLoad = request
break
}
}
}
}
if requestToLoad == nil {
urlComponents.scheme = "http"
requestToLoad = NSURLRequest(URL:urlComponents.URL)
}
}
...
}
...
...
let webView : WKWebView
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
webView.loadRequest(appDelegate.requestToLoad)
...
Example 1
intentará solicitar y cargar "http://www.wilyhacker.com" en WKWebView.dest
cuando un usuario hace clic en el vínculo.
...
strDest = Request.Form('dest')
HyperLink.NavigateTo strDest
...
Example 1
redireccionará el explorador a "http://www.wilyhacker.com".strncpy()
, pueden provocar vulnerabilidades cuando se utilizan incorrectamente. La combinación de manipulación de memoria y presuposiciones erróneas acerca del tamaño y la formación de una unidad de datos es el motivo principal de la mayoría de desbordamientos del búfer.memcpy()
lee la memoria desde fuera de los límites asignados de cArray
, que contiene MAX
elementos de tipo char
, mientras que iArray
contiene MAX
elementos de tipo int
.Ejemplo 2: el siguiente programa corto usa un argumento de línea de comandos no confiable como búfer de búsqueda en una llamada para
void MemFuncs() {
char array1[MAX];
int array2[MAX];
memcpy(array2, array1, sizeof(array2));
}
memchr()
con un número constante de bytes que analizar.
int main(int argc, char** argv) {
char* ret = memchr(argv[0], 'x', MAX_PATH);
printf("%s\n", ret);
}
argv[0]
, buscando el dato argv[0]
hasta un número constante de bytes. Sin embargo, como el número (constante) de bytes puede ser mayor que los datos asignados para argv[0]
, la búsqueda podría continuar más allá de los datos asignados para argv[0]
. Este podría ser el caso cuando x
no se encuentra en argv[0]
.strncpy()
, pueden provocar vulnerabilidades cuando se utilizan incorrectamente. La combinación de manipulación de memoria y presuposiciones erróneas acerca del tamaño y la formación de una unidad de datos es el motivo principal de la mayoría de desbordamientos del búfer.char
, con la última referencia que introduzca un error por uno ("off-by-one").
char Read() {
char buf[5];
return 0
+ buf[0]
+ buf[1]
+ buf[2]
+ buf[3]
+ buf[4]
+ buf[5];
}
strncpy()
, pueden provocar vulnerabilidades cuando se utilizan incorrectamente. La combinación de manipulación de memoria y presuposiciones erróneas acerca del tamaño y la formación de una unidad de datos es el motivo principal de la mayoría de desbordamientos del búfer.getInputLength()
tiene un tamaño menor que el de la output
del búfer de destino. Sin embargo, como la comparación entre len
y MAX
tiene signo, si len
es negativo, se convertirá en un número positivo muy largo cuando se convierta a un argumento sin signo para memcpy()
.
void TypeConvert() {
char input[MAX];
char output[MAX];
fillBuffer(input);
int len = getInputLength();
if (len <= MAX) {
memcpy(output, input, len);
}
...
}
...
*Get the report that is to be deleted
r_name = request->get_form_field( 'report_name' ).
CONCATENATE `C:\\users\\reports\\` r_name INTO dsn.
DELETE DATASET dsn.
...
..\\..\\usr\\sap\\DVEBMGS00\\exe\\disp+work.exe
", la aplicación eliminará un archivo crítico e inmediatamente se bloqueará el sistema SAP.
...
PARAMETERS: p_date TYPE string.
*Get the invoice file for the date provided
CALL FUNCTION 'FILE_GET_NAME'
EXPORTING
logical_filename = 'INVOICE'
parameter_1 = p_date
IMPORTING
file_name = v_file
EXCEPTIONS
file_not_found = 1
OTHERS = 2.
IF sy-subrc <> 0.
* Implement suitable error handling here
ENDIF.
OPEN DATASET v_file FOR INPUT IN TEXT MODE.
DO.
READ DATASET v_file INTO v_record.
IF SY-SUBRC NE 0.
EXIT.
ELSE.
WRITE: / v_record.
ENDIF.
ENDDO.
...
..\\..\\usr\\sap\\sys\\profile\\default.pfl
" en lugar de una fecha válida, la aplicación revelará la configuración predeterminada de todos los parámetros del perfil de servidor de aplicaciones de SAP, lo que posiblemente conduciría a ataques más sofisticados.../../tomcat/conf/server.xml
", lo que podría causar que la aplicación eliminase uno de sus propios archivos de configuración.Ejemplo 2: el siguiente código utiliza la entrada desde un archivo de configuración para determinar qué archivo abrir y escribir en una consola de depuración o en un archivo de registro. Si el programa se ejecuta con los privilegios adecuados y los usuarios malintencionados pueden cambiar el archivo de configuración, estos podrán utilizar el programa para leer cualquier archivo del sistema que termine con la extensión
var params:Object = LoaderInfo(this.root.loaderInfo).parameters;
var rName:String = String(params["reportName"]);
var rFile:File = new File("/usr/local/apfr/reports/" + rName);
...
rFile.deleteFile();
.txt
.
var fs:FileStream = new FileStream();
fs.open(new File(String(configStream.readObject())+".txt"), FileMode.READ);
fs.readBytes(arr);
trace(arr);
public class MyController {
...
public PageRerference loadRes() {
PageReference ref = ApexPages.currentPage();
Map<String,String> params = ref.getParameters();
if (params.containsKey('resName')) {
if (params.containsKey('resPath')) {
return PageReference.forResource(params.get('resName'), params.get('resPath'));
}
}
return null;
}
}
..\\..\\Windows\\System32\\krnl386.exe
", lo que hará que la aplicación elimine un archivo importante del sistema de Windows.Ejemplo 2: el siguiente código utiliza una entrada de un archivo de configuración para determinar el archivo que se debe abrir y reenviar al usuario. Si el programa se ejecuta con privilegios adecuados y los usuarios malintencionados pueden modificar el archivo de configuración, estos podrán utilizar el programa para leer cualquier archivo del sistema que termine con la extensión ".txt".
String rName = Request.Item("reportName");
...
File.delete("C:\\users\\reports\\" + rName);
sr = new StreamReader(resmngr.GetString("sub")+".txt");
while ((line = sr.ReadLine()) != null) {
Console.WriteLine(line);
}
../../apache/conf/httpd.conf
", que provocará que la aplicación elimine el archivo de configuración especificado.Ejemplo 2: el código siguiente utiliza entrada de la línea de comandos para determinar qué archivo abrir y reenviar al usuario. Si el programa se ejecuta con privilegios adecuados y los usuarios malintencionados pueden crear vínculos simbólicos al archivo, estos también pueden usar el programa para leer la primera parte de cualquier archivo del sistema.
char* rName = getenv("reportName");
...
unlink(rName);
ifstream ifs(argv[0]);
string s;
ifs >> s;
cout << s;
...
EXEC CICS
WEB READ
FORMFIELD(FILE)
VALUE(FILENAME)
...
END-EXEC.
EXEC CICS
READ
FILE(FILENAME)
INTO(RECORD)
RIDFLD(ACCTNO)
UPDATE
...
END-EXEC.
...
..\\..\\Windows\\System32\\krnl386.exe
", lo que hará que la aplicación elimine un archivo importante del sistema de Windows.
<cffile action = "delete"
file = "C:\\users\\reports\\#Form.reportName#">
final server = await HttpServer.bind('localhost', 18081);
server.listen((request) async {
final headers = request.headers;
final path = headers.value('path');
File(path!).delete();
}
Example 1
, no se valida headers.value('path')
antes de llevar a cabo las funciones de eliminación en archivos.../../tomcat/conf/server.xml
", lo que podría causar que la aplicación eliminase uno de sus propios archivos de configuración.Ejemplo 2: El siguiente código utiliza una entrada de un archivo de configuración para determinar el archivo que se debe abrir y devolver al usuario. Si el programa se ejecuta con los privilegios adecuados y los usuarios malintencionados pueden cambiar el archivo de configuración, estos pueden utilizar el programa para leer cualquier archivo del sistema que termine con la extensión
rName := "/usr/local/apfr/reports/" + req.FormValue("fName")
rFile, err := os.OpenFile(rName, os.O_RDWR|os.O_CREATE, 0755)
defer os.Remove(rName);
defer rFile.Close()
...
.txt
.
...
config := ReadConfigFile()
filename := config.fName + ".txt";
data, err := ioutil.ReadFile(filename)
...
fmt.Println(string(data))
../../tomcat/conf/server.xml
", lo que podría causar que la aplicación eliminase uno de sus propios archivos de configuración.Ejemplo 2: el siguiente código utiliza una entrada de un archivo de configuración para determinar el archivo que se debe abrir y reenviar al usuario. Si el programa se ejecuta con los privilegios adecuados y los usuarios malintencionados pueden cambiar el archivo de configuración, estos podrán utilizar el programa para leer cualquier archivo del sistema que termine con la extensión
String rName = request.getParameter("reportName");
File rFile = new File("/usr/local/apfr/reports/" + rName);
...
rFile.delete();
.txt
.
fis = new FileInputStream(cfg.getProperty("sub")+".txt");
amt = fis.read(arr);
out.println(arr);
Example 1
a la plataforma Android.
...
String rName = this.getIntent().getExtras().getString("reportName");
File rFile = getBaseContext().getFileStreamPath(rName);
...
rFile.delete();
...
../../tomcat/conf/server.xml
", lo que podría causar que la aplicación eliminase uno de sus propios archivos de configuración.Ejemplo 2: el código siguiente utiliza entrada del almacenamiento local para determinar qué archivo abrir y devolver al usuario. Si los usuarios maliciosos pueden cambiar el contenido del almacenamiento local, pueden utilizar el programa para leer cualquier archivo del sistema que termine con la extensión
...
var reportNameParam = "reportName=";
var reportIndex = document.indexOf(reportNameParam);
if (reportIndex < 0) return;
var rName = document.URL.substring(reportIndex+reportNameParam.length);
window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) {
fs.root.getFile('/usr/local/apfr/reports/' + rName, {create: false}, function(fileEntry) {
fileEntry.remove(function() {
console.log('File removed.');
}, errorHandler);
}, errorHandler);
}, errorHandler);
.txt
.
...
var filename = localStorage.sub + '.txt';
function oninit(fs) {
fs.root.getFile(filename, {}, function(fileEntry) {
fileEntry.file(function(file) {
var reader = new FileReader();
reader.onloadend = function(e) {
var txtArea = document.createElement('textarea');
txtArea.value = this.result;
document.body.appendChild(txtArea);
};
reader.readAsText(file);
}, errorHandler);
}, errorHandler);
}
window.requestFileSystem(window.TEMPORARY, 1024*1024, oninit, errorHandler);
...
../../tomcat/conf/server.xml
", lo que podría causar que la aplicación eliminase uno de sus propios archivos de configuración.Ejemplo 2: El siguiente código utiliza una entrada de un archivo de configuración para determinar el archivo que se debe abrir y devolver al usuario. Si el programa se ejecuta con los privilegios adecuados y los usuarios malintencionados pueden cambiar el archivo de configuración, estos pueden utilizar el programa para leer cualquier archivo del sistema que termine con la extensión
val rName: String = request.getParameter("reportName")
val rFile = File("/usr/local/apfr/reports/$rName")
...
rFile.delete()
.txt
.
fis = FileInputStream(cfg.getProperty("sub").toString() + ".txt")
amt = fis.read(arr)
out.println(arr)
Example 1
a la plataforma Android.
...
val rName: String = getIntent().getExtras().getString("reportName")
val rFile: File = getBaseContext().getFileStreamPath(rName)
...
rFile.delete()
...
- (NSData*) testFileManager {
NSString *rootfolder = @"/Documents/";
NSString *filePath = [rootfolder stringByAppendingString:[fileName text]];
NSFileManager *fm = [NSFileManager defaultManager];
return [fm contentsAtPath:filePath];
}
../../tomcat/conf/server.xml
", lo que podría causar que la aplicación eliminase uno de sus propios archivos de configuración.Ejemplo 2: el siguiente código utiliza una entrada de un archivo de configuración para determinar el archivo que se debe abrir y reenviar al usuario. Si el programa se ejecuta con los privilegios adecuados y los usuarios malintencionados pueden cambiar el archivo de configuración, estos podrán utilizar el programa para leer cualquier archivo del sistema que termine con la extensión
$rName = $_GET['reportName'];
$rFile = fopen("/usr/local/apfr/reports/" . rName,"a+");
...
unlink($rFile);
.txt
.
...
$filename = $CONFIG_TXT['sub'] . ".txt";
$handle = fopen($filename,"r");
$amt = fread($handle, filesize($filename));
echo $amt;
...
../../tomcat/conf/server.xml
", lo que podría causar que la aplicación eliminase uno de sus propios archivos de configuración.Ejemplo 2: el siguiente código utiliza una entrada de un archivo de configuración para determinar el archivo que se debe abrir y reenviar al usuario. Si el programa se ejecuta con los privilegios adecuados y los usuarios malintencionados pueden cambiar el archivo de configuración, estos podrán utilizar el programa para leer cualquier archivo del sistema que termine con la extensión
rName = req.field('reportName')
rFile = os.open("/usr/local/apfr/reports/" + rName)
...
os.unlink(rFile);
.txt
.
...
filename = CONFIG_TXT['sub'] + ".txt";
handle = os.open(filename)
print handle
...
../../tomcat/conf/server.xml
", lo que podría causar que la aplicación eliminase uno de sus propios archivos de configuración.Ejemplo 2: el siguiente código utiliza una entrada de un archivo de configuración para determinar el archivo que se debe abrir y reenviar al usuario. Si el programa se ejecuta con los privilegios adecuados y los usuarios malintencionados pueden cambiar el archivo de configuración, estos podrán utilizar el programa para leer cualquier archivo del sistema que termine con la extensión
rName = req['reportName']
File.delete("/usr/local/apfr/reports/#{rName}")
.txt
.
...
fis = File.new("#{cfg.getProperty("sub")}.txt")
amt = fis.read
puts amt
../../tomcat/conf/server.xml
", lo que podría causar que la aplicación eliminase uno de sus propios archivos de configuración.Ejemplo 2: el siguiente código utiliza una entrada de un archivo de configuración para determinar el archivo que se debe abrir y reenviar al usuario. Si el programa se ejecuta con los privilegios adecuados y los usuarios malintencionados pueden cambiar el archivo de configuración, estos podrán utilizar el programa para leer cualquier archivo del sistema que termine con la extensión
def readFile(reportName: String) = Action { request =>
val rFile = new File("/usr/local/apfr/reports/" + reportName)
...
rFile.delete()
}
.txt
.
val fis = new FileInputStream(cfg.getProperty("sub")+".txt")
val amt = fis.read(arr)
out.println(arr)
func testFileManager() -> NSData {
let filePath : String = "/Documents/\(fileName.text)"
let fm : NSFileManager = NSFileManager.defaultManager()
return fm.contentsAtPath(filePath)
}
..\conf\server.xml
", lo que podría causar que la aplicación eliminase uno de sus propios archivos de configuración.Ejemplo 2: el siguiente código utiliza una entrada de un archivo de configuración para determinar el archivo que se debe abrir y reenviar al usuario. Si el programa se ejecuta con los privilegios adecuados y los usuarios malintencionados pueden cambiar el archivo de configuración, estos podrán utilizar el programa para leer cualquier archivo del sistema que termine con la extensión
Dim rName As String
Dim fso As New FileSystemObject
Dim rFile as File
Set rName = Request.Form("reportName")
Set rFile = fso.GetFile("C:\reports\" & rName)
...
fso.DeleteFile("C:\reports\" & rName)
...
.txt
.
Dim fileName As String
Dim tsContent As String
Dim ts As TextStream
Dim fso As New FileSystemObject
fileName = GetPrivateProfileString("MyApp", "sub", _
"", value, Len(value), _
App.Path & "\" & "Config.ini")
...
Set ts = fso.OpenTextFile(fileName,1)
tsContent = ts.ReadAll
Response.Write tsContent
...
Path.Combine
utiliza varias rutas de archivos como argumentos. Las concatena para obtener la ruta completa, lo cual habitualmente viene seguido de una llamada a read()
o write()
para ese archivo. La documentación describe varios escenarios distintos según si el primer parámetro o los restantes son rutas absolutas. Si se proporciona una ruta absoluta para el segundo parámetro o los restantes, Path.Combine()
devolverá esa ruta absoluta. Los parámetros anteriores se ignorarán. Las implicaciones en este caso son significativas para aplicaciones que tienen código similar al ejemplo siguiente.
// Called with user-controlled data
public static bytes[] getFile(String filename)
{
String imageDir = "\\FILESHARE\images\";
filepath = Path.Combine(imageDir, filename);
return File.ReadAllBytes(filepath);
}
C:\\inetpub\wwwroot\web.config
), un atacante puede controlar qué archivo devuelve la aplicación.
...
" Add Binary File to
CALL METHOD lr_abap_zip->add
EXPORTING
name = p_ifile
content = lv_bufferx.
" Read Binary File to
CALL METHOD lr_abap_zip->get
EXPORTING
name = p_ifile
IMPORTING
content = lv_bufferx2.
...
Example 1
, no se valida p_ifile
antes de llevar a cabo las funciones de lectura/escritura en los datos de la entrada. Si el archivo ZIP se encontró originalmente en el directorio "/tmp/
" de un equipo basado en Unix, una entrada ZIP era "../etc/hosts
" y la aplicación se ejecutó con los permisos necesarios, se sobrescribe el archivo hosts
del sistema. A su vez, esto permite que el tráfico del equipo vaya donde el usuario malintencionado desea, por ejemplo, a su propio equipo.
public static void UnzipFile(ZipArchive archive, string destDirectory)
{
foreach (var entry in archive.Entries)
{
string file = entry.FullName;
if (!string.IsNullOrEmpty(file))
{
string destFileName = Path.Combine(destDirectory, file);
entry.ExtractToFile(destFileName, true);
}
}
}
Example 1
, no se valida entry.FullName
antes de llevar a cabo las operaciones de lectura/escritura en los datos de la entrada. Si el archivo zip se colocase originalmente en el directorio "C:\TEMP
", un nombre de entrada de zip incluyera "segmentos ..\
" y la aplicación se ejecutase con los permisos necesarios, dicho archivo podría sobrescribir de forma arbitraria algunos archivos de sistema.
func Unzip(src string, dest string) ([]string, error) {
var filenames []string
r, err := zip.OpenReader(src)
if err != nil {
return filenames, err
}
defer r.Close()
for _, f := range r.File {
// Store filename/path for returning and using later on
fpath := filepath.Join(dest, f.Name)
filenames = append(filenames, fpath)
if f.FileInfo().IsDir() {
// Make Folder
os.MkdirAll(fpath, os.ModePerm)
continue
}
// Make File
if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
return filenames, err
}
outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return filenames, err
}
rc, err := f.Open()
if err != nil {
return filenames, err
}
_, err = io.Copy(outFile, rc)
// Close the file without defer to close before next iteration of loop
outFile.Close()
rc.Close()
if err != nil {
return filenames, err
}
}
return filenames, nil
}
Example 1
, no se valida f.Name
antes de llevar a cabo las funciones de lectura/escritura en los datos de la entrada. Si el archivo zip se encontrase originalmente en el directorio "/tmp/
" de un equipo basado en Unix, una entrada zip fuese "../etc/hosts
" y la aplicación se ejecutase con los permisos necesarios, se sobrescribiría el archivo hosts
del sistema. A su vez, esto permitiría que el tráfico del equipo fuese donde el usuario malintencionado desease, por ejemplo, a su propio equipo.
private static final int BUFSIZE = 512;
private static final int TOOBIG = 0x640000;
...
public final void unzip(String filename) throws IOException {
FileInputStream fis = new FileInputStream(filename);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis));
ZipEntry zipEntry = null;
int numOfEntries = 0;
long total = 0;
try {
while ((zipEntry = zis.getNextEntry()) != null) {
byte data[] = new byte[BUFSIZE];
int count = 0;
String outFileName = zipEntry.getName();
if (zipEntry.isDirectory()){
new File(outFileName).mkdir(); //create the new directory
continue;
}
FileOutputStream outFile = new FileOutputStream(outFileName);
BufferedOutputStream dest = new BufferedOutputStream(outFile, BUFSIZE);
//read data from Zip, but do not read huge entries
while (total + BUFSIZE <= TOOBIG && (count = zis.read(data, 0, BUFSIZE)) != -1) {
dest.write(data, 0, count);
total += count;
}
...
}
} finally{
zis.close();
}
}
...
Example 1
, no se valida zipEntry.getName()
antes de llevar a cabo las funciones de lectura/escritura en los datos de la entrada. Si el archivo zip se encontrase originalmente en el directorio "/tmp/
" de un equipo basado en Unix, una entrada zip fuese "../etc/hosts
" y la aplicación se ejecutase con los permisos necesarios, se sobrescribiría el archivo hosts
del sistema. A su vez, esto permitiría que el tráfico del equipo fuese donde el usuario malintencionado desease, por ejemplo, a su propio equipo.
var unzipper = require('unzipper');
var fs = require('fs');
var untrusted_zip = getZipFromRequest();
fs.createReadStream(zipPath).pipe(unzipper.Extract({ path: 'out' }));
ZZArchive* archive = [ZZArchive archiveWithURL:[NSURL fileURLWithPath: zipPath] error:&error];
for (ZZArchiveEntry* entry in archive.entries) {
NSString *fullPath = [NSString stringWithFormat: @"%@/%@", destPath, [entry fileName]];
[[entry newDataWithError:nil] writeToFile:newFullPath atomically:YES];
}
Example 1
, no se valida entry.fileName
antes de llevar a cabo las funciones de lectura/escritura en los datos de la entrada. Si el archivo zip se encontrase originalmente en el directorio "Documents/hot_patches
" de una aplicación iOS, y una entrada ZIP fuese "../js/page.js
", dicho archivo podría sobrescribir el archivo page.js
. Esto, a su vez, permitiría al atacante inyectar código malicioso que podría dar lugar a la ejecución de código.
...
$zip = new ZipArchive();
$zip->open("userdefined.zip", ZipArchive::RDONLY);
$zpm = $zip->getNameIndex(0);
$zip->extractTo($zpm);
...
Example 1
, no se valida f.Name
antes de llevar a cabo las funciones de lectura/escritura en los datos de la entrada. Si el archivo zip se encuentra en el directorio "/tmp/
" de un equipo basado en Unix, una entrada ZIP es "../etc/hosts
" y la aplicación se ejecuta con los permisos necesarios, se sobrescribirá el archivo hosts
del sistema. Esto permite que el tráfico del equipo vaya donde el usuario malintencionado desea, por ejemplo, a su propio equipo.
import zipfile
import tarfile
def unzip(archive_name):
zf = zipfile.ZipFile(archive_name)
zf.extractall(".")
zf.close()
def untar(archive_name):
tf = tarfile.TarFile(archive_name)
tf.extractall(".")
tf.close()
Ejemplo 2: El siguiente ejemplo extrae archivos de un archivo zip y los escribe en el disco de forma insegura.
import better.files._
...
val zipPath: File = getUntrustedZip()
val destinationPath = file"out/dest"
zipPath.unzipTo(destination = destinationPath)
import better.files._
...
val zipPath: File = getUntrustedZip()
val destinationPath = file"out/dest"
zipPath.newZipInputStream.mapEntries( (entry : ZipEntry) => {
entry.extractTo(destinationPath, new FileInputStream(entry.getName))
})
Example 2
, no se valida entry.getName
antes de llevar a cabo las funciones de lectura/escritura en los datos de la entrada. Si el archivo zip se encontrase originalmente en el directorio "/tmp/
" de un equipo basado en Unix, una entrada zip fuese "../etc/hosts
" y la aplicación se ejecutase con los permisos necesarios, se sobrescribiría el archivo hosts
del sistema. A su vez, esto permitiría que el tráfico del equipo fuese donde el usuario malintencionado desease, por ejemplo, a su propio equipo.
let archive = try ZZArchive.init(url: URL(fileURLWithPath: zipPath))
for entry in archive.entries {
let fullPath = URL(fileURLWithPath: destPath + "/" + entry.fileName)
try entry.newData().write(to: fullPath)
}
Example 1
, no se valida entry.fileName
antes de llevar a cabo las funciones de lectura/escritura en los datos de la entrada. Si el archivo zip se encontrase originalmente en el directorio "Documents/hot_patches
" de una aplicación iOS, y una entrada ZIP fuese "../js/page.js
", dicho archivo podría sobrescribir el archivo page.js
. Esto, a su vez, permitiría al atacante inyectar código malicioso que podría dar lugar a la ejecución de código.varName
en el siguiente segmento de código ColdFusion, la llamada a SetVariable()
podría sobrescribir cualquier variable arbitraria, incluida #first#
. En este caso, si un valor malintencionado que contenga JavaScript sobrescribe #first#
, el programa es vulnerable a los Cross-Site Scripting.
<cfset first = "User">
<cfscript>
SetVariable(url.varName, url.varValue);
</cfscript>
<cfoutput>
#first#
</cfoutput>
str
en el siguiente segmento de código PHP, la llamada a parse_str()
podría sobrescribir cualquier variable arbitraria, incluida first
. En este caso, si un valor malintencionado que contenga JavaScript sobrescribe first
, el programa es vulnerable a los Cross-Site Scripting.
<?php
$first="User";
...
$str = $_SERVER['QUERY_STRING'];
parse_str($str);
echo $first;
?>
str
en el siguiente segmento de código PHP, la llamada a mb_parse_str()
podría sobrescribir cualquier variable arbitraria, incluida first
. En este caso, si un valor malintencionado que contenga JavaScript sobrescribe first
, el programa es vulnerable a los Cross-Site Scripting.
<?php
$first="User";
...
$str = $_SERVER['QUERY_STRING'];
mb_parse_str($str);
echo $first;
?>
NSPredicate
dinámico con una entrada de un origen que no es de confianza, un usuario malintencionado podría modificar el significado de la instrucción.NSPredicate
especifican cómo debe obtenerse o filtrarse una colección de orígenes como CoreData
del sistema de almacenamiento complejo, una matriz y un diccionario. El lenguaje de consulta proporciona un lenguaje expresivo similar a SQL
para definir condiciones lógicas en las que se debe buscar la colección.NSPredicate
como factor de autenticación para acceder a algunos datos almacenados por la aplicación. Dado que los usuarios pueden proporcionar valores PIN arbitrarios, podrán utilizar un carácter comodín (*
) para eludir la protección PIN.
NSString *pin = [self getPinFromUser];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"pin LIKE %@", pin];
NSPredicate
dinámico con una entrada de un origen que no es de confianza, un usuario malintencionado podría modificar el significado de la instrucción.NSPredicate
especifican cómo debe obtenerse o filtrarse una colección de orígenes como CoreData
del sistema de almacenamiento complejo, una matriz y un diccionario. El lenguaje de consulta proporciona un lenguaje expresivo similar a SQL
para definir condiciones lógicas en las que se debe buscar la colección.NSPredicate
como factor de autenticación para acceder a algunos datos almacenados por la aplicación. Dado que los usuarios pueden proporcionar valores PIN arbitrarios, podrán utilizar un carácter comodín (*
) para eludir la protección PIN.
let pin = getPinFromUser();
let predicate = NSPredicate(format: "pin LIKE '\(pin)'", argumentArray: nil)
...
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.