@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}
]
)
select()
クエリを動的に構築し、実行します。ユーザーは結果を並び替えるカラムを指定することもできます。アプリケーションがすでに認証されており、このコードセグメントの前に customerID
の値を設定していると仮定します。
...
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
および price
に単一引用符が含まれない場合に限ってクエリは正しく動作します。しかし、攻撃者が、"Fax Machines' or productCategory = \"
" という文字列を productCategory
に、文字列 "\" order by 'price
" を sortColumn
に指定すると、クエリは次のようになります。
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
で必要な認証を回避でき、すべての顧客について 'Fax Machines'
と一致する領収書のレコードを表示できます。customerID
の値を設定していると仮定します。
...
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
を連結して、動的に構築されます。そのため、productCategory
および sortColumn
に単一引用符が含まれない場合にのみ、クエリは正しく動作します。攻撃者が、"Fax Machines' or productCategory = \"
" という文字列を productCategory
に、文字列 "\" order by 'price
" を sortColumn
に指定すると、クエリは次のようになります。
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
で必要な認証を回避でき、すべての顧客について 'Fax Machines'
と一致する領収書のレコードを表示できます。
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
では、width
の値が 10 であるため、開発者は box.area
が 10 の倍数のランダム整数であると予想します。しかし実際は、常にハードコーディングされた 0 の値です。コンパイル時定数で宣言された static final フィールドは最初に初期化され、順番に実行されます。これは、height
はコンパイル時定数ではないため box
の宣言の後に宣言され、コンストラクターはフィールド 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.
java.text.Format
にあるメソッド parse()
および format()
には、あるユーザーが別のユーザーのデータを表示できるという設計上の不備が含まれています。java.text.Format
にあるメソッド parse()
および format()
には、あるユーザーが別のユーザーのデータを表示できるという Race Condition が含まれています。
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()
の実装にある Race Condition のため、最初のスレッドの日付が2つ目のスレッドの出力に表示されています。
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
" to name
Jane
" to name
Jane, thanks for visiting!
"Jane, thanks for visiting!
"
public class ConnectionManager {
private static Connection conn = initDbConn();
...
}
null
であるかを確認する前に、null
の可能性があるポインタを間接参照する場合です。チェック後間接参照のエラーが発生するのは、プログラムが null
に対して明示的チェックを実行したにも関わらず、null
であることが判明しているポインタを間接参照した場合です。この種のエラーの多くは、タイプミスかプログラマの不注意が原因です。格納後間接参照のエラーは、プログラムが明示的にポインタを null
に設定し、後でそのポインタを間接参照した場合に発生します。このエラーは通常、変数の宣言時にプログラマがその変数を null
に初期化したことが原因で発生します。foo
が null
であることを確認してから、それを誤って間接参照しています。foo
が if
ステートメントでチェックされたときに null
になっていると、null
間接参照 が発生し、これが NULL ポインター例外の原因となります。例 2: 次のコードでは、プログラマは変数
if (foo is null) {
foo.SetBar(val);
...
}
foo
は null
ではないと仮定しており、オブジェクトを間接参照することによってこの仮定を確認しています。しかし、その後プログラマが foo
と null
を比較したときに、この仮定は成り立たなくなります。foo
が if
ステートメントでチェックされたときに null
であるとすれば、間接参照されたときにも null
である可能性があり、これが NULL ポインタ例外の原因となる場合があります。間接参照が安全でないか、後で実行するチェックが不要であるかのいずれかです。例 3: 次のコードでは、プログラマは明示的に変数
foo.SetBar(val);
...
if (foo is not null) {
...
}
foo
を null
に設定しています。続いて、オブジェクトの null
値をチェックする前に foo
を間接参照しています。
Foo foo = null;
...
foo.SetBar(val);
...
}
null
であるかを確認する前に、null
の可能性があるポインタを間接参照する場合です。チェック後間接参照のエラーが発生するのは、プログラムが null
に対して明示的チェックを実行したにも関わらず、null
であることが判明しているポインタを間接参照した場合です。この種のエラーの多くは、タイプミスかプログラマの不注意が原因です。格納後間接参照のエラーは、プログラムが明示的にポインタを null
に設定し、後でそのポインタを間接参照した場合に発生します。このエラーは通常、変数の宣言時にプログラマがその変数を null
に初期化したことが原因で発生します。ptr
は NULL
ではないと仮定してします。この仮定は、プログラマがポインタを間接参照したときに明らかになります。その後プログラマが ptr
と NULL
を比較したときに、この仮定は成り立たなくなります。ptr
が if
ステートメントでチェックされたときに NULL
であるとすれば、間接参照されたときにも NULL
である可能性があり、これがセグメンテーション違反の原因となる場合があります。例 2: 次のコードでは、プログラマは変数
ptr->field = val;
...
if (ptr != NULL) {
...
}
ptr
が NULL
であることを確認してから、続いてそれを誤って間接参照しています。ptr
が if
ステートメントでチェックされたときに NULL
になっていると、null
Dereference が発生し、これがセグメンテーション違反の原因となります。例 3: 次のコードでは、プログラマが文字列
if (ptr == null) {
ptr->field = val;
...
}
'\0'
(実際は 0) または NULL
を忘れたため、NULL ポインタを間接参照し、セグメンテーション違反を引き起こしています。例 4: 次のコードでは、プログラマは明示的に変数
if (ptr == '\0') {
*ptr = val;
...
}
ptr
を NULL
に設定しています。続いて、オブジェクトの null
値をチェックする前に ptr
を間接参照しています。
*ptr = NULL;
...
ptr->field = val;
...
}
null
に対して明示的チェックを実行したにも関わらず、null
であることが判明しているオブジェクトを間接参照した場合です。この種のエラーの多くは、タイプミスかプログラマの不注意が原因です。foo
が null
であることを確認してから、続いてそれを誤って間接参照しています。foo
が if
ステートメントでチェックされたときに null
になっていると、null
Dereference が発生し、これが NULL ポインタ例外の原因となります。
if (foo == null) {
foo.setBar(val);
...
}
Content-Disposition
ヘッダーの設定が間違っている。これにより、HTTP レスポンスの Content-Type
ヘッダーと Content-Disposition
ヘッダーまたはそのいずれかが攻撃者によって操作される可能性があるほか、ターゲット アプリケーションに、デフォルトではブラウザーにレンダリングされない Content-Type
が含まれる場合があります。ContentNegotiationManager
を使用して、さまざまなレスポンス形式を動的に生成している場合、RFD 攻撃に必要な条件が整います。ContentNegotiationManager
は、リクエスト パス拡張に基づいてレスポンス形式を決定し、Java Activation Framework (JAF) を使用してクライアントが要求した形式により一致する Content-Type
を探すように設定されます。また、リクエストの Accept
ヘッダーで送信されるメディア タイプを介してレスポンス コンテンツ タイプを指定することをクライアントに許可します。例 2: 次の例のアプリケーションは、リクエストの
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="true" />
<property name="useJaf" value="true" />
</bean>
Accept
ヘッダーによってレスポンス コンテンツ タイプを決定するように設定されています。
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="ignoreAcceptHeader" value="false" />
</bean>
ContentNegotiationManagerFactoryBean
プロパティのデフォルト値は次のとおりです。useJaf
: true
favorPathExtension
: true
ignoreAcceptHeader
: false
Example 1
の構成では、攻撃者が次のような悪意のある URL を作成することが可能です。ContentNegotiationManager
は、Java Activation Framework (activity.jar がクラスパスにある場合) を使用して、指定したファイル拡張子についてメディア タイプを解決しようとし、それに合わせてレスポンスの ContentType
ヘッダーを設定します。この例では、ファイル拡張子は "bat" で、Content-Type
ヘッダーは application/x-msdownload
です (正確な Content-Type
は、サーバーの OS と JAF の設定によって異なります)。その結果、この悪意のある URL にアクセスした被害者のマシンは、攻撃者が操作したコンテンツを含む ".bat" ファイルのダウンロードを自動的に開始します。その後、このファイルが実行されると、被害者のマシンは攻撃者のペイロードで指定されたコマンドを実行します。
...
host_name = request->get_form_field( 'host' ).
CALL FUNCTION 'FTP_CONNECT'
EXPORTING
USER = user
PASSWORD = password
HOST = host_name
RFC_DESTINATION = 'SAPFTP'
IMPORTING
HANDLE = mi_handle
EXCEPTIONS
NOT_CONNECTED = 1
OTHERS = 2.
...
int rPort = Int32.Parse(Request.Item("rPort"));
...
IPEndPoint endpoint = new IPEndPoint(address,rPort);
socket = new Socket(endpoint.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
socket.Connect(endpoint);
...
...
char* rPort = getenv("rPort");
...
serv_addr.sin_port = htons(atoi(rPort));
if (connect(sockfd,&serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
...
...
ACCEPT QNAME.
EXEC CICS
READQ TD
QUEUE(QNAME)
INTO(DATA)
LENGTH(LDATA)
END-EXEC.
...
ServerSocket
オブジェクトを作成し、HTTP リクエストから読み取ったポート番号を使用してソケットを作成します。
<cfobject action="create" type="java" class="java.net.ServerSocket" name="myObj">
<cfset srvr = myObj.init(#url.port#)>
<cfset socket = srvr.accept()>
Passing user input to objects imported from other languages can be very dangerous.
final server = await HttpServer.bind('localhost', 18081);
server.listen((request) async {
final remotePort = headers.value('port');
final serverSocket = await ServerSocket.bind(host, remotePort as int);
final httpServer = HttpServer.listenOn(serverSocket);
});
...
func someHandler(w http.ResponseWriter, r *http.Request){
r.parseForm()
deviceName := r.FormValue("device")
...
syscall.BindToDevice(fd, deviceName)
}
String remotePort = request.getParameter("remotePort");
...
ServerSocket srvr = new ServerSocket(remotePort);
Socket skt = srvr.accept();
...
WebView
にロードします。
...
WebView webview = new WebView(this);
setContentView(webview);
String url = this.getIntent().getExtras().getString("url");
webview.loadUrl(url);
...
var socket = new WebSocket(document.URL.indexOf("url=")+20);
...
char* rHost = getenv("host");
...
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)rHost, 80, &readStream, &writeStream);
...
<?php
$host=$_GET['host'];
$dbconn = pg_connect("host=$host port=1234 dbname=ticketdb");
...
$result = pg_prepare($dbconn, "my_query", 'SELECT * FROM pricelist WHERE name = $1');
$result = pg_execute($dbconn, "my_query", array("ticket"));
?>
...
filename := SUBSTR(OWA_UTIL.get_cgi_env('PATH_INFO'), 2);
WPG_DOCLOAD.download_file(filename);
...
host=request.GET['host']
dbconn = db.connect(host=host, port=1234, dbname=ticketdb)
c = dbconn.cursor()
...
result = c.execute('SELECT * FROM pricelist')
...
def controllerMethod = Action { request =>
val result = request.getQueryString("key").map { key =>
val user = db.getUser()
cache.set(key, user)
Ok("Cached Request")
}
Ok("Done")
}
...
func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool {
var inputStream : NSInputStream?
var outputStream : NSOutputStream?
...
var readStream : Unmanaged<CFReadStream>?
var writeStream : Unmanaged<CFWriteStream>?
let rHost = getQueryStringParameter(url.absoluteString, "host")
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, rHost, 80, &readStream, &writeStream);
...
}
func getQueryStringParameter(url: String?, param: String) -> String? {
if let url = url, urlComponents = NSURLComponents(string: url), queryItems = (urlComponents.queryItems as? [NSURLQueryItem]) {
return queryItems.filter({ (item) in item.name == param }).first?.value!
}
return nil
}
...
...
Begin MSWinsockLib.Winsock tcpServer
...
Dim Response As Response
Dim Request As Request
Dim Session As Session
Dim Application As Application
Dim Server As Server
Dim Port As Variant
Set Response = objContext("Response")
Set Request = objContext("Request")
Set Session = objContext("Session")
Set Application = objContext("Application")
Set Server = objContext("Server")
Set Port = Request.Form("port")
...
tcpServer.LocalPort = Port
tcpServer.Accept
...
@ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpAdvice() {
super("callback");
}
}
GET /api/latest.json?callback=myCallbackFunction
などのリクエストの場合、コントローラー メソッドは、次のようなレスポンスを生成します。
HTTP/1.1 200 Ok
Content-Type: application/json; charset=utf-8
Date: Tue, 12 Dec 2017 16:16:04 GMT
Server: nginx/1.12.1
Content-Length: 225
Connection: Close
myCallbackFunction({<json>})
Script
タグを使用することができ、その結果、myCallbackFunction
関数が実行されます。 攻撃者は、異なるコールバック名を使用して DOM を参照し、操作する可能性があります。 たとえば opener.document.body.someElemnt.firstChild.nextElementSibling.submit
を使用してターゲット ページにフォームを配置し、送信することができます。
def myJSONPService(callback: String) = Action {
val json = getJSONToBeReturned()
Ok(Jsonp(callback, json))
}
GET /api/latest.json?callback=myCallbackFunction
などのリクエストの場合、Example 1
のコントローラー メソッドは、次のようなレスポンスを生成します。
HTTP/1.1 200 Ok
Content-Type: application/json; charset=utf-8
Date: Tue, 12 Dec 2017 16:16:04 GMT
Server: nginx/1.12.1
Content-Length: 225
Connection: Close
myCallbackFunction({<json>})
Script
タグを使用することができ、その結果、myCallbackFunction
関数が実行されます。 攻撃者は、異なるコールバック名を使用して DOM を参照し、操作する可能性があります。 たとえば opener.document.body.someElemnt.firstChild.nextElementSibling.submit
を使用してターゲット ページにフォームを配置し、送信することができます。
...
lv_uri = request->get_form_field( 'uri' ).
CALL METHOD cl_http_utility=>set_request_uri
EXPORTING
request = lo_request
uri = lv_uri.
...
http
や https
とは異なるプロトコルを使用することが可能になります。
...
PageReference ref = ApexPages.currentPage();
Map<String,String> params = ref.getParameters();
HttpRequest req = new HttpRequest();
req.setEndpoint(params.get('url'));
HTTPResponse res = new Http().send(req);
http
や https
とは異なるプロトコルを使用することが可能になります。
string url = Request.Form["url"];
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(url);
http
や https
とは異なるプロトコルを使用することが可能になります。
char *url = maliciousInput();
CURL *curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, url);
CURLcode res = curl_easy_perform(curl);
http
や https
とは異なるプロトコルを使用することが可能になります。
...
final server = await HttpServer.bind('localhost', 18081);
server.listen((request) async {
final headers = request.headers;
final url = headers.value('url');
final client = IOClient();
final response = await client.get(Uri.parse(url!));
...
}
http
や https
とは異なるプロトコルを使用することが可能になります。
url := request.Form.Get("url")
res, err =: http.Get(url)
...
http
や https
とは異なる次のようなプロトコルを使用することが可能になります。
String url = request.getParameter("url");
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response1 = httpclient.execute(httpGet);
http
や https
とは異なるプロトコルを使用することが可能になります。
var http = require('http');
var url = require('url');
function listener(request, response){
var request_url = url.parse(request.url, true)['query']['url'];
http.request(request_url)
...
}
...
http.createServer(listener).listen(8080);
...
http
や https
とは異なるプロトコルを使用することが可能になります。
val url: String = request.getParameter("url")
val httpclient: CloseableHttpClient = HttpClients.createDefault()
val httpGet = HttpGet(url)
val response1: CloseableHttpResponse = httpclient.execute(httpGet)
http
や https
とは異なるプロトコルを使用することが可能になります。
$url = $_GET['url'];
$c = curl_init();
curl_setopt($c, CURLOPT_POST, 0);
curl_setopt($c,CURLOPT_URL,$url);
$response=curl_exec($c);
curl_close($c);
http
や https
とは異なるプロトコルを使用することが可能になります。
url = request.GET['url']
handle = urllib.urlopen(url)
http
や https
とは異なるプロトコルを使用することが可能になります。
url = req['url']
Net::HTTP.get(url)
http
や https
とは異なるプロトコルを使用することが可能になります。
def getFile(url: String) = Action { request =>
...
val url = request.body.asText.getOrElse("http://google.com")
ws.url(url).get().map { response =>
Ok(s"Request sent to $url")
}
...
}
http
や https
とは異なるプロトコルを使用することが可能になります。
// Set up the context data
VelocityContext context = new VelocityContext();
context.put( "name", user.name );
// Load the template
String template = getUserTemplateFromRequestBody(request);
RuntimeServices runtimeServices = RuntimeSingleton.getRuntimeServices();
StringReader reader = new StringReader(template);
SimpleNode node = runtimeServices.parse(reader, "myTemplate");
template = new Template();
template.setRuntimeServices(runtimeServices);
template.setData(node);
template.initDocument();
// Render the template with the context data
StringWriter sw = new StringWriter();
template.merge( context, sw );
Example 1
では Velocity
をテンプレート エンジンとして使用しています。そのエンジンでは、攻撃者は次のテンプレートを送信してサーバー上で任意のコマンドを実行できます。
$name.getClass().forName("java.lang.Runtime").getRuntime().exec(<COMMAND>)
app.get('/', function(req, res){
var template = _.template(req.params['template']);
res.write("<html><body><h2>Hello World!</h2>" + template() + "</body></html>");
});
Example 1
では、Node.js
アプリケーション内で Underscore.js
をテンプレート エンジンとして使用しています。そのエンジンでは、攻撃者は次のテンプレートを送信してサーバー上で任意のコマンドを実行できます。
<% cp = process.mainModule.require('child_process');cp.exec(<COMMAND>); %>
Jinja2
テンプレート エンジンを使用してレンダーする方法を示しています。
from django.http import HttpResponse
from jinja2 import Template as Jinja2_Template
from jinja2 import Environment, DictLoader, escape
def process_request(request):
# Load the template
template = request.GET['template']
t = Jinja2_Template(template)
name = source(request.GET['name'])
# Render the template with the context data
html = t.render(name=escape(name))
return HttpResponse(html)
Example 1
では Jinja2
をテンプレート エンジンとして使用しています。そのエンジンでは、攻撃者は次のテンプレートを送信してサーバーから任意のファイルを読み取ることができます。例 2: 次の例は、テンプレートを HTTP リクエストから取得し、
template={{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}
Django
テンプレート エンジンを使用してレンダーする方法を示しています。
from django.http import HttpResponse
from django.template import Template, Context, Engine
def process_request(request):
# Load the template
template = source(request.GET['template'])
t = Template(template)
user = {"name": "John", "secret":getToken()}
ctx = Context(locals())
html = t.render(ctx)
return HttpResponse(html)
Example 2
では Django
をテンプレート エンジンとして使用しています。そのエンジンでは、攻撃者は任意のコマンドを実行できませんが、テンプレート コンテキストにあるすべてのオブジェクトにアクセスできます。この例では、シークレット トークンをコンテキストで入手できるため、攻撃者によって漏洩される可能性があります。
<http auto-config="true">
...
<session-management session-fixation-protection="none"/>
</http>
Example 1
では、攻撃者は明らかに直接的な方法を使用しており、あまり知られていない Web サイトまでには攻撃の手を広げていません。ただし、安心は禁物です。攻撃者はこの攻撃手段の限界をバイパスするための七つ道具を持っています。攻撃者が最も一般的に使用するテクニックは、ターゲット サイトにある Cross-Site Scripting や HTTP レスポンス スプリッティングといった脆弱性を利用することです [1]。攻撃者は攻撃対象を操作して、JavaScript などのコードを攻撃対象のブラウザーに反映するなど悪意あるリクエストを脆弱なアプリケーションに送信するように仕向けて cookie を作成し、攻撃者の支配下にあるセッション ID を再使用させることができます。bank.example.com
や recipes.example.com
のように、多数のアプリケーションが同一の最上位ドメインに存在する場合、1 つのアプリケーションに脆弱性があると、攻撃者はセッション ID が固定化された cookie を設定して、ドメイン example.com
上のあらゆるアプリケーションとの間のすべての通信で使用されるようにできます [2]。use_strict_mode
属性を無効にしています。
ini_set("session.use_strict_mode", "0");
@SessionAttributes
のアノテーションが付けられているクラスは、Spring がセッション オブジェクトのモデル属性への変更を複製することを意味します。攻撃者が任意の値をモデル属性内に格納できる場合、変更は、その変更がアプリケーションにより信頼されるセッション オブジェクトに複製されます。そのセッション属性が、ユーザーが修正できないはずの信頼できるデータにより初期化される場合、攻撃者は、セッションを混乱させる攻撃を実行してアプリケーションのロジックを悪用できる可能性があります。
@Controller
@SessionAttributes("user")
public class HomeController {
...
@RequestMapping(value= "/auth", method=RequestMethod.POST)
public String authHandler(@RequestParam String username, @RequestParam String password, RedirectAttributes attributes, Model model) {
User user = userService.findByNamePassword(username, password);
if (user == null) {
// Handle error
...
} else {
// Handle success
attributes.addFlashAttribute("user", user);
return "redirect:home";
}
}
...
}
@SessionAttributes("user")
のアノテーションが付けられているため、User
インスタンスのセッションからのロードを試み、パスワードの質問のリセットの確認に使用します。
@Controller
@SessionAttributes("user")
public class ResetPasswordController {
@RequestMapping(value = "/resetQuestion", method = RequestMethod.POST)
public String resetQuestionHandler(@RequestParam String answerReset, SessionStatus status, User user, Model model) {
if (!user.getAnswer().equals(answerReset)) {
// Handle error
...
} else {
// Handle success
...
}
}
}
user
インスタンスを、ログイン処理中それが格納されているセッションからロードする意図がありました。ただし、Spring は、リクエストを確認し、データのモデル user
インスタンスへのバインドを試みます。受信したリクエストに、User
クラスに紐付けることができるデータが含まれている場合、Spring により受信データがユーザー セッションの属性にマージされます。このシナリオは、answerReset
クエリ パラメーターの任意の回答および同値を送信し、セッションに格納された値を上書きすることで、悪用できます。このようにして、攻撃者は、不特定多数のユーザーに対して任意の新しいパスワードを設定できます。
...
taintedConnectionStr = request->get_form_field( 'dbconn_name' ).
TRY.
DATA(con) = cl_sql_connection=>get_connection( `R/3*` && taintedConnectionStr ).
...
con->close( ).
CATCH cx_sql_exception INTO FINAL(exc).
...
ENDTRY.
...
sethostid(argv[1]);
...
sethostid()
を適切に呼び出すためにはプロセスを権限付きで実行することが必要ですが、権限を所有しないユーザーがこのプログラムを呼び出すことが可能な場合もあります。このコード例では、入力により直接システム設定値を制御することをユーザーに許可しています。攻撃者がホスト ID に悪意のある値を代入すると、ネットワーク上で影響を受けたマシンの識別が不能になる、または意図しない動作を引き起こす原因となります。
...
ACCEPT OPT1.
ACCEPT OPT2
COMPUTE OPTS = OPT1 + OPT2.
CALL 'MQOPEN' USING HCONN, OBJECTDESC, OPTS, HOBJ, COMPOCODE REASON.
...
...
<cfset code = SetProfileString(IniPath,
Section, "timeout", Form.newTimeout)>
...
Form.newTimeout
の値は、タイムアウトの指定に使用されます。そのため、攻撃者は非常に大きい数値を指定することで、アプリケーションに対して Denial of Service (DoS) 攻撃を仕掛けることが可能となる場合があります。
...
catalog := request.Form.Get("catalog")
path := request.Form.Get("path")
os.Setenv(catalog, path)
...
HttpServletRequest
から文字列を読み取り、データベース Connection
のアクティブなカタログに設定します。
...
conn.setCatalog(request.getParamter("catalog"));
...
http.IncomingMessage
要求変数から文字列を読み取り、追加の V8 コマンド行フラグを設定するためにそれを使用します。
var v8 = require('v8');
...
var flags = url.parse(request.url, true)['query']['flags'];
...
v8.setFlagsFromString(flags);
...
<?php
...
$table_name=$_GET['catalog'];
$retrieved_array = pg_copy_to($db_connection, $table_name);
...
?>
...
catalog = request.GET['catalog']
path = request.GET['path']
os.putenv(catalog, path)
...
Connection
のアクティブなカタログとして設定します。
def connect(catalog: String) = Action { request =>
...
conn.setCatalog(catalog)
...
}
...
sqlite3(SQLITE_CONFIG_LOG, user_controllable);
...
Request
オブジェクトから文字列を読み取り、データベース Connection
のアクティブなカタログに設定します。
...
Dim conn As ADODB.Connection
Set conn = New ADODB.Connection
Dim rsTables As ADODB.Recordset
Dim Catalog As New ADOX.Catalog
Set Catalog.ActiveConnection = conn
Catalog.Create Request.Form("catalog")
...
String beans = getBeanDefinitionFromUser();
GenericApplicationContext ctx = new GenericApplicationContext();
XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx);
xmlReader.loadBeanDefinitions(new UrlResource(beans));
ctx.refresh();
ACTUATOR
ロールを持つユーザーのみにアクセスを許可することで保護されています。
management.security.enabled=false
endpoints.health.sensitive=false
@Component
public class CustomEndpoint implements Endpoint<List<String>> {
public String getId() {
return "customEndpoint";
}
public boolean isEnabled() {
return true;
}
public boolean isSensitive() {
return false;
}
public List<String> invoke() {
// Custom logic to build the output
...
}
}