Aller au contenu

XML Injection

Injection XML

Description

L'injection XML est une vulnérabilité qui découle d'un manque de validation et d'assainissement appropriés des entrées utilisateur avant de les incorporer dans des fichiers de configuration ou des flux XML. Cet oubli permet à un attaquant d'exploiter l'application en injectant du contenu XML malveillant. En manipulant la structure XML ou en introduisant des entités malveillantes, un attaquant peut potentiellement perturber la logique de l'application, altérer les données ou extraire des informations sensibles.

import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'XML Injection Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final TextEditingController _usernameController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();

  @override
  void dispose() {
    _usernameController.dispose();
    _passwordController.dispose();
    super.dispose();
  }

  Future<void> saveConfigFile() async {
    final directory = await getApplicationDocumentsDirectory();
    final configFile = File('${directory.path}/config.xml');

    // Create XML data
    final xmlData = '''
    <root>
      <username>${_usernameController.text}</username>
      <password>${_passwordController.text}</password>
    </root>
    ''';

    // Save the XML to the configuration file
    await configFile.writeAsString(xmlData);
  }

  void login() {
    final username = _usernameController.text;
    final password = _passwordController.text;

    // Perform login logic
    print('Performing login with username: $username and password: $password');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('XML Injection Demo'),
      ),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('Enter username:'),
            TextField(
              controller: _usernameController,
            ),
            SizedBox(height: 16.0),
            Text('Enter password:'),
            TextField(
              controller: _passwordController,
              obscureText: true,
            ),
            SizedBox(height: 16.0),
            ElevatedButton(
              onPressed: () async {
                await saveConfigFile();
                login();
              },
              child: Text('Login'),
            ),
          ],
        ),
      ),
    );
  }
}
import SwiftUI

struct ContentView: View {
    @State private var username = ""
    @State private var password = ""

    var body: some View {
        VStack {
            Text("Enter username:")
            TextField("Username", text: $username)
                .textFieldStyle(RoundedBorderTextFieldStyle())

            Text("Enter password:")
            SecureField("Password", text: $password)
                .textFieldStyle(RoundedBorderTextFieldStyle())

            Button(action: {
                saveConfigFile()
                login()
            }) {
                Text("Login")
            }
        }
        .padding()
    }

    func saveConfigFile() {
        // Create XML data
        let xmlData = '''
        <root>
          <username>\(username)</username>
          <password>\(password)</password>
        </root>
        '''

        // Save the XML to the configuration file
        let configFilePath = "config.xml"
        do {
            try xmlData.write(toFile: configFilePath, atomically: true, encoding: .utf8)
        } catch {
            print("Failed to write XML to file: \(error)")
        }
    }

    func login() {
        // Perform login logic
        print("Performing login with username: \(username) and password: \(password)")
    }
}

@main
struct XMLInjectionApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import java.io.File
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult

class MainActivity : AppCompatActivity() {

    private lateinit var usernameEditText: EditText
    private lateinit var passwordEditText: EditText
    private lateinit var loginButton: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        usernameEditText = findViewById(R.id.usernameEditText)
        passwordEditText = findViewById(R.id.passwordEditText)
        loginButton = findViewById(R.id.loginButton)

        loginButton.setOnClickListener {
            val username = usernameEditText.text.toString()
            val password = passwordEditText.text.toString()

            saveConfigFile(username, password)
            login(username, password)
        }
    }

    private fun saveConfigFile(username: String, password: String) {
        val configFilePath = "config.xml"

        try {
            val configFile = File(configFilePath)
            val xmlData = configFile.readText()

            val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
            val doc = docBuilder.parse(xmlData.byteInputStream())
            doc.documentElement.normalize()

            val root = doc.documentElement

            root.getElementsByTagName("username").item(0).textContent = username
            root.getElementsByTagName("password").item(0).textContent = password

            val transformer = TransformerFactory.newInstance().newTransformer()
            val result = StreamResult(configFile)
            val source = DOMSource(doc)
            transformer.transform(source, result)
        } catch (e: Exception) {
            Toast.makeText(this, "Failed to save configuration file", Toast.LENGTH_SHORT).show()
        }
    }

    private fun login(username: String, password: String) {
        // Perform login logic
        Toast.makeText(this, "Performing login with username: $username and password: $password", Toast.LENGTH_SHORT).show()
    }
}

Recommandation

Pour atténuer les vulnérabilités d'injection XML, considérez les points suivants :

  • Mettez en œuvre des techniques appropriées de validation et d'assainissement des entrées.
  • Validez les entrées utilisateur par rapport aux formats attendus et assainissez-les en supprimant ou en échappant les caractères qui pourraient être interprétés comme des balises ou des entités XML.
  • Envisagez d'utiliser des bibliothèques d'analyse XML sécurisées ou des API qui gèrent la liaison de données de manière sécurisée.
import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:xml/xml.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'XML Injection Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final TextEditingController _usernameController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();

  @override
  void dispose() {
    _usernameController.dispose();
    _passwordController.dispose();
    super.dispose();
  }

  Future<void> saveConfigFile() async {
    final directory = await getApplicationDocumentsDirectory();
    final configFile = File('${directory.path}/config.xml');

    // Create XML data
    final builder = XmlBuilder();
    builder.processing('xml', 'version="1.0"');
    builder.element('root', nest: () {
      builder.element('username', nest: _usernameController.text);
      builder.element('password', nest: _passwordController.text);
    });
    final xmlData = builder.build().toXmlString(pretty: true);

    // Save the XML to the configuration file
    await configFile.writeAsString(xmlData);
  }

  void login() {
    final username = _usernameController.text;
    final password = _passwordController.text;

    // Perform login logic
    print('Performing login with username: $username and password: $password');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('XML Injection Demo'),
      ),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('Enter username:'),
            TextField(
              controller: _usernameController,
            ),
            SizedBox(height: 16.0),
            Text('Enter password:'),
            TextField(
              controller: _passwordController,
              obscureText: true,
            ),
            SizedBox(height: 16.0),
            ElevatedButton(
              onPressed: () async {
                await saveConfigFile();
                login();
              },
              child: Text('Login'),
            ),
          ],
        ),
      ),
    );
  }
}
import Foundation

func main() {
    let configFilePath = "config.xml"

    // Load configuration file
    guard let configFileURL = Bundle.main.url(forResource: "config", withExtension: "xml") else {
        print("Failed to load configuration file")
        return
    }

    guard let xmlData = try? Data(contentsOf: configFileURL) else {
        print("Failed to read configuration file")
        return
    }

    // Parse XML
    let xmlDoc: XMLDocument
    do {
        xmlDoc = try XMLDocument(data: xmlData, options: .documentTidyXML)
    } catch {
        print("Failed to parse XML: \(error)")
        return
    }

    guard let root = xmlDoc.rootElement() else {
        print("Invalid XML structure")
        return
    }

    // Read configuration values from user input
    guard let username = getUserInput(prompt: "Enter username:"), let password = getUserInput(prompt: "Enter password:") else {
        print("Invalid input")
        return
    }

    // Update configuration values in XML
    if let usernameElement = root.elements(forName: "username").first {
        usernameElement.stringValue = username
    }

    if let passwordElement = root.elements(forName: "password").first {
        passwordElement.stringValue = password
    }

    // Save the modified XML back to the configuration file
    guard let modifiedXmlData = xmlDoc.xmlData(options: .nodePrettyPrint) else {
        print("Failed to generate modified XML")
        return
    }

    do {
        try modifiedXmlData.write(to: configFileURL)
    } catch {
        print("Failed to write modified XML to file: \(error)")
        return
    }

    // Use the configuration values
    login(username: username, password: password)
}

func getUserInput(prompt: String) -> String? {
    print(prompt, terminator: " ")
    return readLine()
}

func login(username: String, password: String) {
    // Perform login logic
    print("Performing login with username: \(username) and password: \(password)")
}

// Start the program
main()
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import org.w3c.dom.Document
import java.io.File
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult

class MainActivity : AppCompatActivity() {

    private lateinit var usernameEditText: EditText
    private lateinit var passwordEditText: EditText
    private lateinit var loginButton: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        usernameEditText = findViewById(R.id.usernameEditText)
        passwordEditText = findViewById(R.id.passwordEditText)
        loginButton = findViewById(R.id.loginButton)

        loginButton.setOnClickListener {
            val username = usernameEditText.text.toString()
            val password = passwordEditText.text.toString()

            saveConfigFile(username, password)
            login(username, password)
        }
    }

    private fun saveConfigFile(username: String, password: String) {
        val configFilePath = "config.xml"

        try {
            val configFile = File(configFilePath)
            val xmlDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument()

            val rootElement = xmlDoc.createElement("root")
            xmlDoc.appendChild(rootElement)

            val usernameElement = xmlDoc.createElement("username")
            usernameElement.appendChild(xmlDoc.createTextNode(username))
            rootElement.appendChild(usernameElement)

            val passwordElement = xmlDoc.createElement("password")
            passwordElement.appendChild(xmlDoc.createTextNode(password))
            rootElement.appendChild(passwordElement)

            val transformer = TransformerFactory.newInstance().newTransformer()
            val result = StreamResult(configFile)
            val source = DOMSource(xmlDoc)
            transformer.transform(source, result)
        } catch (e: Exception) {
            Toast.makeText(this, "Failed to save configuration file", Toast.LENGTH_SHORT).show()
        }
    }

    private fun login(username: String, password: String) {
        // Perform login logic
        Toast.makeText(this, "Performing login with username: $username and password: $password", Toast.LENGTH_SHORT).show()
    }
}

Liens

Normes

  • OWASP_MASVS_L1:
    • MSTG_PLATFORM_2
  • OWASP_MASVS_L2:
    • MSTG_PLATFORM_2
  • PCI_STANDARDS:
    • REQ_2_2
    • REQ_6_2
    • REQ_6_3
    • REQ_11_3
  • OWASP_MASVS_v2_1:
    • MASVS_CODE_4
  • SOC2_CONTROLS:
    • CC_2_1
    • CC_3_4
    • CC_4_1
    • CC_7_1
    • CC_7_2
    • CC_7_4
    • CC_7_5