Math.random 함수는 nodejs 같은 프레임워크에서 사용되며 주로 난수 발생을 위한 기능으로 사용됩니다.
그러나 대부분이 알고있듯이 Math.random함수는 PRNG를 통해 난수를 생성하는데 이는 암호학적으로 다음 값 예측이 가능합니다.
그래서 원래는 Math.random값을 전부 알 경우에 특정 도구를 통해서 다음값을 예측이 가능한데...
이번 LA CTF 문제를 복기하다가 Math.random값이 변형되거나 일부만 알때도 다음값이 예측 가능하다는 점을 새로 알게되서 글로 정리해보려 합니다.
Math.random 전체 값을 알 경우
Math.random의 전체 값은 대충
0.9311600617849973
이런 소수값으로 나옵니다.
해당 경우엔 아래 유명한 v8-randomness-predictor를 사용해서 예측이 가능합니다.
https://github.com/PwnFunction/v8-randomness-predictor
GitHub - PwnFunction/v8-randomness-predictor: Using z3 to predict `Math.random` in v8
Using z3 to predict `Math.random` in v8. Contribute to PwnFunction/v8-randomness-predictor development by creating an account on GitHub.
github.com
대충 Math.random값이 전 값을 또 사용하기 때문에 Math.random 여러 값을 넣어주면 z3 라이브러리를 사용해서 요리조리? 해서 다음값을 예측합니다. (암호학은 공부를 안해서 정확한 원리는 잘 모르겠네요;;)
Math.random 일부 값을 알 경우
이때는 참 곤란합니다....
웬만한 서비스에선 Math.random값 그대로를 사용 안하고 보통 값을 곱하거나 더하거나 floor 시켜서 사용하는게 대다수입니다.
예를 들어 살펴보자면 다음과 같습니다.
Math.floor(Math.random() * 1e9)
해당 코드는 2025 LA CTF에서 그대로 나온 코드입니다.
random값에다가 1,000,000,000을 곱하고 그 값을 floor시킵니다.
이렇게 되면은 전체 Math.random값을 알 수 없고 몇자리 짤린 값만 알기 때문에 위의 도구로는 불가능합니다.
그래서 해당 경우에 사용할 수 있는 도구가 없을까 하고 여러 자료들을 뒤져봤습니다.
https://github.com/Arc-blroth/ece117-unpredictables/
GitHub - Arc-blroth/ece117-unpredictables
Contribute to Arc-blroth/ece117-unpredictables development by creating an account on GitHub.
github.com
위는 LA CTF에서 출제자가 참고자료로 준 자료인데 Math.random값이 변형되었을때 어떻게 예측 가능한지 논문으로 적혀져 있습니다.
그러나 저같은 초보자는 논문 이해하기가 빡세기에 다른 자료를 찾아봤습니다.
https://www.youtube.com/watch?v=_Iv6fBrcbAM
위 유튜브에서 해당 경우에 대해 엄청 자세히 설명해주고 있습니다.
대충 XS128+를 Math.random은 거꾸로 놓게 하는데... 이걸 사용해서 뭐 math.random값이 특정 값에 곱해졌을때도 예측이 가능하다네요
exploit코드는 아래 링크에서 확인 가능합니다.
https://github.com/d0nutptr/v8_rand_buster/tree/master
GitHub - d0nutptr/v8_rand_buster
Contribute to d0nutptr/v8_rand_buster development by creating an account on GitHub.
github.com
사용법은 변형된 random값들을 전부 아무때나 작성해놓고 그걸 tac을 사용해서 거꾸로 뒤집습니다.
그 값을 xs128p.py에 넣으면 되는데 이때 multiple 옵션에다가 얼마가 곱해진 값인지 집어넣습니다.
그러면 seeds값들을 얻을 수 있고 해당 seeds 값들을 --gen옵션에 넣고 얻고 싶은 랜덤값 길이를 넣으면 작동합니다.
대충 예시를 들자면 아래 경우의 코드를 들고 왔습니다.
Math.floor(Math.random() * 1e9)
해당 경우의 값들을 예시로 뽑아보면 아래와 같은 숫자들이 나옵니다.
353307991
630804994
125622016
703564311
206790164
168956598
962037314
366029441
416604608
877461533
그리고 해당 숫자들을 넣고 돌리는 명령어입니다.
echo "353307991
630804994
125622016
703564311
206790164
168956598
962037314
366029441
416604608
877461533" | tac | python3 xs128p.py --multiple 1000000000 --lead 2
1e9값 즉 1000000000이 곱해졌기에 --multiple에 저 값을 넣어주었고 877461533이후의 랜덤값 두개를 얻고 싶어 --lead 2옵션을 넣어줬습니다.
이때, 주의해야할게 저 같은 경우는 뭐 code.txt이런데 넣은 다음 cat code.txt | tac 해줬는데
tac에서 마지막 부분의 줄바꿈이 안돼서 줄바꿈 추가해줘서 했습니다... (왜그런진 잘 모르겠네요)
// 이렇게 마지막 부분에 띄어쓰기 추가해야됩니다.
353307991
630804994
125622016
703564311
206790164
168956598
962037314
366029441
416604608
877461533
뭐 여튼 위의 코드를 돌리면 seed값이 나옵니다.
요 seed값을 gen 옵션에 넣어주고 한 20개 number를 뽑아보죠.
명령어는 아래와 같습니다.
python3 xs128p.py --multiple 1000000000 --gen 10288824643452828480,10498123127750857472,20 --lead 2
저희가 넣었던 마지막 숫자가 877461533이니 그다음 숫자는 872894071이 되겠네요!
이렇게 Math.random()의 일부자리가 날라가거나 변형된 경우에도 예측이 가능하다는 걸 알아냈습니다!
위의 경우엔 multiple에 관해서만 소개가 되었지만 덧셈 뻴셈 같은 경우에도 코드 수정을 살짝만 해주면 가능하다고 하네요!
'Web Hacking > Information' 카테고리의 다른 글
Docker wsl 연결 문제 해결 (0) | 2025.01.19 |
---|---|
LFI로 할 수 있는거 (0) | 2024.07.12 |
Command Injection (0) | 2024.05.01 |
SQL Injection 팁 정리 (0) | 2024.04.15 |
NoSQL Injection - MongoDB (0) | 2024.04.10 |