Just use CROSS JOIN.
create table Variations
(
    Id int not null,
    Value varchar(50) not null
)
create table Variation_Attributes
(
    Id int not null,
    VariationId int not null,
    Value varchar(50) not null
)
GO
insert into Variations
(Id, Value)
values
(1   , 'Color'),
(2   , 'Size'),
(3   , 'Length');
insert into Variation_Attributes
(Id  , VariationId , Value)
values
(1   , 1           , 'Black'),
(2   , 1           , 'Red'),
(3   , 2           , 'Large'),
(4   , 2           , 'Small'),
(5   , 2           , 'Medium'),
(6   , 3           , 'Tall');
GO
select *
 from Variations
 cross join Variation_Attributes
UPDATE
After OP edition we can better evaluate the question and the problem itself.
This is a bad design problem leading to a complex solution.
A better solution can be to redesign the tables.
A table for each kind of property can work better here Color, Size Length.
On the other hand, if you must give attributes to an object, let's say it's a shop that sells electric devices for the kitchen, so you will need a relation table for each product and its possible attributes what "solves" the problem.
"Solves" is quoted here why it's possible the real problem is not the problem OP is trying to solve. A very common issue in the IT industry.
UPDATE 2
When someone calls the "It's legacy" card there's not much what you can do.
Of course, the solution is trivial for a fixed number of Variation.
select v0.Value, v1.Value, v2.Value
 from Variation_Attributes v0
 join Variation_Attributes v1 on v1.Id != v0.Id
 join Variation_Attributes v2 on v1.Id != v0.Id and v2.Id != v1.Id
where v0.VariationId = 1
and   v1.VariationId = 2
and   v2.VariationId = 3
it give us all six possibilities.
But for a dynamic scenario OP must use PIVOT or build the query dynamically.
Example:
declare @index int = 0, @select varchar(max), @from varchar(max), @where varchar(max), @VariationId int;
declare MyLoop cursor fast_forward for (select Id from Variations);
open MyLoop;
fetch next from MyLoop into @VariationId
while @@FETCH_STATUS != -1
begin
    if (@index = 0)
    begin
        set @select = 'select v'+cast(@index as varchar)+'.Value as v'+cast(@index as varchar);
        set @from   = 'from Variation_Attributes v'+cast(@index as varchar);
        set @where  = 'where v'+cast(@index as varchar)+'.VariationId = '+cast(@VariationId as varchar);
    end
    else begin
        set @select = @select + ', v'+cast(@index as varchar)+'.Value as v'+cast(@index as varchar);
        set @from   = @from   + ' cross join Variation_Attributes v'+cast(@index as varchar);
        set @where  = @where  + ' and v'+cast(@index as varchar)+'.VariationId = '+cast(@VariationId as varchar);
    end
    set @index = @index + 1;
    fetch next from MyLoop into @VariationId;
end
--print @select;
--print @from;
--print @where;
close MyLoop;
deallocate MyLoop;
exec (@select+' '+@from+' '+@where);
For the example data it yelds
v0      v1      v2
------- ------- -------
Black   Large   Tall
Black   Small   Tall
Black   Medium  Tall
Red     Large   Tall
Red     Small   Tall
Red     Medium  Tall