🤖
ångstromCTF 2021 - Float On
問題概要
I cast my int into a double the other day, well nothing crashed, sometimes life's okay.
We'll all float on, anyway: float_on.c.
Float on over to
/problems/2021/float_on
on the shell server, or connect withnc shell.actf.co 21399
.
float_on.c
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#define DO_STAGE(num, cond) do {\
printf("Stage " #num ": ");\
scanf("%lu", &converter.uint);\
x = converter.dbl;\
if(cond) {\
puts("Stage " #num " passed!");\
} else {\
puts("Stage " #num " failed!");\
return num;\
}\
} while(0);
void print_flag() {
FILE* flagfile = fopen("flag.txt", "r");
if (flagfile == NULL) {
puts("Couldn't find a flag file.");
return;
}
char flag[128];
fgets(flag, 128, flagfile);
flag[strcspn(flag, "\n")] = '\x00';
puts(flag);
}
union cast {
uint64_t uint;
double dbl;
};
int main(void) {
union cast converter;
double x;
DO_STAGE(1, x == -x);
DO_STAGE(2, x != x);
DO_STAGE(3, x + 1 == x && x * 2 == x);
DO_STAGE(4, x + 1 == x && x * 2 != x);
DO_STAGE(5, (1 + x) - 1 != 1 + (x - 1));
print_flag();
return 0;
}
解説
x
をuint64
で与えて、それをdouble
型で解釈したときにDO_STAGE
で与えられている式を満たすようにする問題です。
Cにおけるdouble
の処理はIEEE 754で規定されていて、これを見ながらそれぞれの条件に合う数字を探していくことになります。
-
x == -x
: これを満たす代表例として を考えるのは自然でしょう。0 -
x != x
: これはNaN
が満たします。 -
x + 1 == x && x * 2 == x
: これはinfinity
が満たします。 -
x + 1 == x && x * 2 != x
: これは非常に大きい数(具体的に を足しても数が変わらないよう、仮数部でカバー出来ない程に大きければOKです。有効数字は大体15桁とかなので、多分1 進数における10 桁以上の数字なら大丈夫でしょう。僕は20 桁でやりました。100 -
(1 + x) - 1 != 1 + (x - 1)
: これは少し悩んだのですが、NaN
はこれを満たします。NaN
に関するルールで以下があるためです。-
NaN
と数字の演算はNaN
になる。この結果、右辺と左辺はそれぞれNaN
になる -
NaN == NaN
はfalse
-
この結果からそれぞれ与えるべきdouble
としてのx
は求まったので、これをuint64
型に変換して与える必要があります。まぁ問題で与えられている手法の逆で変換すれば出来るので...
convert.c
#include <stdio.h>
#include <stdint.h>
#include <math.h>
#include <assert.h>
union cast {
uint64_t uint;
double dbl;
};
int main() {
union cast conv;
// stage 1: x == -x, x = 0
conv.dbl = 0.0;
printf("%lu\n", conv.uint);
// stage 2: x != x, x = NaN
conv.dbl = NAN;
printf("%lu\n", conv.uint);
// stage 3: x + 1 == x, x * 2 == x, x is infinity
conv.dbl = INFINITY;
printf("%lu\n", conv.uint);
// stage 4: x + 1 == x, x * 2 != x, x is very huge number
conv.dbl = 1e100;
printf("%lu\n", conv.uint);
// stage 5: (1 + x) - 1 != 1 + (x - 1), x is NaN
conv.dbl = NAN;
double x = conv.dbl;
if ((1 + x) - 1 != 1 + (x - 1)) {
printf("%lu\n", conv.uint);
} else {
printf("stage 5 failed\n");
}
return 0;
}
solve.py
from pwn import *
context.log_level = "debug"
r = remote("shell.actf.co", 21399)
r.recvuntil(b"Stage 1: ")
r.sendline("0")
r.recvuntil(b"Stage 2: ")
r.sendline("9221120237041090560")
r.recvuntil(b"Stage 3: ")
r.sendline("9218868437227405312")
r.recvuntil(b"Stage 4: ")
r.sendline("6103021453049119613")
r.recvuntil(b"Stage 5: ")
r.sendline("9221120237041090560")
r.interactive()
Discussion