fun25에서 1차 도메인과 서브 도메인도 지정해준다. 그래야지 default port를 사용할 수 있다. fun25는 좀 특이하게 내부아이피 이용으로 프락시서버 등록이랑 포트포워딩 자동화해놔서 ssh 접속할 때도 포트를 22가 아니라 외부포트를 지정해서 접속한다 ㅋㅋ;
fun25 세팅
리버스 프록시로 ctfd는 8000포트를 이용해서 ctf.sung.pw로 서브도메인을 사용하게끔하면 된다.
그리고 CustomLog를 설정할 수 있다. 이게 뭐냐면 ctf.sung.pw에서 발생하는 로그가 /var/log/apache2/access.log 경로에 작성되지 않고 ctf_log/access.log에 로그가 남게된다. 이건 구분할 수 있어서 유용하게 사용할 수 있고 좋은 점 같다.
그래서 $ tail -f /var/log/apache2/access.log로 확인해보면 sung.pw에서 발생하는 로그만 남게된다!
무슨 CTF였더라.. 대충 재밌었던 Level들만 정리 레벨은 아마 1부터15까지 있었나? 명령어도 한정되어서 재밋엇다..
/*
https://del.dog/asm-hell.txt
Command Description
MOVE A1 A2 A1 is the amount of steps (can be negative), A2 is the direction (0 for vertical, 1 for horizontal)
WAIT A1 A1 is the amount of cycles to wait
UNLOCK A1 A1 is the key
READ M1 M1 stores the result of tile read instruction
ADD M1 A1 A2 M1 stores the result of A1 + A2
SUB M1 A1 A2 M1 stores the result of A1 - A2
MUL M1 A1 A2 M1 stores the result of A1 * A2
JMP A1 A1 line in code to be jumped to
JMPZ A1 A2 jump to line A1 if A2 is zero
JMPN A1 A2 jump to line A1 if A2 is negative
CMP M1 A1 A2 M1 stores the result of A1 > A2 (B); A1 > A2: 1, A1 = A2: 0, A1 < A2: -1
*/
Level 8
여기서 주어진 어셈블리 명령에는 div 연산이 존재하지 않아 직접 만들어줘야한다.
왼쪽에 주어진 숫자를 0번 레지스터에 오른쪽에 주어진 숫자를 1번 레지스터에 넣어준다.
0번 레지스터를 계속 1번 레지스터 값으로 빼준 다음에 0번 레지스터에 저장한다. 이렇게 한번 뺄 때마다 5번 레지스터의 값을 1씩 더해준다. 전체적으로 0번 레지스터를 계속 빼주면서 0번 레지스터의 값이 0이 될 때까지 반복해주면 된다.
그리고 키값은 UNLOCK [5]로 카운팅해준 것으로 풀어주면 된다.
Level 12
제일 재밌게 푼 문제이다. 소수판별 문제이다. 우선 c언어로 대충 구상하고 풀었다. 아래 c코드를 옮긴 것이 아래와 같다.
for(int i=2; i<n; i++)
if(n%i==0) MOVE -1 1;
MOVE -1 1;
/*
0 : n
1 : i = 2
2 : 사용 안함
3 : n-1
4 : cmp [6] [0]
5 : cmp [1] [3]
6 : n -> 나머지 연산이 구현되어 있지 않아 [1]로 계속 sub해줌
*/
문제 풀이 과정은 간단하다. 블루스택이나 녹스를 사용할때 루팅으로 후킹해서 문제를 풀려면 루팅체크하는 부분을 우회해서 후킹을 해야한다. 출제자 풀이를 보면 finish 후킹했는데 나는 라이브러리 후킹해서 풀었다. finish 후킹할 생각을 못해서 조금 시간 뺏겼다. 그 이후로는 쉬움!
PS. 문제 풀면서 느낀게 jadx보단 jeb가 훨씬 좋다 ㅇㅇ.. (유료가 최고임)
MainActivity
package com.hspace.pengsu;
import android.os.Bundle;
import android.util.Log;
import android.view.View.OnClickListener;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import b.b.c.h;
import b.b.c.u;
import c.b.a.a.a;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Random;
public class MainActivity extends h implements View.OnClickListener {
public int o;
public Random p;
public TextView q;
public ProgressBar r;
public ImageButton[] s;
public RootCheck t;
public static final int[] u;
public static int v;
public static {
MainActivity.u = new int[]{0x7F0800B7, 0x7F0800B8, 0x7F0800B9, 0x7F0800BA, 0x7F0800BB, 0x7F0800BC, 0x7F0800BD, 0x7F0800BE}; // id:ib1
MainActivity.v = 0;
}
public MainActivity() {
this.o = 0;
this.p = new Random();
}
@Override // android.view.View$OnClickListener
public void onClick(View arg5) {
int v5 = this.p.nextInt(10);
this.o = v5;
int v1 = MainActivity.v + v5;
MainActivity.v = v1;
if(v1 == 100) {
try {
a.a();
}
catch(Exception v5_1) {
v5_1.printStackTrace();
}
}
if(this.o != 3 && this.o != 5 && this.o != 7 && this.o != 9 && this.o != 10) {
TextView v5_2 = this.q;
StringBuilder v0 = c.a.a.a.a.f("Pengsu\'s Wallet : ");
v0.append(MainActivity.v);
v0.append("$");
v5_2.setText(v0.toString());
this.r.setProgress(MainActivity.v);
return;
}
this.r.setProgress(MainActivity.v);
MainActivity.v = 0;
TextView v5_3 = this.q;
StringBuilder v0_1 = c.a.a.a.a.f("Pengsu\'s Wallet : ");
v0_1.append(MainActivity.v);
v0_1.append("$");
v5_3.setText(v0_1.toString());
}
@Override // b.b.c.h
public void onCreate(Bundle arg6) {
super.onCreate(arg6);
this.s = new ImageButton[8];
this.setContentView(0x7F0B001C); // layout:activity_main
this.q = (TextView)this.findViewById(0x7F0801A0); // id:wallet
this.findViewById(0x7F0801A1); // id:wallet2
this.r = (ProgressBar)this.findViewById(0x7F080128); // id:progress
((u)this.o()).e.setTitle("Make Pengsu earn 100$");
this.q.setText("Pengsu\'s Wallet : 0");
int v0;
for(v0 = 0; v0 < 8; ++v0) {
this.s[v0] = (ImageButton)this.findViewById(MainActivity.u[v0]);
this.s[v0].setOnClickListener(this);
}
RootCheck v6 = new RootCheck();
this.t = v6;
if(!v6.rootCheck()) {
StringBuffer v2 = new StringBuffer();
try {
Process v6_2 = Runtime.getRuntime().exec("ps adbd");
v6_2.waitFor();
BufferedReader v3 = new BufferedReader(new InputStreamReader(v6_2.getInputStream()));
while(true) {
String v6_3 = v3.readLine();
if(v6_3 == null) {
break;
}
v2.append(v6_3 + "\n");
}
}
catch(Exception v6_1) {
v6_1.printStackTrace();
}
if(v2.toString().indexOf("bad") != -1) {
Toast.makeText(this, "No Rooted or ADB Process Detected", 1).show();
Log.d("MainActivity", "Detection Not Rooted or ADB!");
return;
}
}
Toast.makeText(this, "Detection Rooted or ADB Process!", 1).show();
Log.d("MainActivity", String.valueOf(this.t.rootCheck()));
Log.d("MainActivity", "Detection Rooted or ADB Process!");
this.finish();
}
}
a
package c.b.a.a;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
import android.os.Build.VERSION;
import android.util.Base64;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.ViewParent;
import b.b.h.x0;
import b.h.j.n;
import c.b.a.a.l.d.b;
import c.b.a.a.l.d.c;
import c.b.a.a.l.d.e;
import c.b.a.a.l.d;
import c.b.a.a.v.g;
import c.b.a.a.v.i;
import com.hspace.pengsu.MainActivity;
import com.hspace.pengsu.RootCheck;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.security.MessageDigest;
import java.util.List;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public final class a {
public static RootCheck a;
public static void a() {
a.a = new RootCheck();
MainActivity v0 = new MainActivity();
if(a.a.rootCheck()) {
label_43:
Log.d("CipherAlgorithm", String.valueOf(a.a.rootCheck()));
Log.d("CipherAlgorithm", "Please follow the game rules");
v0.finish();
}
else {
StringBuffer v3 = new StringBuffer();
try {
Process v1_1 = Runtime.getRuntime().exec("ps adbd");
v1_1.waitFor();
BufferedReader v4 = new BufferedReader(new InputStreamReader(v1_1.getInputStream()));
while(true) {
String v1_2 = v4.readLine();
if(v1_2 == null) {
break;
}
v3.append(v1_2 + "\n");
}
}
catch(Exception v1) {
v1.printStackTrace();
}
if(v3.toString().indexOf("bad") == -1) {
goto label_43;
}
Log.d("CipherAlgorithm", "Detection Not Rooted or ADB!");
}
String v1_3 = RootCheck.a();
"hello android world!".getBytes("UTF-8");
byte[] v0_1 = "hello android world!".getBytes();
MessageDigest v5 = MessageDigest.getInstance("MD5");
v5.update("SOJUBEERSOJUBEERSOJUBEERSOJUBEERSOJUBEERSOJUBEERSOJUBEERSOJUBEERSOJUBEERSOJUBEERSOJUBEER".getBytes());
byte[] v5_1 = v5.digest();
MessageDigest v6 = MessageDigest.getInstance("SHA-256");
v6.update(v1_3.getBytes());
byte[] v6_1 = v6.digest();
IvParameterSpec v7 = new IvParameterSpec(v5_1);
SecretKeySpec v5_2 = new SecretKeySpec(v6_1, "AES");
Cipher v6_2 = Cipher.getInstance("AES/CBC/PKCS5Padding");
v6_2.init(1, v5_2, v7);
byte[] v0_2 = v6_2.doFinal(v0_1);
if(MainActivity.v == 100) {
Log.d("CipherAlgorithm", " Decrypt Cipher : " + new String(a.d(v1_3, v1_3, Base64.decode("zem8Qf+nUSVM8gsOgKiEeZ+OPRR9EKu76gEjg2eYf4MdXK9wAXrCeTQ9r1CpWcMu".getBytes("UTF-8"), 0)), "UTF-8"));
}
else {
android.os.Process.killProcess(android.os.Process.myPid());
}
a.d("SOJUBEERSOJUBEERSOJUBEERSOJUBEERSOJUBEERSOJUBEERSOJUBEERSOJUBEERSOJUBEERSOJUBEERSOJUBEER", v1_3, v0_2);
}
public static Animator b(d arg6, float arg7, float arg8, float arg9) {
ObjectAnimator v0 = ObjectAnimator.ofObject(arg6, c.a, b.b, new e[]{new e(arg7, arg8, arg9)});
if(Build.VERSION.SDK_INT >= 21) {
e v1 = arg6.getRevealInfo();
if(v1 != null) {
Animator v6 = ViewAnimationUtils.createCircularReveal(((View)arg6), ((int)arg7), ((int)arg8), v1.c, arg9);
AnimatorSet v7 = new AnimatorSet();
v7.playTogether(new Animator[]{v0, v6});
return v7;
}
throw new IllegalStateException("Caller must set a non-null RevealInfo before calling this.");
}
return v0;
}
public static c.b.a.a.v.d c(int arg1) {
return arg1 != 0 && arg1 == 1 ? new c.b.a.a.v.e() : new i();
}
public static byte[] d(String arg2, String arg3, byte[] arg4) {
MessageDigest v0 = MessageDigest.getInstance("MD5");
v0.update(arg2.getBytes());
byte[] v2 = v0.digest();
MessageDigest v0_1 = MessageDigest.getInstance("SHA-256");
v0_1.update(arg3.getBytes());
byte[] v3 = v0_1.digest();
IvParameterSpec v0_2 = new IvParameterSpec(v2);
SecretKeySpec v2_1 = new SecretKeySpec(v3, "AES");
Cipher v3_1 = Cipher.getInstance("AES/CBC/PKCS5Padding");
v3_1.init(2, v2_1, v0_2);
return v3_1.doFinal(arg4);
}
public static float e(float arg0, float arg1, float arg2, float arg3) {
return (float)Math.hypot(((double)(arg2 - arg0)), ((double)(arg3 - arg1)));
}
public static int f(Context arg0, int arg1, int arg2) {
TypedValue v0 = a.r(arg0, arg1);
return v0 == null ? arg2 : v0.data;
}
public static int g(View arg1, int arg2) {
return a.t(arg1.getContext(), arg2, arg1.getClass().getCanonicalName());
}
public static ColorStateList h(Context arg1, TypedArray arg2, int arg3) {
if(arg2.hasValue(arg3)) {
int v0 = arg2.getResourceId(arg3, 0);
if(v0 != 0) {
ColorStateList v1 = b.b.d.a.a.a(arg1, v0);
return v1 == null ? arg2.getColorStateList(arg3) : v1;
}
}
return arg2.getColorStateList(arg3);
}
public static ColorStateList i(Context arg2, x0 arg3, int arg4) {
if(arg3.b.hasValue(arg4)) {
int v0 = arg3.b.getResourceId(arg4, 0);
if(v0 != 0) {
ColorStateList v2 = b.b.d.a.a.a(arg2, v0);
return v2 == null ? arg3.c(arg4) : v2;
}
}
return arg3.c(arg4);
}
public static Drawable j(Context arg1, TypedArray arg2, int arg3) {
if(arg2.hasValue(arg3)) {
int v0 = arg2.getResourceId(arg3, 0);
if(v0 != 0) {
Drawable v1 = b.b.d.a.a.b(arg1, v0);
return v1 == null ? arg2.getDrawable(arg3) : v1;
}
}
return arg2.getDrawable(arg3);
}
public static boolean k(Context arg1) {
return arg1.getResources().getConfiguration().fontScale >= 1.3f;
}
public static boolean l(Context arg1) {
return arg1.getResources().getConfiguration().fontScale >= 2f;
}
public static boolean m(View arg1) {
return n.m(arg1) == 1;
}
public static int n(int arg1, int arg2, float arg3) {
return b.h.d.a.a(b.h.d.a.c(arg2, Math.round(((float)Color.alpha(arg2)) * arg3)), arg1);
}
public static float o(float arg1, float arg2, float arg3) {
return arg3 * arg2 + (1f - arg3) * arg1;
}
public static PorterDuff.Mode p(int arg1, PorterDuff.Mode arg2) {
if(arg1 != 3) {
if(arg1 != 5) {
if(arg1 != 9) {
switch(arg1) {
case 14: {
break;
}
case 15: {
return PorterDuff.Mode.SCREEN;
}
case 16: {
return PorterDuff.Mode.ADD;
}
default: {
return arg2;
}
}
return PorterDuff.Mode.MULTIPLY;
}
return PorterDuff.Mode.SRC_ATOP;
}
return PorterDuff.Mode.SRC_IN;
}
return PorterDuff.Mode.SRC_OVER;
}
public static void q(AnimatorSet arg10, List arg11) {
int v0 = arg11.size();
long v2 = 0L;
int v4;
for(v4 = 0; v4 < v0; ++v4) {
Animator v5 = (Animator)arg11.get(v4);
long v6 = v5.getStartDelay();
v2 = Math.max(v2, v5.getDuration() + v6);
}
ValueAnimator v0_1 = ValueAnimator.ofInt(new int[]{0, 0});
v0_1.setDuration(v2);
arg11.add(0, v0_1);
arg10.playTogether(arg11);
}
public static TypedValue r(Context arg2, int arg3) {
TypedValue v0 = new TypedValue();
return arg2.getTheme().resolveAttribute(arg3, v0, true) ? v0 : null;
}
public static boolean s(Context arg1, int arg2, boolean arg3) {
TypedValue v1 = a.r(arg1, arg2);
return v1 != null && v1.type == 18 ? v1.data != 0 : arg3;
}
public static int t(Context arg3, int arg4, String arg5) {
TypedValue v0 = a.r(arg3, arg4);
if(v0 != null) {
return v0.data;
}
throw new IllegalArgumentException(String.format("%1$s requires a value for the %2$s attribute to be set in your app theme. You can either set the attribute in your theme or update your theme to inherit from Theme.MaterialComponents (or a descendant).", arg5, arg3.getResources().getResourceName(arg4)));
}
public static void u(View arg2, float arg3) {
Drawable v2 = arg2.getBackground();
if((v2 instanceof g)) {
g v2_1 = (g)v2;
c.b.a.a.v.g.b v0 = v2_1.b;
if(v0.o != arg3) {
v0.o = arg3;
v2_1.w();
}
}
}
public static void v(View arg2, g arg3) {
if(arg3.b.b != null && (arg3.b.b.a)) {
ViewParent v2 = arg2.getParent();
float v0 = 0f;
while((v2 instanceof View)) {
v0 += n.k(((View)v2));
v2 = v2.getParent();
}
c.b.a.a.v.g.b v2_1 = arg3.b;
if(v2_1.n != v0) {
v2_1.n = v0;
arg3.w();
}
}
}
public static PorterDuffColorFilter w(Drawable arg1, ColorStateList arg2, PorterDuff.Mode arg3) {
return arg2 == null || arg3 == null ? null : new PorterDuffColorFilter(arg2.getColorForState(arg1.getState(), 0), arg3);
}
}
RootCheck
package com.hspace.pengsu;
public class RootCheck {
public static {
System.loadLibrary("native-lib");
}
public static native String a() {
}
public native boolean rootCheck() {
}
}